Source for javax.swing.plaf.basic.BasicTreeUI

   1: /* BasicTreeUI.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: 
  39: package javax.swing.plaf.basic;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Dimension;
  44: import java.awt.Font;
  45: import java.awt.FontMetrics;
  46: import java.awt.Graphics;
  47: import java.awt.Insets;
  48: import java.awt.Point;
  49: import java.awt.Rectangle;
  50: import java.awt.event.ActionEvent;
  51: import java.awt.event.ActionListener;
  52: import java.awt.event.ComponentAdapter;
  53: import java.awt.event.ComponentEvent;
  54: import java.awt.event.ComponentListener;
  55: import java.awt.event.FocusEvent;
  56: import java.awt.event.FocusListener;
  57: import java.awt.event.KeyAdapter;
  58: import java.awt.event.KeyEvent;
  59: import java.awt.event.KeyListener;
  60: import java.awt.event.MouseAdapter;
  61: import java.awt.event.MouseEvent;
  62: import java.awt.event.MouseListener;
  63: import java.awt.event.MouseMotionListener;
  64: import java.beans.PropertyChangeEvent;
  65: import java.beans.PropertyChangeListener;
  66: import java.util.Enumeration;
  67: import java.util.Hashtable;
  68: 
  69: import javax.swing.AbstractAction;
  70: import javax.swing.Action;
  71: import javax.swing.ActionMap;
  72: import javax.swing.CellRendererPane;
  73: import javax.swing.Icon;
  74: import javax.swing.InputMap;
  75: import javax.swing.JComponent;
  76: import javax.swing.JScrollBar;
  77: import javax.swing.JScrollPane;
  78: import javax.swing.JTextField;
  79: import javax.swing.JTree;
  80: import javax.swing.KeyStroke;
  81: import javax.swing.LookAndFeel;
  82: import javax.swing.SwingUtilities;
  83: import javax.swing.Timer;
  84: import javax.swing.UIManager;
  85: import javax.swing.event.CellEditorListener;
  86: import javax.swing.event.ChangeEvent;
  87: import javax.swing.event.MouseInputListener;
  88: import javax.swing.event.TreeExpansionEvent;
  89: import javax.swing.event.TreeExpansionListener;
  90: import javax.swing.event.TreeModelEvent;
  91: import javax.swing.event.TreeModelListener;
  92: import javax.swing.event.TreeSelectionEvent;
  93: import javax.swing.event.TreeSelectionListener;
  94: import javax.swing.plaf.ActionMapUIResource;
  95: import javax.swing.plaf.ComponentUI;
  96: import javax.swing.plaf.InputMapUIResource;
  97: import javax.swing.plaf.TreeUI;
  98: import javax.swing.text.Caret;
  99: import javax.swing.tree.AbstractLayoutCache;
 100: import javax.swing.tree.DefaultTreeCellEditor;
 101: import javax.swing.tree.DefaultTreeCellRenderer;
 102: import javax.swing.tree.FixedHeightLayoutCache;
 103: import javax.swing.tree.TreeCellEditor;
 104: import javax.swing.tree.TreeCellRenderer;
 105: import javax.swing.tree.TreeModel;
 106: import javax.swing.tree.TreeNode;
 107: import javax.swing.tree.TreePath;
 108: import javax.swing.tree.TreeSelectionModel;
 109: 
 110: /**
 111:  * A delegate providing the user interface for <code>JTree</code> according to
 112:  * the Basic look and feel.
 113:  * 
 114:  * @see javax.swing.JTree
 115:  * @author Lillian Angel (langel@redhat.com)
 116:  * @author Sascha Brawer (brawer@dandelis.ch)
 117:  */
 118: public class BasicTreeUI extends TreeUI
 119: {
 120:   /** Collapse Icon for the tree. */
 121:   protected transient Icon collapsedIcon;
 122: 
 123:   /** Expanded Icon for the tree. */
 124:   protected transient Icon expandedIcon;
 125: 
 126:   /** Distance between left margin and where vertical dashes will be drawn. */
 127:   protected int leftChildIndent;
 128: 
 129:   /**
 130:    * Distance between leftChildIndent and where cell contents will be drawn.
 131:    */
 132:   protected int rightChildIndent;
 133: 
 134:   /**
 135:    * Total fistance that will be indented. The sum of leftChildIndent and
 136:    * rightChildIndent .
 137:    */
 138:   protected int totalChildIndent;
 139: 
 140:   /** Index of the row that was last selected. */
 141:   protected int lastSelectedRow;
 142: 
 143:   /** Component that we're going to be drawing onto. */
 144:   protected JTree tree;
 145: 
 146:   /** Renderer that is being used to do the actual cell drawing. */
 147:   protected transient TreeCellRenderer currentCellRenderer;
 148: 
 149:   /**
 150:    * Set to true if the renderer that is currently in the tree was created by
 151:    * this instance.
 152:    */
 153:   protected boolean createdRenderer;
 154: 
 155:   /** Editor for the tree. */
 156:   protected transient TreeCellEditor cellEditor;
 157: 
 158:   /**
 159:    * Set to true if editor that is currently in the tree was created by this
 160:    * instance.
 161:    */
 162:   protected boolean createdCellEditor;
 163: 
 164:   /**
 165:    * Set to false when editing and shouldSelectCall() returns true meaning the
 166:    * node should be selected before editing, used in completeEditing.
 167:    */
 168:   protected boolean stopEditingInCompleteEditing;
 169: 
 170:   /** Used to paint the TreeCellRenderer. */
 171:   protected CellRendererPane rendererPane;
 172: 
 173:   /** Size needed to completely display all the nodes. */
 174:   protected Dimension preferredSize;
 175: 
 176:   /** Minimum size needed to completely display all the nodes. */
 177:   protected Dimension preferredMinSize;
 178: 
 179:   /** Is the preferredSize valid? */
 180:   protected boolean validCachedPreferredSize;
 181: 
 182:   /** Object responsible for handling sizing and expanded issues. */
 183:   protected AbstractLayoutCache treeState;
 184: 
 185:   /** Used for minimizing the drawing of vertical lines. */
 186:   protected Hashtable drawingCache;
 187: 
 188:   /**
 189:    * True if doing optimizations for a largeModel. Subclasses that don't support
 190:    * this may wish to override createLayoutCache to not return a
 191:    * FixedHeightLayoutCache instance.
 192:    */
 193:   protected boolean largeModel;
 194: 
 195:   /** Responsible for telling the TreeState the size needed for a node. */
 196:   protected AbstractLayoutCache.NodeDimensions nodeDimensions;
 197: 
 198:   /** Used to determine what to display. */
 199:   protected TreeModel treeModel;
 200: 
 201:   /** Model maintaining the selection. */
 202:   protected TreeSelectionModel treeSelectionModel;
 203: 
 204:   /**
 205:    * How much the depth should be offset to properly calculate x locations. This
 206:    * is based on whether or not the root is visible, and if the root handles are
 207:    * visible.
 208:    */
 209:   protected int depthOffset;
 210: 
 211:   /**
 212:    * When editing, this will be the Component that is doing the actual editing.
 213:    */
 214:   protected Component editingComponent;
 215: 
 216:   /** Path that is being edited. */
 217:   protected TreePath editingPath;
 218: 
 219:   /**
 220:    * Row that is being edited. Should only be referenced if editingComponent is
 221:    * null.
 222:    */
 223:   protected int editingRow;
 224: 
 225:   /** Set to true if the editor has a different size than the renderer. */
 226:   protected boolean editorHasDifferentSize;
 227: 
 228:   /** The action listener for the editor's Timer. */
 229:   Timer editorTimer = new EditorUpdateTimer();
 230: 
 231:   /** The new value of the node after editing. */
 232:   Object newVal;
 233: 
 234:   /** The action bound to KeyStrokes. */
 235:   TreeAction action;
 236: 
 237:   /** Boolean to keep track of editing. */
 238:   boolean isEditing;
 239: 
 240:   /** The current path of the visible nodes in the tree. */
 241:   TreePath currentVisiblePath;
 242: 
 243:   /** The gap between the icon and text. */
 244:   int gap = 4;
 245: 
 246:   /** The max height of the nodes in the tree. */
 247:   int maxHeight = 0;
 248: 
 249:   /** Listeners */
 250:   private PropertyChangeListener propertyChangeListener;
 251: 
 252:   private FocusListener focusListener;
 253: 
 254:   private TreeSelectionListener treeSelectionListener;
 255: 
 256:   private MouseListener mouseListener;
 257: 
 258:   private KeyListener keyListener;
 259: 
 260:   private PropertyChangeListener selectionModelPropertyChangeListener;
 261: 
 262:   private ComponentListener componentListener;
 263: 
 264:   CellEditorListener cellEditorListener;
 265: 
 266:   private TreeExpansionListener treeExpansionListener;
 267: 
 268:   private TreeModelListener treeModelListener;
 269: 
 270:   /**
 271:    * Creates a new BasicTreeUI object.
 272:    */
 273:   public BasicTreeUI()
 274:   {
 275:     validCachedPreferredSize = false;
 276:     drawingCache = new Hashtable();
 277:     nodeDimensions = createNodeDimensions();
 278:     configureLayoutCache();
 279: 
 280:     propertyChangeListener = createPropertyChangeListener();
 281:     focusListener = createFocusListener();
 282:     treeSelectionListener = createTreeSelectionListener();
 283:     mouseListener = createMouseListener();
 284:     keyListener = createKeyListener();
 285:     selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener();
 286:     componentListener = createComponentListener();
 287:     cellEditorListener = createCellEditorListener();
 288:     treeExpansionListener = createTreeExpansionListener();
 289:     treeModelListener = createTreeModelListener();
 290: 
 291:     editingRow = -1;
 292:     lastSelectedRow = -1;
 293:   }
 294: 
 295:   /**
 296:    * Returns an instance of the UI delegate for the specified component.
 297:    * 
 298:    * @param c
 299:    *          the <code>JComponent</code> for which we need a UI delegate for.
 300:    * @return the <code>ComponentUI</code> for c.
 301:    */
 302:   public static ComponentUI createUI(JComponent c)
 303:   {
 304:     return new BasicTreeUI();
 305:   }
 306: 
 307:   /**
 308:    * Returns the Hash color.
 309:    * 
 310:    * @return the <code>Color</code> of the Hash.
 311:    */
 312:   protected Color getHashColor()
 313:   {
 314:     return UIManager.getColor("Tree.hash");
 315:   }
 316: 
 317:   /**
 318:    * Sets the Hash color.
 319:    * 
 320:    * @param color
 321:    *          the <code>Color</code> to set the Hash to.
 322:    */
 323:   protected void setHashColor(Color color)
 324:   {
 325:     // FIXME: Putting something in the UIDefaults map is certainly wrong.
 326:     UIManager.put("Tree.hash", color);
 327:   }
 328: 
 329:   /**
 330:    * Sets the left child's indent value.
 331:    * 
 332:    * @param newAmount
 333:    *          is the new indent value for the left child.
 334:    */
 335:   public void setLeftChildIndent(int newAmount)
 336:   {
 337:     leftChildIndent = newAmount;
 338:   }
 339: 
 340:   /**
 341:    * Returns the indent value for the left child.
 342:    * 
 343:    * @return the indent value for the left child.
 344:    */
 345:   public int getLeftChildIndent()
 346:   {
 347:     return leftChildIndent;
 348:   }
 349: 
 350:   /**
 351:    * Sets the right child's indent value.
 352:    * 
 353:    * @param newAmount
 354:    *          is the new indent value for the right child.
 355:    */
 356:   public void setRightChildIndent(int newAmount)
 357:   {
 358:     rightChildIndent = newAmount;
 359:   }
 360: 
 361:   /**
 362:    * Returns the indent value for the right child.
 363:    * 
 364:    * @return the indent value for the right child.
 365:    */
 366:   public int getRightChildIndent()
 367:   {
 368:     return rightChildIndent;
 369:   }
 370: 
 371:   /**
 372:    * Sets the expanded icon.
 373:    * 
 374:    * @param newG
 375:    *          is the new expanded icon.
 376:    */
 377:   public void setExpandedIcon(Icon newG)
 378:   {
 379:     expandedIcon = newG;
 380:   }
 381: 
 382:   /**
 383:    * Returns the current expanded icon.
 384:    * 
 385:    * @return the current expanded icon.
 386:    */
 387:   public Icon getExpandedIcon()
 388:   {
 389:     return expandedIcon;
 390:   }
 391: 
 392:   /**
 393:    * Sets the collapsed icon.
 394:    * 
 395:    * @param newG
 396:    *          is the new collapsed icon.
 397:    */
 398:   public void setCollapsedIcon(Icon newG)
 399:   {
 400:     collapsedIcon = newG;
 401:   }
 402: 
 403:   /**
 404:    * Returns the current collapsed icon.
 405:    * 
 406:    * @return the current collapsed icon.
 407:    */
 408:   public Icon getCollapsedIcon()
 409:   {
 410:     return collapsedIcon;
 411:   }
 412: 
 413:   /**
 414:    * Updates the componentListener, if necessary.
 415:    * 
 416:    * @param largeModel
 417:    *          sets this.largeModel to it.
 418:    */
 419:   protected void setLargeModel(boolean largeModel)
 420:   {
 421:     if (largeModel != this.largeModel)
 422:       {
 423:         tree.removeComponentListener(componentListener);
 424:         this.largeModel = largeModel;
 425:         tree.addComponentListener(componentListener);
 426:       }
 427:   }
 428: 
 429:   /**
 430:    * Returns true if largeModel is set
 431:    * 
 432:    * @return true if largeModel is set, otherwise false.
 433:    */
 434:   protected boolean isLargeModel()
 435:   {
 436:     return largeModel;
 437:   }
 438: 
 439:   /**
 440:    * Sets the row height.
 441:    * 
 442:    * @param rowHeight
 443:    *          is the height to set this.rowHeight to.
 444:    */
 445:   protected void setRowHeight(int rowHeight)
 446:   {
 447:     if (rowHeight == 0)
 448:       rowHeight = Math.max(getMaxHeight(tree), 20);
 449:     treeState.setRowHeight(rowHeight);
 450:   }
 451: 
 452:   /**
 453:    * Returns the current row height.
 454:    * 
 455:    * @return current row height.
 456:    */
 457:   protected int getRowHeight()
 458:   {
 459:     return treeState.getRowHeight();
 460:   }
 461: 
 462:   /**
 463:    * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
 464:    * <code>updateRenderer</code>.
 465:    * 
 466:    * @param tcr
 467:    *          is the new TreeCellRenderer.
 468:    */
 469:   protected void setCellRenderer(TreeCellRenderer tcr)
 470:   {
 471:     currentCellRenderer = tcr;
 472:     updateRenderer();
 473:   }
 474: 
 475:   /**
 476:    * Return currentCellRenderer, which will either be the trees renderer, or
 477:    * defaultCellRenderer, which ever was not null.
 478:    * 
 479:    * @return the current Cell Renderer
 480:    */
 481:   protected TreeCellRenderer getCellRenderer()
 482:   {
 483:     if (currentCellRenderer != null)
 484:       return currentCellRenderer;
 485: 
 486:     return createDefaultCellRenderer();
 487:   }
 488: 
 489:   /**
 490:    * Sets the tree's model.
 491:    * 
 492:    * @param model
 493:    *          to set the treeModel to.
 494:    */
 495:   protected void setModel(TreeModel model)
 496:   {
 497:     tree.setModel(model);
 498:     treeModel = tree.getModel();
 499:   }
 500: 
 501:   /**
 502:    * Returns the tree's model
 503:    * 
 504:    * @return treeModel
 505:    */
 506:   protected TreeModel getModel()
 507:   {
 508:     return treeModel;
 509:   }
 510: 
 511:   /**
 512:    * Sets the root to being visible.
 513:    * 
 514:    * @param newValue
 515:    *          sets the visibility of the root
 516:    */
 517:   protected void setRootVisible(boolean newValue)
 518:   {
 519:     tree.setRootVisible(newValue);
 520:   }
 521: 
 522:   /**
 523:    * Returns true if the root is visible.
 524:    * 
 525:    * @return true if the root is visible.
 526:    */
 527:   protected boolean isRootVisible()
 528:   {
 529:     return tree.isRootVisible();
 530:   }
 531: 
 532:   /**
 533:    * Determines whether the node handles are to be displayed.
 534:    * 
 535:    * @param newValue
 536:    *          sets whether or not node handles should be displayed.
 537:    */
 538:   protected void setShowsRootHandles(boolean newValue)
 539:   {
 540:     tree.setShowsRootHandles(newValue);
 541:   }
 542: 
 543:   /**
 544:    * Returns true if the node handles are to be displayed.
 545:    * 
 546:    * @return true if the node handles are to be displayed.
 547:    */
 548:   protected boolean getShowsRootHandles()
 549:   {
 550:     return tree.getShowsRootHandles();
 551:   }
 552: 
 553:   /**
 554:    * Sets the cell editor.
 555:    * 
 556:    * @param editor
 557:    *          to set the cellEditor to.
 558:    */
 559:   protected void setCellEditor(TreeCellEditor editor)
 560:   {
 561:     cellEditor = editor;
 562:     createdCellEditor = true;
 563:   }
 564: 
 565:   /**
 566:    * Returns the <code>TreeCellEditor</code> for this tree.
 567:    * 
 568:    * @return the cellEditor for this tree.
 569:    */
 570:   protected TreeCellEditor getCellEditor()
 571:   {
 572:     return cellEditor;
 573:   }
 574: 
 575:   /**
 576:    * Configures the receiver to allow, or not allow, editing.
 577:    * 
 578:    * @param newValue
 579:    *          sets the receiver to allow editing if true.
 580:    */
 581:   protected void setEditable(boolean newValue)
 582:   {
 583:     tree.setEditable(newValue);
 584:   }
 585: 
 586:   /**
 587:    * Returns true if the receiver allows editing.
 588:    * 
 589:    * @return true if the receiver allows editing.
 590:    */
 591:   protected boolean isEditable()
 592:   {
 593:     return tree.isEditable();
 594:   }
 595: 
 596:   /**
 597:    * Resets the selection model. The appropriate listeners are installed on the
 598:    * model.
 599:    * 
 600:    * @param newLSM
 601:    *          resets the selection model.
 602:    */
 603:   protected void setSelectionModel(TreeSelectionModel newLSM)
 604:   {
 605:     if (newLSM != null)
 606:       {
 607:         treeSelectionModel = newLSM;
 608:         tree.setSelectionModel(treeSelectionModel);
 609:       }
 610:   }
 611: 
 612:   /**
 613:    * Returns the current selection model.
 614:    * 
 615:    * @return the current selection model.
 616:    */
 617:   protected TreeSelectionModel getSelectionModel()
 618:   {
 619:     return treeSelectionModel;
 620:   }
 621: 
 622:   /**
 623:    * Returns the Rectangle enclosing the label portion that the last item in
 624:    * path will be drawn to. Will return null if any component in path is
 625:    * currently valid.
 626:    * 
 627:    * @param tree
 628:    *          is the current tree the path will be drawn to.
 629:    * @param path
 630:    *          is the current path the tree to draw to.
 631:    * @return the Rectangle enclosing the label portion that the last item in the
 632:    *         path will be drawn to.
 633:    */
 634:   public Rectangle getPathBounds(JTree tree, TreePath path)
 635:   {
 636:     int row = -1;
 637:     Object cell = null;
 638:     if (path != null)
 639:       {
 640:         row = getRowForPath(tree, path);
 641:         cell = path.getLastPathComponent();
 642:       }
 643:     return nodeDimensions.getNodeDimensions(cell, row, getLevel(cell),
 644:                                             tree.isExpanded(path),
 645:                                             new Rectangle());
 646:   }
 647: 
 648:   /**
 649:    * Returns the max height of all the nodes in the tree.
 650:    * 
 651:    * @param tree -
 652:    *          the current tree
 653:    * @return the max height.
 654:    */
 655:   private int getMaxHeight(JTree tree)
 656:   {
 657:     if (maxHeight != 0)
 658:       return maxHeight;
 659: 
 660:     Icon e = UIManager.getIcon("Tree.openIcon");
 661:     Icon c = UIManager.getIcon("Tree.closedIcon");
 662:     Icon l = UIManager.getIcon("Tree.leafIcon");
 663:     int rc = getRowCount(tree);
 664:     int iconHeight = 0;
 665: 
 666:     for (int row = 0; row < rc; row++)
 667:       {
 668:         if (isLeaf(row))
 669:           iconHeight = l.getIconHeight();
 670:         else if (tree.isExpanded(row))
 671:           iconHeight = e.getIconHeight();
 672:         else
 673:           iconHeight = c.getIconHeight();
 674: 
 675:         maxHeight = Math.max(maxHeight, iconHeight + gap);
 676:       }
 677: 
 678:     return maxHeight;
 679:   }
 680: 
 681:   /**
 682:    * Returns the path for passed in row. If row is not visible null is returned.
 683:    * 
 684:    * @param tree
 685:    *          is the current tree to return path for.
 686:    * @param row
 687:    *          is the row number of the row to return.
 688:    * @return the path for passed in row. If row is not visible null is returned.
 689:    */
 690:   public TreePath getPathForRow(JTree tree, int row)
 691:   {
 692:     if (treeModel != null && currentVisiblePath != null)
 693:       {
 694:         Object[] nodes = currentVisiblePath.getPath();
 695:         if (row < nodes.length)
 696:           return new TreePath(getPathToRoot(nodes[row], 0));
 697:       }
 698:     return null;
 699:   }
 700: 
 701:   /**
 702:    * Returns the row that the last item identified in path is visible at. Will
 703:    * return -1 if any of the elments in the path are not currently visible.
 704:    * 
 705:    * @param tree
 706:    *          is the current tree to return the row for.
 707:    * @param path
 708:    *          is the path used to find the row.
 709:    * @return the row that the last item identified in path is visible at. Will
 710:    *         return -1 if any of the elments in the path are not currently
 711:    *         visible.
 712:    */
 713:   public int getRowForPath(JTree tree, TreePath path)
 714:   {
 715:     int row = 0;
 716:     Object dest = path.getLastPathComponent();
 717:     int rowCount = getRowCount(tree);
 718:     if (currentVisiblePath != null)
 719:       {
 720:         Object[] nodes = currentVisiblePath.getPath();
 721:         while (row < rowCount)
 722:           {
 723:             if (dest.equals(nodes[row]))
 724:               return row;
 725:             row++;
 726:           }
 727:       }
 728:     return -1;
 729:   }
 730: 
 731:   /**
 732:    * Returns the number of rows that are being displayed.
 733:    * 
 734:    * @param tree
 735:    *          is the current tree to return the number of rows for.
 736:    * @return the number of rows being displayed.
 737:    */
 738:   public int getRowCount(JTree tree)
 739:   {
 740:     if (currentVisiblePath != null)
 741:       return currentVisiblePath.getPathCount();
 742:     return 0;
 743:   }
 744: 
 745:   /**
 746:    * Returns the path to the node that is closest to x,y. If there is nothing
 747:    * currently visible this will return null, otherwise it'll always return a
 748:    * valid path. If you need to test if the returned object is exactly at x,y
 749:    * you should get the bounds for the returned path and test x,y against that.
 750:    * 
 751:    * @param tree
 752:    *          the tree to search for the closest path
 753:    * @param x
 754:    *          is the x coordinate of the location to search
 755:    * @param y
 756:    *          is the y coordinate of the location to search
 757:    * @return the tree path closes to x,y.
 758:    */
 759:   public TreePath getClosestPathForLocation(JTree tree, int x, int y)
 760:   {
 761:     int row = Math.round(y / getMaxHeight(tree));
 762:     TreePath path = getPathForRow(tree, row);
 763: 
 764:     // no row is visible at this node
 765:     while (row > 0 && path == null)
 766:       {
 767:         --row;
 768:         path = getPathForRow(tree, row);
 769:       }
 770: 
 771:     return path;
 772:   }
 773: 
 774:   /**
 775:    * Returns true if the tree is being edited. The item that is being edited can
 776:    * be returned by getEditingPath().
 777:    * 
 778:    * @param tree
 779:    *          is the tree to check for editing.
 780:    * @return true if the tree is being edited.
 781:    */
 782:   public boolean isEditing(JTree tree)
 783:   {
 784:     return isEditing;
 785:   }
 786: 
 787:   /**
 788:    * Stops the current editing session. This has no effect if the tree is not
 789:    * being edited. Returns true if the editor allows the editing session to
 790:    * stop.
 791:    * 
 792:    * @param tree
 793:    *          is the tree to stop the editing on
 794:    * @return true if the editor allows the editing session to stop.
 795:    */
 796:   public boolean stopEditing(JTree tree)
 797:   {
 798:     if (isEditing(tree))
 799:       completeEditing(true, false, false);
 800:     return !isEditing(tree);
 801:   }
 802: 
 803:   /**
 804:    * Cancels the current editing session.
 805:    * 
 806:    * @param tree
 807:    *          is the tree to cancel the editing session on.
 808:    */
 809:   public void cancelEditing(JTree tree)
 810:   {
 811:     if (isEditing(tree))
 812:       completeEditing(false, true, false);
 813:   }
 814: 
 815:   /**
 816:    * Selects the last item in path and tries to edit it. Editing will fail if
 817:    * the CellEditor won't allow it for the selected item.
 818:    * 
 819:    * @param tree
 820:    *          is the tree to edit on.
 821:    * @param path
 822:    *          is the path in tree to edit on.
 823:    */
 824:   public void startEditingAtPath(JTree tree, TreePath path)
 825:   {
 826:     startEditing(path, null);
 827:   }
 828: 
 829:   /**
 830:    * Returns the path to the element that is being editted.
 831:    * 
 832:    * @param tree
 833:    *          is the tree to get the editing path from.
 834:    * @return the path that is being edited.
 835:    */
 836:   public TreePath getEditingPath(JTree tree)
 837:   {
 838:     return editingPath;
 839:   }
 840: 
 841:   /**
 842:    * Invoked after the tree instance variable has been set, but before any
 843:    * default/listeners have been installed.
 844:    */
 845:   protected void prepareForUIInstall()
 846:   {
 847:     // TODO: Implement this properly.
 848:   }
 849: 
 850:   /**
 851:    * Invoked from installUI after all the defaults/listeners have been
 852:    * installed.
 853:    */
 854:   protected void completeUIInstall()
 855:   {
 856:     // TODO: Implement this properly.
 857:   }
 858: 
 859:   /**
 860:    * Invoked from uninstallUI after all the defaults/listeners have been
 861:    * uninstalled.
 862:    */
 863:   protected void completeUIUninstall()
 864:   {
 865:     // TODO: Implement this properly.
 866:   }
 867: 
 868:   /**
 869:    * Installs the subcomponents of the tree, which is the renderer pane.
 870:    */
 871:   protected void installComponents()
 872:   {
 873:     currentCellRenderer = createDefaultCellRenderer();
 874:     rendererPane = createCellRendererPane();
 875:     createdRenderer = true;
 876:     setCellRenderer(currentCellRenderer);
 877:   }
 878: 
 879:   /**
 880:    * Creates an instance of NodeDimensions that is able to determine the size of
 881:    * a given node in the tree.
 882:    * 
 883:    * @return the NodeDimensions of a given node in the tree
 884:    */
 885:   protected AbstractLayoutCache.NodeDimensions createNodeDimensions()
 886:   {
 887:     return new NodeDimensionsHandler();
 888:   }
 889: 
 890:   /**
 891:    * Creates a listener that is reponsible for the updates the UI based on how
 892:    * the tree changes.
 893:    * 
 894:    * @return the PropertyChangeListener that is reposnsible for the updates
 895:    */
 896:   protected PropertyChangeListener createPropertyChangeListener()
 897:   {
 898:     return new PropertyChangeHandler();
 899:   }
 900: 
 901:   /**
 902:    * Creates the listener responsible for updating the selection based on mouse
 903:    * events.
 904:    * 
 905:    * @return the MouseListener responsible for updating.
 906:    */
 907:   protected MouseListener createMouseListener()
 908:   {
 909:     return new MouseHandler();
 910:   }
 911: 
 912:   /**
 913:    * Creates the listener that is responsible for updating the display when
 914:    * focus is lost/grained.
 915:    * 
 916:    * @return the FocusListener responsible for updating.
 917:    */
 918:   protected FocusListener createFocusListener()
 919:   {
 920:     return new FocusHandler();
 921:   }
 922: 
 923:   /**
 924:    * Creates the listener reponsible for getting key events from the tree.
 925:    * 
 926:    * @return the KeyListener responsible for getting key events.
 927:    */
 928:   protected KeyListener createKeyListener()
 929:   {
 930:     return new KeyHandler();
 931:   }
 932: 
 933:   /**
 934:    * Creates the listener responsible for getting property change events from
 935:    * the selection model.
 936:    * 
 937:    * @returns the PropertyChangeListener reponsible for getting property change
 938:    *          events from the selection model.
 939:    */
 940:   protected PropertyChangeListener createSelectionModelPropertyChangeListener()
 941:   {
 942:     return new SelectionModelPropertyChangeHandler();
 943:   }
 944: 
 945:   /**
 946:    * Creates the listener that updates the display based on selection change
 947:    * methods.
 948:    * 
 949:    * @return the TreeSelectionListener responsible for updating.
 950:    */
 951:   protected TreeSelectionListener createTreeSelectionListener()
 952:   {
 953:     return new TreeSelectionHandler();
 954:   }
 955: 
 956:   /**
 957:    * Creates a listener to handle events from the current editor
 958:    * 
 959:    * @return the CellEditorListener that handles events from the current editor
 960:    */
 961:   protected CellEditorListener createCellEditorListener()
 962:   {
 963:     return new CellEditorHandler();
 964:   }
 965: 
 966:   /**
 967:    * Creates and returns a new ComponentHandler. This is used for the large
 968:    * model to mark the validCachedPreferredSize as invalid when the component
 969:    * moves.
 970:    * 
 971:    * @return a new ComponentHandler.
 972:    */
 973:   protected ComponentListener createComponentListener()
 974:   {
 975:     return new ComponentHandler();
 976:   }
 977: 
 978:   /**
 979:    * Creates and returns the object responsible for updating the treestate when
 980:    * a nodes expanded state changes.
 981:    * 
 982:    * @return the TreeExpansionListener responsible for updating the treestate
 983:    */
 984:   protected TreeExpansionListener createTreeExpansionListener()
 985:   {
 986:     return new TreeExpansionHandler();
 987:   }
 988: 
 989:   /**
 990:    * Creates the object responsible for managing what is expanded, as well as
 991:    * the size of nodes.
 992:    * 
 993:    * @return the object responsible for managing what is expanded.
 994:    */
 995:   protected AbstractLayoutCache createLayoutCache()
 996:   {
 997:     return new FixedHeightLayoutCache();
 998:   }
 999: 
1000:   /**
1001:    * Returns the renderer pane that renderer components are placed in.
1002:    * 
1003:    * @return the rendererpane that render components are placed in.
1004:    */
1005:   protected CellRendererPane createCellRendererPane()
1006:   {
1007:     return new CellRendererPane();
1008:   }
1009: 
1010:   /**
1011:    * Creates a default cell editor.
1012:    * 
1013:    * @return the default cell editor.
1014:    */
1015:   protected TreeCellEditor createDefaultCellEditor()
1016:   {
1017:     if (currentCellRenderer != null)
1018:       return new DefaultTreeCellEditor(
1019:                                        tree,
1020:                                        (DefaultTreeCellRenderer) currentCellRenderer,
1021:                                        cellEditor);
1022:     return new DefaultTreeCellEditor(
1023:                                      tree,
1024:                                      (DefaultTreeCellRenderer) createDefaultCellRenderer(),
1025:                                      cellEditor);
1026:   }
1027: 
1028:   /**
1029:    * Returns the default cell renderer that is used to do the stamping of each
1030:    * node.
1031:    * 
1032:    * @return the default cell renderer that is used to do the stamping of each
1033:    *         node.
1034:    */
1035:   protected TreeCellRenderer createDefaultCellRenderer()
1036:   {
1037:     return new DefaultTreeCellRenderer();
1038:   }
1039: 
1040:   /**
1041:    * Returns a listener that can update the tree when the model changes.
1042:    * 
1043:    * @return a listener that can update the tree when the model changes.
1044:    */
1045:   protected TreeModelListener createTreeModelListener()
1046:   {
1047:     return new TreeModelHandler();
1048:   }
1049: 
1050:   /**
1051:    * Uninstall all registered listeners
1052:    */
1053:   protected void uninstallListeners()
1054:   {
1055:     tree.removePropertyChangeListener(propertyChangeListener);
1056:     tree.removeFocusListener(focusListener);
1057:     tree.removeTreeSelectionListener(treeSelectionListener);
1058:     tree.removeMouseListener(mouseListener);
1059:     tree.removeKeyListener(keyListener);
1060:     tree.removePropertyChangeListener(selectionModelPropertyChangeListener);
1061:     tree.removeComponentListener(componentListener);
1062:     tree.removeTreeExpansionListener(treeExpansionListener);
1063: 
1064:     TreeCellEditor tce = tree.getCellEditor();
1065:     if (tce != null)
1066:       tce.removeCellEditorListener(cellEditorListener);
1067:     if (treeModel != null)
1068:       treeModel.removeTreeModelListener(treeModelListener);
1069:   }
1070: 
1071:   /**
1072:    * Uninstall all keyboard actions.
1073:    */
1074:   protected void uninstallKeyboardActions()
1075:   {
1076:     action = null;
1077:     tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
1078:                                                                               null);
1079:     tree.getActionMap().setParent(null);
1080:   }
1081: 
1082:   /**
1083:    * Uninstall the rendererPane.
1084:    */
1085:   protected void uninstallComponents()
1086:   {
1087:     currentCellRenderer = null;
1088:     rendererPane = null;
1089:     createdRenderer = false;
1090:     setCellRenderer(currentCellRenderer);
1091:   }
1092: 
1093:   /**
1094:    * The vertical element of legs between nodes starts at the bottom of the
1095:    * parent node by default. This method makes the leg start below that.
1096:    * 
1097:    * @return the vertical leg buffer
1098:    */
1099:   protected int getVerticalLegBuffer()
1100:   {
1101:     return getRowHeight() / 2;
1102:   }
1103: 
1104:   /**
1105:    * The horizontal element of legs between nodes starts at the right of the
1106:    * left-hand side of the child node by default. This method makes the leg end
1107:    * before that.
1108:    * 
1109:    * @return the horizontal leg buffer
1110:    */
1111:   protected int getHorizontalLegBuffer()
1112:   {
1113:     return rightChildIndent / 2;
1114:   }
1115: 
1116:   /**
1117:    * Make all the nodes that are expanded in JTree expanded in LayoutCache. This
1118:    * invokes updateExpandedDescendants with the root path.
1119:    */
1120:   protected void updateLayoutCacheExpandedNodes()
1121:   {
1122:     if (treeModel != null)
1123:       updateExpandedDescendants(new TreePath(treeModel.getRoot()));
1124:   }
1125: 
1126:   /**
1127:    * Updates the expanded state of all the descendants of the <code>path</code>
1128:    * by getting the expanded descendants from the tree and forwarding to the
1129:    * tree state.
1130:    * 
1131:    * @param path
1132:    *          the path used to update the expanded states
1133:    */
1134:   protected void updateExpandedDescendants(TreePath path)
1135:   {
1136:     Enumeration expanded = tree.getExpandedDescendants(path);
1137:     while (expanded.hasMoreElements())
1138:       treeState.setExpandedState(((TreePath) expanded.nextElement()), true);
1139:   }
1140: 
1141:   /**
1142:    * Returns a path to the last child of <code>parent</code>
1143:    * 
1144:    * @param parent
1145:    *          is the topmost path to specified
1146:    * @return a path to the last child of parent
1147:    */
1148:   protected TreePath getLastChildPath(TreePath parent)
1149:   {
1150:     return ((TreePath) parent.getLastPathComponent());
1151:   }
1152: 
1153:   /**
1154:    * Updates how much each depth should be offset by.
1155:    */
1156:   protected void updateDepthOffset()
1157:   {
1158:     depthOffset += getVerticalLegBuffer();
1159:   }
1160: 
1161:   /**
1162:    * Updates the cellEditor based on editability of the JTree that we're
1163:    * contained in. If the tree is editable but doesn't have a cellEditor, a
1164:    * basic one will be used.
1165:    */
1166:   protected void updateCellEditor()
1167:   {
1168:     if (tree.isEditable() && cellEditor == null)
1169:       setCellEditor(createDefaultCellEditor());
1170:     createdCellEditor = true;
1171:   }
1172: 
1173:   /**
1174:    * Messaged from the tree we're in when the renderer has changed.
1175:    */
1176:   protected void updateRenderer()
1177:   {
1178:     if (tree != null)
1179:       {
1180:         if (tree.getCellRenderer() == null)
1181:           {
1182:             if (currentCellRenderer == null)
1183:               currentCellRenderer = createDefaultCellRenderer();
1184:             tree.setCellRenderer(currentCellRenderer);
1185:           }
1186:       }
1187:   }
1188: 
1189:   /**
1190:    * Resets the treeState instance based on the tree we're providing the look
1191:    * and feel for.
1192:    */
1193:   protected void configureLayoutCache()
1194:   {
1195:     treeState = createLayoutCache();
1196:   }
1197: 
1198:   /**
1199:    * Marks the cached size as being invalid, and messages the tree with
1200:    * <code>treeDidChange</code>.
1201:    */
1202:   protected void updateSize()
1203:   {
1204:     preferredSize = null;
1205:     updateCachedPreferredSize();
1206:     tree.treeDidChange();
1207:   }
1208: 
1209:   /**
1210:    * Updates the <code>preferredSize</code> instance variable, which is
1211:    * returned from <code>getPreferredSize()</code>.
1212:    */
1213:   protected void updateCachedPreferredSize()
1214:   {
1215:     int maxWidth = 0;
1216:     boolean isLeaf = false;
1217:     if (currentVisiblePath != null)
1218:       {
1219:         Object[] path = currentVisiblePath.getPath();
1220:         for (int i = 0; i < path.length; i++)
1221:           {
1222:             TreePath curr = new TreePath(getPathToRoot(path[i], 0));
1223:             Rectangle bounds = getPathBounds(tree, curr);
1224:             if (treeModel != null)
1225:               isLeaf = treeModel.isLeaf(path[i]);
1226:             if (!isLeaf && hasControlIcons())
1227:               bounds.width += getCurrentControlIcon(curr).getIconWidth();
1228:             maxWidth = Math.max(maxWidth, bounds.x + bounds.width);
1229:           }
1230: 
1231:         maxHeight = 0;
1232:         maxHeight = getMaxHeight(tree);
1233:         preferredSize = new Dimension(maxWidth, (maxHeight * path.length));
1234:       }
1235:     else
1236:       preferredSize = new Dimension(0, 0);
1237:     validCachedPreferredSize = true;
1238:   }
1239: 
1240:   /**
1241:    * Messaged from the VisibleTreeNode after it has been expanded.
1242:    * 
1243:    * @param path
1244:    *          is the path that has been expanded.
1245:    */
1246:   protected void pathWasExpanded(TreePath path)
1247:   {
1248:     validCachedPreferredSize = false;
1249:     tree.repaint();
1250:   }
1251: 
1252:   /**
1253:    * Messaged from the VisibleTreeNode after it has collapsed
1254:    */
1255:   protected void pathWasCollapsed(TreePath path)
1256:   {
1257:     validCachedPreferredSize = false;
1258:     tree.repaint();
1259:   }
1260: 
1261:   /**
1262:    * Install all defaults for the tree.
1263:    */
1264:   protected void installDefaults()
1265:   {
1266:     LookAndFeel.installColorsAndFont(tree, "Tree.background",
1267:                                      "Tree.foreground", "Tree.font");
1268:     tree.setOpaque(true);
1269: 
1270:     rightChildIndent = UIManager.getInt("Tree.rightChildIndent");
1271:     leftChildIndent = UIManager.getInt("Tree.leftChildIndent");
1272:     setRowHeight(UIManager.getInt("Tree.rowHeight"));
1273:     tree.setRowHeight(getRowHeight());
1274:     tree.requestFocusInWindow(false);
1275:     tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand"));
1276:     setExpandedIcon(UIManager.getIcon("Tree.expandedIcon"));
1277:     setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon"));
1278:   }
1279: 
1280:   /**
1281:    * Install all keyboard actions for this
1282:    */
1283:   protected void installKeyboardActions()
1284:   {
1285:     InputMap focusInputMap = (InputMap) UIManager.get("Tree.focusInputMap");
1286:     InputMapUIResource parentInputMap = new InputMapUIResource();
1287:     ActionMap parentActionMap = new ActionMapUIResource();
1288:     action = new TreeAction();
1289:     Object keys[] = focusInputMap.allKeys();
1290: 
1291:     for (int i = 0; i < keys.length; i++)
1292:       {
1293:         parentInputMap.put(
1294:                            KeyStroke.getKeyStroke(
1295:                                                   ((KeyStroke) keys[i]).getKeyCode(),
1296:                                                   convertModifiers(((KeyStroke) keys[i]).getModifiers())),
1297:                            (String) focusInputMap.get((KeyStroke) keys[i]));
1298: 
1299:         parentInputMap.put(
1300:                            KeyStroke.getKeyStroke(
1301:                                                   ((KeyStroke) keys[i]).getKeyCode(),
1302:                                                   ((KeyStroke) keys[i]).getModifiers()),
1303:                            (String) focusInputMap.get((KeyStroke) keys[i]));
1304: 
1305:         parentActionMap.put(
1306:                             (String) focusInputMap.get((KeyStroke) keys[i]),
1307:                             new ActionListenerProxy(
1308:                                                     action,
1309:                                                     (String) focusInputMap.get((KeyStroke) keys[i])));
1310: 
1311:       }
1312: 
1313:     parentInputMap.setParent(tree.getInputMap(
1314:                                               JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent());
1315:     parentActionMap.setParent(tree.getActionMap().getParent());
1316:     tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
1317:                                                                               parentInputMap);
1318:     tree.getActionMap().setParent(parentActionMap);
1319:   }
1320: 
1321:   /**
1322:    * Converts the modifiers.
1323:    * 
1324:    * @param mod -
1325:    *          modifier to convert
1326:    * @returns the new modifier
1327:    */
1328:   private int convertModifiers(int mod)
1329:   {
1330:     if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0)
1331:       {
1332:         mod |= KeyEvent.SHIFT_MASK;
1333:         mod &= ~KeyEvent.SHIFT_DOWN_MASK;
1334:       }
1335:     if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0)
1336:       {
1337:         mod |= KeyEvent.CTRL_MASK;
1338:         mod &= ~KeyEvent.CTRL_DOWN_MASK;
1339:       }
1340:     if ((mod & KeyEvent.META_DOWN_MASK) != 0)
1341:       {
1342:         mod |= KeyEvent.META_MASK;
1343:         mod &= ~KeyEvent.META_DOWN_MASK;
1344:       }
1345:     if ((mod & KeyEvent.ALT_DOWN_MASK) != 0)
1346:       {
1347:         mod |= KeyEvent.ALT_MASK;
1348:         mod &= ~KeyEvent.ALT_DOWN_MASK;
1349:       }
1350:     if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0)
1351:       {
1352:         mod |= KeyEvent.ALT_GRAPH_MASK;
1353:         mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK;
1354:       }
1355:     return mod;
1356:   }
1357: 
1358:   /**
1359:    * Install all listeners for this
1360:    */
1361:   protected void installListeners()
1362:   {
1363:     tree.addPropertyChangeListener(propertyChangeListener);
1364:     tree.addFocusListener(focusListener);
1365:     tree.addTreeSelectionListener(treeSelectionListener);
1366:     tree.addMouseListener(mouseListener);
1367:     tree.addKeyListener(keyListener);
1368:     tree.addPropertyChangeListener(selectionModelPropertyChangeListener);
1369:     tree.addComponentListener(componentListener);
1370:     tree.addTreeExpansionListener(treeExpansionListener);
1371:     if (treeModel != null)
1372:       treeModel.addTreeModelListener(treeModelListener);
1373:   }
1374: 
1375:   /**
1376:    * Install the UI for the component
1377:    * 
1378:    * @param c
1379:    *          the component to install UI for
1380:    */
1381:   public void installUI(JComponent c)
1382:   {
1383:     tree = (JTree) c;
1384:     prepareForUIInstall();
1385:     super.installUI(c);
1386:     installDefaults();
1387: 
1388:     installComponents();
1389:     installKeyboardActions();
1390:     installListeners();
1391: 
1392:     setCellEditor(createDefaultCellEditor());
1393:     createdCellEditor = true;
1394:     isEditing = false;
1395: 
1396:     setModel(tree.getModel());
1397:     treeSelectionModel = tree.getSelectionModel();
1398: 
1399:     completeUIInstall();
1400:   }
1401: 
1402:   /**
1403:    * Uninstall the defaults for the tree
1404:    */
1405:   protected void uninstallDefaults()
1406:   {
1407:     tree.setFont(null);
1408:     tree.setForeground(null);
1409:     tree.setBackground(null);
1410:   }
1411: 
1412:   /**
1413:    * Uninstall the UI for the component
1414:    * 
1415:    * @param c
1416:    *          the component to uninstall UI for
1417:    */
1418:   public void uninstallUI(JComponent c)
1419:   {
1420:     prepareForUIUninstall();
1421:     uninstallDefaults();
1422:     uninstallKeyboardActions();
1423:     uninstallListeners();
1424:     tree = null;
1425:     uninstallComponents();
1426:     completeUIUninstall();
1427:   }
1428: 
1429:   /**
1430:    * Paints the specified component appropriate for the look and feel. This
1431:    * method is invoked from the ComponentUI.update method when the specified
1432:    * component is being painted. Subclasses should override this method and use
1433:    * the specified Graphics object to render the content of the component.
1434:    * 
1435:    * @param g
1436:    *          the Graphics context in which to paint
1437:    * @param c
1438:    *          the component being painted; this argument is often ignored, but
1439:    *          might be used if the UI object is stateless and shared by multiple
1440:    *          components
1441:    */
1442:   public void paint(Graphics g, JComponent c)
1443:   {
1444:     JTree tree = (JTree) c;
1445:     updateCurrentVisiblePath();
1446: 
1447:     Rectangle clip = g.getClipBounds();
1448:     Insets insets = tree.getInsets();
1449: 
1450:     if (clip != null && treeModel != null && currentVisiblePath != null)
1451:       {
1452:         int startIndex = tree.getClosestRowForLocation(clip.x, clip.y);
1453:         int endIndex = tree.getClosestRowForLocation(clip.x + clip.width,
1454:                                                      clip.y + clip.height);
1455: 
1456:         paintVerticalPartOfLeg(g, clip, insets, currentVisiblePath);
1457:         for (int i = startIndex; i <= endIndex; i++)
1458:           {
1459:             Object curr = currentVisiblePath.getPathComponent(i);
1460:             boolean isLeaf = treeModel.isLeaf(curr);
1461:             TreePath path = new TreePath(getPathToRoot(curr, 0));
1462: 
1463:             boolean isExpanded = tree.isExpanded(path);
1464:             Rectangle bounds = getPathBounds(tree, path);
1465:             paintHorizontalPartOfLeg(g, clip, insets, bounds, path, i,
1466:                                      isExpanded, false, isLeaf);
1467:             paintRow(g, clip, insets, bounds, path, i, isExpanded, false,
1468:                      isLeaf);
1469:           }
1470:       }
1471:   }
1472: 
1473:   /**
1474:    * Ensures that the rows identified by beginRow through endRow are visible.
1475:    * 
1476:    * @param beginRow
1477:    *          is the first row
1478:    * @param endRow
1479:    *          is the last row
1480:    */
1481:   protected void ensureRowsAreVisible(int beginRow, int endRow)
1482:   {
1483:     if (beginRow < endRow)
1484:       {
1485:         int temp = endRow;
1486:         endRow = beginRow;
1487:         beginRow = temp;
1488:       }
1489: 
1490:     for (int i = beginRow; i < endRow; i++)
1491:       {
1492:         TreePath path = getPathForRow(tree, i);
1493:         if (!tree.isVisible(path))
1494:           tree.makeVisible(path);
1495:       }
1496:   }
1497: 
1498:   /**
1499:    * Sets the preferred minimum size.
1500:    * 
1501:    * @param newSize
1502:    *          is the new preferred minimum size.
1503:    */
1504:   public void setPreferredMinSize(Dimension newSize)
1505:   {
1506:     preferredMinSize = newSize;
1507:   }
1508: 
1509:   /**
1510:    * Gets the preferred minimum size.
1511:    * 
1512:    * @returns the preferred minimum size.
1513:    */
1514:   public Dimension getPreferredMinSize()
1515:   {
1516:     return preferredMinSize;
1517:   }
1518: 
1519:   /**
1520:    * Returns the preferred size to properly display the tree, this is a cover
1521:    * method for getPreferredSize(c, false).
1522:    * 
1523:    * @param c
1524:    *          the component whose preferred size is being queried; this argument
1525:    *          is often ignored but might be used if the UI object is stateless
1526:    *          and shared by multiple components
1527:    * @return the preferred size
1528:    */
1529:   public Dimension getPreferredSize(JComponent c)
1530:   {
1531:     return getPreferredSize(c, false);
1532:   }
1533: 
1534:   /**
1535:    * Returns the preferred size to represent the tree in c. If checkConsistancy
1536:    * is true, checkConsistancy is messaged first.
1537:    * 
1538:    * @param c
1539:    *          the component whose preferred size is being queried.
1540:    * @param checkConsistancy
1541:    *          if true must check consistancy
1542:    * @return the preferred size
1543:    */
1544:   public Dimension getPreferredSize(JComponent c, boolean checkConsistancy)
1545:   {
1546:     // FIXME: checkConsistancy not implemented, c not used
1547:     if (!validCachedPreferredSize)
1548:       updateCachedPreferredSize();
1549:     return preferredSize;
1550:   }
1551: 
1552:   /**
1553:    * Returns the minimum size for this component. Which will be the min
1554:    * preferred size or (0,0).
1555:    * 
1556:    * @param c
1557:    *          the component whose min size is being queried.
1558:    * @returns the preferred size or null
1559:    */
1560:   public Dimension getMinimumSize(JComponent c)
1561:   {
1562:     Dimension min = getPreferredMinSize();
1563:     if (min == null)
1564:       return new Dimension();
1565:     return min;
1566:   }
1567: 
1568:   /**
1569:    * Returns the maximum size for the component, which will be the preferred
1570:    * size if the instance is currently in JTree or (0,0).
1571:    * 
1572:    * @param c
1573:    *          the component whose preferred size is being queried
1574:    * @return the max size or null
1575:    */
1576:   public Dimension getMaximumSize(JComponent c)
1577:   {
1578:     if (c instanceof JTree)
1579:       return ((JTree) c).getPreferredSize();
1580:     return new Dimension();
1581:   }
1582: 
1583:   /**
1584:    * Messages to stop the editing session. If the UI the receiver is providing
1585:    * the look and feel for returns true from
1586:    * <code>getInvokesStopCellEditing</code>, stopCellEditing will be invoked
1587:    * on the current editor. Then completeEditing will be messaged with false,
1588:    * true, false to cancel any lingering editing.
1589:    */
1590:   protected void completeEditing()
1591:   {
1592:     completeEditing(false, true, false);
1593:   }
1594: 
1595:   /**
1596:    * Stops the editing session. If messageStop is true, the editor is messaged
1597:    * with stopEditing, if messageCancel is true the editor is messaged with
1598:    * cancelEditing. If messageTree is true, the treeModel is messaged with
1599:    * valueForPathChanged.
1600:    * 
1601:    * @param messageStop
1602:    *          message to stop editing
1603:    * @param messageCancel
1604:    *          message to cancel editing
1605:    * @param messageTree
1606:    *          message to treeModel
1607:    */
1608:   protected void completeEditing(boolean messageStop, boolean messageCancel,
1609:                                  boolean messageTree)
1610:   {
1611:     if (messageStop)
1612:       {
1613:         getCellEditor().stopCellEditing();
1614:         stopEditingInCompleteEditing = true;
1615:       }
1616: 
1617:     if (messageCancel)
1618:       {
1619:         getCellEditor().cancelCellEditing();
1620:         stopEditingInCompleteEditing = true;
1621:       }
1622: 
1623:     if (messageTree)
1624:       treeModel.valueForPathChanged(tree.getLeadSelectionPath(), newVal);
1625:   }
1626: 
1627:   /**
1628:    * Will start editing for node if there is a cellEditor and shouldSelectCall
1629:    * returns true. This assumes that path is valid and visible.
1630:    * 
1631:    * @param path
1632:    *          is the path to start editing
1633:    * @param event
1634:    *          is the MouseEvent performed on the path
1635:    * @return true if successful
1636:    */
1637:   protected boolean startEditing(TreePath path, MouseEvent event)
1638:   {
1639:     int x;
1640:     int y;
1641:     if (event == null)
1642:       {
1643:         Rectangle bounds = getPathBounds(tree, path);
1644:         x = bounds.x;
1645:         y = bounds.y;
1646:       }
1647:     else
1648:       {
1649:         x = event.getX();
1650:         y = event.getY();
1651:       }
1652: 
1653:     updateCellEditor();
1654:     TreeCellEditor ed = getCellEditor();
1655:     if (ed != null && ed.shouldSelectCell(event) && ed.isCellEditable(event))
1656:       {
1657:         editingPath = path;
1658:         editingRow = tree.getRowForPath(editingPath);
1659: 
1660:         Object val = editingPath.getLastPathComponent();
1661:         cellEditor.addCellEditorListener(cellEditorListener);
1662:         stopEditingInCompleteEditing = false;
1663:         boolean expanded = tree.isExpanded(editingPath);
1664:         isEditing = true;
1665:         editingComponent = ed.getTreeCellEditorComponent(tree, val, true,
1666:                                                          expanded,
1667:                                                          isLeaf(editingRow),
1668:                                                          editingRow);
1669:         editingComponent.getParent().setVisible(true);
1670:         editingComponent.getParent().validate();
1671:         tree.add(editingComponent.getParent());
1672:         editingComponent.getParent().validate();
1673:         validCachedPreferredSize = false;
1674: 
1675:         ((JTextField) editingComponent).requestFocusInWindow(false);
1676:         editorTimer.start();
1677:         return true;
1678:       }
1679:     return false;
1680:   }
1681: 
1682:   /**
1683:    * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
1684:    * collapse region of the row, this will toggle the row.
1685:    * 
1686:    * @param path
1687:    *          the path we are concerned with
1688:    * @param mouseX
1689:    *          is the cursor's x position
1690:    * @param mouseY
1691:    *          is the cursor's y position
1692:    */
1693:   protected void checkForClickInExpandControl(TreePath path, int mouseX,
1694:                                               int mouseY)
1695:   {
1696:     if (isLocationInExpandControl(path, mouseX, mouseY))
1697:       toggleExpandState(path);
1698:   }
1699: 
1700:   /**
1701:    * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall in
1702:    * the area of row that is used to expand/collpse the node and the node at row
1703:    * does not represent a leaf.
1704:    * 
1705:    * @param path
1706:    *          the path we are concerned with
1707:    * @param mouseX
1708:    *          is the cursor's x position
1709:    * @param mouseY
1710:    *          is the cursor's y position
1711:    * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in
1712:    *         the area of row that is used to expand/collpse the node and the
1713:    *         node at row does not represent a leaf.
1714:    */
1715:   protected boolean isLocationInExpandControl(TreePath path, int mouseX,
1716:                                               int mouseY)
1717:   {
1718:     boolean cntlClick = false;
1719:     int row = getRowForPath(tree, path);
1720: 
1721:     if (!isLeaf(row))
1722:       {
1723:         Rectangle bounds = getPathBounds(tree, path);
1724: 
1725:         if (hasControlIcons()
1726:             && (mouseX < bounds.x)
1727:             && (mouseX > (bounds.x - getCurrentControlIcon(path).getIconWidth() - gap)))
1728:           cntlClick = true;
1729:       }
1730:     return cntlClick;
1731:   }
1732: 
1733:   /**
1734:    * Messaged when the user clicks the particular row, this invokes
1735:    * toggleExpandState.
1736:    * 
1737:    * @param path
1738:    *          the path we are concerned with
1739:    * @param mouseX
1740:    *          is the cursor's x position
1741:    * @param mouseY
1742:    *          is the cursor's y position
1743:    */
1744:   protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY)
1745:   {
1746:     toggleExpandState(path);
1747:   }
1748: 
1749:   /**
1750:    * Expands path if it is not expanded, or collapses row if it is expanded. If
1751:    * expanding a path and JTree scroll on expand, ensureRowsAreVisible is
1752:    * invoked to scroll as many of the children to visible as possible (tries to
1753:    * scroll to last visible descendant of path).
1754:    * 
1755:    * @param path
1756:    *          the path we are concerned with
1757:    */
1758:   protected void toggleExpandState(TreePath path)
1759:   {
1760:     if (tree.isExpanded(path))
1761:       tree.collapsePath(path);
1762:     else
1763:       tree.expandPath(path);
1764:   }
1765: 
1766:   /**
1767:    * Returning true signifies a mouse event on the node should toggle the
1768:    * selection of only the row under the mouse.
1769:    * 
1770:    * @param event
1771:    *          is the MouseEvent performed on the row.
1772:    * @return true signifies a mouse event on the node should toggle the
1773:    *         selection of only the row under the mouse.
1774:    */
1775:   protected boolean isToggleSelectionEvent(MouseEvent event)
1776:   {
1777:     return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION);
1778:   }
1779: 
1780:   /**
1781:    * Returning true signifies a mouse event on the node should select from the
1782:    * anchor point.
1783:    * 
1784:    * @param event
1785:    *          is the MouseEvent performed on the node.
1786:    * @return true signifies a mouse event on the node should select from the
1787:    *         anchor point.
1788:    */
1789:   protected boolean isMultiSelectEvent(MouseEvent event)
1790:   {
1791:     return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
1792:   }
1793: 
1794:   /**
1795:    * Returning true indicates the row under the mouse should be toggled based on
1796:    * the event. This is invoked after checkForClickInExpandControl, implying the
1797:    * location is not in the expand (toggle) control.
1798:    * 
1799:    * @param event
1800:    *          is the MouseEvent performed on the row.
1801:    * @return true indicates the row under the mouse should be toggled based on
1802:    *         the event.
1803:    */
1804:   protected boolean isToggleEvent(MouseEvent event)
1805:   {
1806:     return true;
1807:   }
1808: 
1809:   /**
1810:    * Messaged to update the selection based on a MouseEvent over a particular
1811:    * row. If the even is a toggle selection event, the row is either selected,
1812:    * or deselected. If the event identifies a multi selection event, the
1813:    * selection is updated from the anchor point. Otherwise, the row is selected,
1814:    * and if the even specified a toggle event the row is expanded/collapsed.
1815:    * 
1816:    * @param path
1817:    *          is the path selected for an event
1818:    * @param event
1819:    *          is the MouseEvent performed on the path.
1820:    */
1821:   protected void selectPathForEvent(TreePath path, MouseEvent event)
1822:   {
1823:     if (isToggleSelectionEvent(event))
1824:       {
1825:         if (tree.isPathSelected(path))
1826:           tree.removeSelectionPath(path);
1827:         else
1828:           {
1829:             tree.addSelectionPath(path);
1830:             tree.setAnchorSelectionPath(path);
1831:           }
1832:       }
1833:     else if (isMultiSelectEvent(event))
1834:       {
1835:         TreePath anchor = tree.getAnchorSelectionPath();
1836:         if (anchor != null)
1837:           {
1838:             int aRow = getRowForPath(tree, anchor);
1839:             tree.addSelectionInterval(aRow, getRowForPath(tree, path));
1840:           }
1841:         else
1842:           tree.addSelectionPath(path);
1843:       }
1844:     else
1845:       tree.addSelectionPath(path);
1846:   }
1847: 
1848:   /**
1849:    * Returns true if the node at <code>row</code> is a leaf.
1850:    * 
1851:    * @param row
1852:    *          is the row we are concerned with.
1853:    * @return true if the node at <code>row</code> is a leaf.
1854:    */
1855:   protected boolean isLeaf(int row)
1856:   {
1857:     TreePath pathForRow = getPathForRow(tree, row);
1858:     if (pathForRow == null)
1859:       return true;
1860: 
1861:     Object node = pathForRow.getLastPathComponent();
1862:     return treeModel.isLeaf(node);
1863:   }
1864: 
1865:   /**
1866:    * This class implements the actions that we want to happen when specific keys
1867:    * are pressed for the JTree. The actionPerformed method is called when a key
1868:    * that has been registered for the JTree is received.
1869:    */
1870:   class TreeAction extends AbstractAction
1871:   {
1872: 
1873:     /**
1874:      * What to do when this action is called.
1875:      * 
1876:      * @param e
1877:      *          the ActionEvent that caused this action.
1878:      */
1879:     public void actionPerformed(ActionEvent e)
1880:     {
1881:       TreePath lead = tree.getLeadSelectionPath();
1882: 
1883:       if (e.getActionCommand().equals("selectPreviousChangeLead")
1884:           || e.getActionCommand().equals("selectPreviousExtendSelection")
1885:           || e.getActionCommand().equals("selectPrevious")
1886:           || e.getActionCommand().equals("selectNext")
1887:           || e.getActionCommand().equals("selectNextExtendSelection")
1888:           || e.getActionCommand().equals("selectNextChangeLead"))
1889:         (new TreeIncrementAction(0, "")).actionPerformed(e);
1890:       else if (e.getActionCommand().equals("selectParent")
1891:                || e.getActionCommand().equals("selectChild"))
1892:         (new TreeTraverseAction(0, "")).actionPerformed(e);
1893:       else if (e.getActionCommand().equals("selectAll"))
1894:         {
1895:           TreePath[] paths = new TreePath[tree.getVisibleRowCount()];
1896: 
1897:           Object curr = getNextVisibleNode(treeModel.getRoot());
1898:           int i = 0;
1899:           while (curr != null && i < paths.length)
1900:             {
1901:               paths[i] = new TreePath(getPathToRoot(curr, 0));
1902:               i++;
1903:             }
1904: 
1905:           tree.addSelectionPaths(paths);
1906:         }
1907:       else if (e.getActionCommand().equals("startEditing"))
1908:         tree.startEditingAtPath(lead);
1909:       else if (e.getActionCommand().equals("toggle"))
1910:         {
1911:           if (tree.isEditing())
1912:             tree.stopEditing();
1913:           else
1914:             {
1915:               Object last = lead.getLastPathComponent();
1916:               TreePath path = new TreePath(getPathToRoot(last, 0));
1917:               if (!treeModel.isLeaf(last))
1918:                 toggleExpandState(path);
1919:             }
1920:         }
1921:       else if (e.getActionCommand().equals("clearSelection"))
1922:         tree.clearSelection();
1923: 
1924:       if (tree.isEditing() && !e.getActionCommand().equals("startEditing"))
1925:         tree.cancelEditing();
1926: 
1927:       tree.scrollPathToVisible(lead);
1928:     }
1929:   }
1930: 
1931:   /**
1932:    * This class is used to mimic the behaviour of the JDK when registering
1933:    * keyboard actions. It is the same as the private class used in JComponent
1934:    * for the same reason. This class receives an action event and dispatches it
1935:    * to the true receiver after altering the actionCommand property of the
1936:    * event.
1937:    */
1938:   private static class ActionListenerProxy extends AbstractAction
1939:   {
1940:     ActionListener target;
1941: 
1942:     String bindingCommandName;
1943: 
1944:     public ActionListenerProxy(ActionListener li, String cmd)
1945:     {
1946:       target = li;
1947:       bindingCommandName = cmd;
1948:     }
1949: 
1950:     public void actionPerformed(ActionEvent e)
1951:     {
1952:       ActionEvent derivedEvent = new ActionEvent(e.getSource(), e.getID(),
1953:                                                  bindingCommandName,
1954:                                                  e.getModifiers());
1955: 
1956:       target.actionPerformed(derivedEvent);
1957:     }
1958:   }
1959: 
1960:   /**
1961:    * The timer that updates the editor component.
1962:    */
1963:   private class EditorUpdateTimer extends Timer implements ActionListener
1964:   {
1965:     /**
1966:      * Creates a new EditorUpdateTimer object with a default delay of 0.3
1967:      * seconds.
1968:      */
1969:     public EditorUpdateTimer()
1970:     {
1971:       super(300, null);
1972:       addActionListener(this);
1973:     }
1974: 
1975:     /**
1976:      * Lets the caret blink and repaints the table.
1977:      */
1978:     public void actionPerformed(ActionEvent ev)
1979:     {
1980:       Caret c = ((JTextField) editingComponent).getCaret();
1981:       if (c != null)
1982:         c.setVisible(!c.isVisible());
1983:       tree.repaint();
1984:     }
1985: 
1986:     /**
1987:      * Updates the blink delay according to the current caret.
1988:      */
1989:     public void update()
1990:     {
1991:       stop();
1992:       Caret c = ((JTextField) editingComponent).getCaret();
1993:       if (c != null)
1994:         {
1995:           setDelay(c.getBlinkRate());
1996:           if (((JTextField) editingComponent).isEditable())
1997:             start();
1998:           else
1999:             c.setVisible(false);
2000:         }
2001:     }
2002:   }
2003: 
2004:   /**
2005:    * Updates the preferred size when scrolling, if necessary.
2006:    */
2007:   public class ComponentHandler extends ComponentAdapter implements
2008:       ActionListener
2009:   {
2010:     /**
2011:      * Timer used when inside a scrollpane and the scrollbar is adjusting
2012:      */
2013:     protected Timer timer;
2014: 
2015:     /** ScrollBar that is being adjusted */
2016:     protected JScrollBar scrollBar;
2017: 
2018:     /**
2019:      * Constructor
2020:      */
2021:     public ComponentHandler()
2022:     {
2023:       // Nothing to do here.
2024:     }
2025: 
2026:     /**
2027:      * Invoked when the component's position changes.
2028:      * 
2029:      * @param e
2030:      *          the event that occurs when moving the component
2031:      */
2032:     public void componentMoved(ComponentEvent e)
2033:     {
2034:       // TODO: What should be done here, if anything?
2035:     }
2036: 
2037:     /**
2038:      * Creates, if necessary, and starts a Timer to check if needed to resize
2039:      * the bounds
2040:      */
2041:     protected void startTimer()
2042:     {
2043:       // TODO: Implement this properly.
2044:     }
2045: 
2046:     /**
2047:      * Returns the JScrollPane housing the JTree, or null if one isn't found.
2048:      * 
2049:      * @return JScrollPane housing the JTree, or null if one isn't found.
2050:      */
2051:     protected JScrollPane getScrollPane()
2052:     {
2053:       return null;
2054:     }
2055: 
2056:     /**
2057:      * Public as a result of Timer. If the scrollBar is null, or not adjusting,
2058:      * this stops the timer and updates the sizing.
2059:      * 
2060:      * @param ae
2061:      *          is the action performed
2062:      */
2063:     public void actionPerformed(ActionEvent ae)
2064:     {
2065:       // TODO: Implement this properly.
2066:     }
2067:   }
2068: 
2069:   /**
2070:    * Listener responsible for getting cell editing events and updating the tree
2071:    * accordingly.
2072:    */
2073:   public class CellEditorHandler implements CellEditorListener
2074:   {
2075:     /**
2076:      * Constructor
2077:      */
2078:     public CellEditorHandler()
2079:     {
2080:       // Nothing to do here.
2081:     }
2082: 
2083:     /**
2084:      * Messaged when editing has stopped in the tree. Tells the listeners
2085:      * editing has stopped.
2086:      * 
2087:      * @param e
2088:      *          is the notification event
2089:      */
2090:     public void editingStopped(ChangeEvent e)
2091:     {
2092:       editingPath = null;
2093:       editingRow = -1;
2094:       stopEditingInCompleteEditing = false;
2095:       if (editingComponent != null)
2096:         {
2097:           tree.remove(editingComponent.getParent());
2098:           editingComponent = null;
2099:         }
2100:       if (cellEditor != null)
2101:         {
2102:           newVal = ((JTextField) getCellEditor().getCellEditorValue()).getText();
2103:           completeEditing(false, false, true);
2104:           if (cellEditor instanceof DefaultTreeCellEditor)
2105:             tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor);
2106:           cellEditor.removeCellEditorListener(cellEditorListener);
2107:           setCellEditor(null);
2108:           createdCellEditor = false;
2109:         }
2110:       isEditing = false;
2111:       tree.requestFocusInWindow(false);
2112:       editorTimer.stop();
2113:       validCachedPreferredSize = false;
2114:       tree.repaint();
2115:     }
2116: 
2117:     /**
2118:      * Messaged when editing has been canceled in the tree. This tells the
2119:      * listeners the editor has canceled editing.
2120:      * 
2121:      * @param e
2122:      *          is the notification event
2123:      */
2124:     public void editingCanceled(ChangeEvent e)
2125:     {
2126:       editingPath = null;
2127:       editingRow = -1;
2128:       stopEditingInCompleteEditing = false;
2129:       if (editingComponent != null)
2130:         tree.remove(editingComponent.getParent());
2131:       editingComponent = null;
2132:       if (cellEditor != null)
2133:         {
2134:           if (cellEditor instanceof DefaultTreeCellEditor)
2135:             tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor);
2136:           cellEditor.removeCellEditorListener(cellEditorListener);
2137:           setCellEditor(null);
2138:           createdCellEditor = false;
2139:         }
2140:       tree.requestFocusInWindow(false);
2141:       editorTimer.stop();
2142:       isEditing = false;
2143:       validCachedPreferredSize = false;
2144:       tree.repaint();
2145:     }
2146:   }// CellEditorHandler
2147: 
2148:   /**
2149:    * Repaints the lead selection row when focus is lost/grained.
2150:    */
2151:   public class FocusHandler implements FocusListener
2152:   {
2153:     /**
2154:      * Constructor
2155:      */
2156:     public FocusHandler()
2157:     {
2158:       // Nothing to do here.
2159:     }
2160: 
2161:     /**
2162:      * Invoked when focus is activated on the tree we're in, redraws the lead
2163:      * row. Invoked when a component gains the keyboard focus.
2164:      * 
2165:      * @param e
2166:      *          is the focus event that is activated
2167:      */
2168:     public void focusGained(FocusEvent e)
2169:     {
2170:       // TODO: Implement this properly.
2171:     }
2172: 
2173:     /**
2174:      * Invoked when focus is deactivated on the tree we're in, redraws the lead
2175:      * row. Invoked when a component loses the keyboard focus.
2176:      * 
2177:      * @param e
2178:      *          is the focus event that is deactivated
2179:      */
2180:     public void focusLost(FocusEvent e)
2181:     {
2182:       // TODO: Implement this properly.
2183:     }
2184:   }
2185: 
2186:   /**
2187:    * This is used to get multiple key down events to appropriately genereate
2188:    * events.
2189:    */
2190:   public class KeyHandler extends KeyAdapter
2191:   {
2192:     /** Key code that is being generated for. */
2193:     protected Action repeatKeyAction;
2194: 
2195:     /** Set to true while keyPressed is active */
2196:     protected boolean isKeyDown;
2197: 
2198:     /**
2199:      * Constructor
2200:      */
2201:     public KeyHandler()
2202:     {
2203:       // Nothing to do here.
2204:     }
2205: 
2206:     /**
2207:      * Invoked when a key has been typed. Moves the keyboard focus to the first
2208:      * element whose first letter matches the alphanumeric key pressed by the
2209:      * user. Subsequent same key presses move the keyboard focus to the next
2210:      * object that starts with the same letter.
2211:      * 
2212:      * @param e
2213:      *          the key typed
2214:      */
2215:     public void keyTyped(KeyEvent e)
2216:     {
2217:       // TODO: What should be done here, if anything?
2218:     }
2219: 
2220:     /**
2221:      * Invoked when a key has been pressed.
2222:      * 
2223:      * @param e
2224:      *          the key pressed
2225:      */
2226:     public void keyPressed(KeyEvent e)
2227:     {
2228:       // TODO: What should be done here, if anything?
2229:     }
2230: 
2231:     /**
2232:      * Invoked when a key has been released
2233:      * 
2234:      * @param e
2235:      *          the key released
2236:      */
2237:     public void keyReleased(KeyEvent e)
2238:     {
2239:       // TODO: What should be done here, if anything?
2240:     }
2241:   }
2242: 
2243:   /**
2244:    * MouseListener is responsible for updating the selection based on mouse
2245:    * events.
2246:    */
2247:   public class MouseHandler extends MouseAdapter implements MouseMotionListener
2248:   {
2249:     /**
2250:      * Constructor
2251:      */
2252:     public MouseHandler()
2253:     {
2254:       // Nothing to do here.
2255:     }
2256: 
2257:     /**
2258:      * Invoked when a mouse button has been pressed on a component.
2259:      * 
2260:      * @param e
2261:      *          is the mouse event that occured
2262:      */
2263:     public void mousePressed(MouseEvent e)
2264:     {
2265:       Point click = e.getPoint();
2266:       TreePath path = getClosestPathForLocation(tree, click.x, click.y);
2267: 
2268:       if (path != null)
2269:         {
2270:           Rectangle bounds = getPathBounds(tree, path);
2271:           int row = getRowForPath(tree, path);
2272:           boolean cntlClick = isLocationInExpandControl(path, click.x, click.y);
2273: 
2274:           boolean isLeaf = isLeaf(row);
2275: 
2276:           TreeCellRenderer tcr = getCellRenderer();
2277:           Icon icon;
2278:           if (isLeaf)
2279:             icon = UIManager.getIcon("Tree.leafIcon");
2280:           else if (tree.isExpanded(path))
2281:             icon = UIManager.getIcon("Tree.openIcon");
2282:           else
2283:             icon = UIManager.getIcon("Tree.closedIcon");
2284: 
2285:           if (tcr instanceof DefaultTreeCellRenderer)
2286:             {
2287:               Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon();
2288:               if (tmp != null)
2289:                 icon = tmp;
2290:             }
2291: 
2292:           // add gap*2 for the space before and after the text
2293:           if (icon != null)
2294:             bounds.width += icon.getIconWidth() + gap * 2;
2295: 
2296:           boolean inBounds = bounds.contains(click.x, click.y);
2297:           if ((inBounds || cntlClick) && tree.isVisible(path))
2298:             {
2299:               if (inBounds)
2300:                 {
2301:                   selectPath(tree, path);
2302:                   if (e.getClickCount() == 2 && !isLeaf(row))
2303:                     toggleExpandState(path);
2304:                 }
2305: 
2306:               if (cntlClick)
2307:                 {
2308:                   handleExpandControlClick(path, click.x, click.y);
2309:                   if (cellEditor != null)
2310:                     cellEditor.cancelCellEditing();
2311:                   tree.scrollPathToVisible(path);
2312:                 }
2313:               else if (tree.isEditable())
2314:                 startEditing(path, e);
2315:             }
2316:         }
2317:     }
2318: 
2319:     /**
2320:      * Invoked when a mouse button is pressed on a component and then dragged.
2321:      * MOUSE_DRAGGED events will continue to be delivered to the component where
2322:      * the drag originated until the mouse button is released (regardless of
2323:      * whether the mouse position is within the bounds of the component).
2324:      * 
2325:      * @param e
2326:      *          is the mouse event that occured
2327:      */
2328:     public void mouseDragged(MouseEvent e)
2329:     {
2330:       // TODO: What should be done here, if anything?
2331:     }
2332: 
2333:     /**
2334:      * Invoked when the mouse button has been moved on a component (with no
2335:      * buttons no down).
2336:      * 
2337:      * @param e
2338:      *          the mouse event that occured
2339:      */
2340:     public void mouseMoved(MouseEvent e)
2341:     {
2342:       // TODO: What should be done here, if anything?
2343:     }
2344: 
2345:     /**
2346:      * Invoked when a mouse button has been released on a component.
2347:      * 
2348:      * @param e
2349:      *          is the mouse event that occured
2350:      */
2351:     public void mouseReleased(MouseEvent e)
2352:     {
2353:       // TODO: What should be done here, if anything?
2354:     }
2355:   }
2356: 
2357:   /**
2358:    * MouseInputHandler handles passing all mouse events, including mouse motion
2359:    * events, until the mouse is released to the destination it is constructed
2360:    * with.
2361:    */
2362:   public class MouseInputHandler implements MouseInputListener
2363:   {
2364:     /** Source that events are coming from */
2365:     protected Component source;
2366: 
2367:     /** Destination that receives all events. */
2368:     protected Component destination;
2369: 
2370:     /**
2371:      * Constructor
2372:      * 
2373:      * @param source
2374:      *          that events are coming from
2375:      * @param destination
2376:      *          that receives all events
2377:      * @param e
2378:      *          is the event received
2379:      */
2380:     public MouseInputHandler(Component source, Component destination,
2381:                              MouseEvent e)
2382:     {
2383:       this.source = source;
2384:       this.destination = destination;
2385:     }
2386: 
2387:     /**
2388:      * Invoked when the mouse button has been clicked (pressed and released) on
2389:      * a component.
2390:      * 
2391:      * @param e
2392:      *          mouse event that occured
2393:      */
2394:     public void mouseClicked(MouseEvent e)
2395:     {
2396:       // TODO: What should be done here, if anything?
2397:     }
2398: 
2399:     /**
2400:      * Invoked when a mouse button has been pressed on a component.
2401:      * 
2402:      * @param e
2403:      *          mouse event that occured
2404:      */
2405:     public void mousePressed(MouseEvent e)
2406:     {
2407:       // TODO: What should be done here, if anything?
2408:     }
2409: 
2410:     /**
2411:      * Invoked when a mouse button has been released on a component.
2412:      * 
2413:      * @param e
2414:      *          mouse event that occured
2415:      */
2416:     public void mouseReleased(MouseEvent e)
2417:     {
2418:       // TODO: What should be done here, if anything?
2419:     }
2420: 
2421:     /**
2422:      * Invoked when the mouse enters a component.
2423:      * 
2424:      * @param e
2425:      *          mouse event that occured
2426:      */
2427:     public void mouseEntered(MouseEvent e)
2428:     {
2429:       // TODO: What should be done here, if anything?
2430:     }
2431: 
2432:     /**
2433:      * Invoked when the mouse exits a component.
2434:      * 
2435:      * @param e
2436:      *          mouse event that occured
2437:      */
2438:     public void mouseExited(MouseEvent e)
2439:     {
2440:       // TODO: What should be done here, if anything?
2441:     }
2442: 
2443:     /**
2444:      * Invoked when a mouse button is pressed on a component and then dragged.
2445:      * MOUSE_DRAGGED events will continue to be delivered to the component where
2446:      * the drag originated until the mouse button is released (regardless of
2447:      * whether the mouse position is within the bounds of the component).
2448:      * 
2449:      * @param e
2450:      *          mouse event that occured
2451:      */
2452:     public void mouseDragged(MouseEvent e)
2453:     {
2454:       // TODO: What should be done here, if anything?
2455:     }
2456: 
2457:     /**
2458:      * Invoked when the mouse cursor has been moved onto a component but no
2459:      * buttons have been pushed.
2460:      * 
2461:      * @param e
2462:      *          mouse event that occured
2463:      */
2464:     public void mouseMoved(MouseEvent e)
2465:     {
2466:       // TODO: What should be done here, if anything?
2467:     }
2468: 
2469:     /**
2470:      * Removes event from the source
2471:      */
2472:     protected void removeFromSource()
2473:     {
2474:       // TODO: Implement this properly.
2475:     }
2476:   }
2477: 
2478:   /**
2479:    * Class responsible for getting size of node, method is forwarded to
2480:    * BasicTreeUI method. X location does not include insets, that is handled in
2481:    * getPathBounds.
2482:    */
2483:   public class NodeDimensionsHandler extends AbstractLayoutCache.NodeDimensions
2484:   {
2485:     /**
2486:      * Constructor
2487:      */
2488:     public NodeDimensionsHandler()
2489:     {
2490:       // Nothing to do here.
2491:     }
2492: 
2493:     /**
2494:      * Returns, by reference in bounds, the size and x origin to place value at.
2495:      * The calling method is responsible for determining the Y location. If
2496:      * bounds is null, a newly created Rectangle should be returned, otherwise
2497:      * the value should be placed in bounds and returned.
2498:      * 
2499:      * @param cell
2500:      *          the value to be represented
2501:      * @param row
2502:      *          row being queried
2503:      * @param depth
2504:      *          the depth of the row
2505:      * @param expanded
2506:      *          true if row is expanded
2507:      * @param size
2508:      *          a Rectangle containing the size needed to represent value
2509:      * @return containing the node dimensions, or null if node has no dimension
2510:      */
2511:     public Rectangle getNodeDimensions(Object cell, int row, int depth,
2512:                                        boolean expanded, Rectangle size)
2513:     {
2514:       if (size == null || cell == null)
2515:         return null;
2516: 
2517:       String s = cell.toString();
2518:       Font f = tree.getFont();
2519:       FontMetrics fm = tree.getToolkit().getFontMetrics(f);
2520: 
2521:       if (s != null)
2522:         {
2523:           size.x = getRowX(row, depth);
2524:           size.width = SwingUtilities.computeStringWidth(fm, s);
2525:           size.height = getMaxHeight(tree);
2526:           size.y = size.height * row;
2527:         }
2528: 
2529:       return size;
2530:     }
2531: 
2532:     /**
2533:      * Returns the amount to indent the given row
2534:      * 
2535:      * @return amount to indent the given row.
2536:      */
2537:     protected int getRowX(int row, int depth)
2538:     {
2539:       if (row == 0)
2540:         return 0;
2541:       return depth * rightChildIndent;
2542:     }
2543:   }// NodeDimensionsHandler
2544: 
2545:   /**
2546:    * PropertyChangeListener for the tree. Updates the appropriate varaible, or
2547:    * TreeState, based on what changes.
2548:    */
2549:   public class PropertyChangeHandler implements PropertyChangeListener
2550:   {
2551: 
2552:     /**
2553:      * Constructor
2554:      */
2555:     public PropertyChangeHandler()
2556:     {
2557:       // Nothing to do here.
2558:     }
2559: 
2560:     /**
2561:      * This method gets called when a bound property is changed.
2562:      * 
2563:      * @param event
2564:      *          A PropertyChangeEvent object describing the event source and the
2565:      *          property that has changed.
2566:      */
2567:     public void propertyChange(PropertyChangeEvent event)
2568:     {
2569:       if ((event.getPropertyName()).equals("rootVisible"))
2570:         {
2571:           validCachedPreferredSize = false;
2572:           tree.repaint();
2573:         }
2574:     }
2575:   }
2576: 
2577:   /**
2578:    * Listener on the TreeSelectionModel, resets the row selection if any of the
2579:    * properties of the model change.
2580:    */
2581:   public class SelectionModelPropertyChangeHandler implements
2582:       PropertyChangeListener
2583:   {
2584: 
2585:     /**
2586:      * Constructor
2587:      */
2588:     public SelectionModelPropertyChangeHandler()
2589:     {
2590:       // Nothing to do here.
2591:     }
2592: 
2593:     /**
2594:      * This method gets called when a bound property is changed.
2595:      * 
2596:      * @param event
2597:      *          A PropertyChangeEvent object describing the event source and the
2598:      *          property that has changed.
2599:      */
2600:     public void propertyChange(PropertyChangeEvent event)
2601:     {
2602:       // TODO: What should be done here, if anything?
2603:     }
2604:   }
2605: 
2606:   /**
2607:    * ActionListener that invokes cancelEditing when action performed.
2608:    */
2609:   public class TreeCancelEditingAction extends AbstractAction
2610:   {
2611: 
2612:     /**
2613:      * Constructor
2614:      */
2615:     public TreeCancelEditingAction(String name)
2616:     {
2617:       // TODO: Implement this properly.
2618:     }
2619: 
2620:     /**
2621:      * Invoked when an action occurs.
2622:      * 
2623:      * @param e
2624:      *          event that occured
2625:      */
2626:     public void actionPerformed(ActionEvent e)
2627:     {
2628:       // TODO: Implement this properly.
2629:     }
2630: 
2631:     /**
2632:      * Returns true if the action is enabled.
2633:      * 
2634:      * @return true if the action is enabled, false otherwise
2635:      */
2636:     public boolean isEnabled()
2637:     {
2638:       // TODO: Implement this properly.
2639:       return false;
2640:     }
2641:   }
2642: 
2643:   /**
2644:    * Updates the TreeState in response to nodes expanding/collapsing.
2645:    */
2646:   public class TreeExpansionHandler implements TreeExpansionListener
2647:   {
2648: 
2649:     /**
2650:      * Constructor
2651:      */
2652:     public TreeExpansionHandler()
2653:     {
2654:       // Nothing to do here.
2655:     }
2656: 
2657:     /**
2658:      * Called whenever an item in the tree has been expanded.
2659:      * 
2660:      * @param event
2661:      *          is the event that occured
2662:      */
2663:     public void treeExpanded(TreeExpansionEvent event)
2664:     {
2665:       validCachedPreferredSize = false;
2666:       tree.repaint();
2667:     }
2668: 
2669:     /**
2670:      * Called whenever an item in the tree has been collapsed.
2671:      * 
2672:      * @param event
2673:      *          is the event that occured
2674:      */
2675:     public void treeCollapsed(TreeExpansionEvent event)
2676:     {
2677:       validCachedPreferredSize = false;
2678:       tree.repaint();
2679:     }
2680:   }// TreeExpansionHandler
2681: 
2682:   /**
2683:    * TreeHomeAction is used to handle end/home actions. Scrolls either the first
2684:    * or last cell to be visible based on direction.
2685:    */
2686:   public class TreeHomeAction extends AbstractAction
2687:   {
2688: 
2689:     /** direction is either home or end */
2690:     protected int direction;
2691: 
2692:     /**
2693:      * Constructor
2694:      * 
2695:      * @param direction -
2696:      *          it is home or end
2697:      * @param name
2698:      *          is the name of the direction
2699:      */
2700:     public TreeHomeAction(int direction, String name)
2701:     {
2702:       // TODO: Implement this properly
2703:     }
2704: 
2705:     /**
2706:      * Invoked when an action occurs.
2707:      * 
2708:      * @param e
2709:      *          is the event that occured
2710:      */
2711:     public void actionPerformed(ActionEvent e)
2712:     {
2713:       // TODO: Implement this properly
2714:     }
2715: 
2716:     /**
2717:      * Returns true if the action is enabled.
2718:      * 
2719:      * @return true if the action is enabled.
2720:      */
2721:     public boolean isEnabled()
2722:     {
2723:       // TODO: Implement this properly
2724:       return false;
2725:     }
2726:   }
2727: 
2728:   /**
2729:    * TreeIncrementAction is used to handle up/down actions. Selection is moved
2730:    * up or down based on direction.
2731:    */
2732:   public class TreeIncrementAction extends AbstractAction
2733:   {
2734: 
2735:     /** Specifies the direction to adjust the selection by. */
2736:     protected int direction;
2737: 
2738:     /**
2739:      * Constructor
2740:      * 
2741:      * @param direction
2742:      *          up or down
2743:      * @param name
2744:      *          is the name of the direction
2745:      */
2746:     public TreeIncrementAction(int direction, String name)
2747:     {
2748:       // TODO: Implement this properly
2749:     }
2750: 
2751:     /**
2752:      * Invoked when an action occurs.
2753:      * 
2754:      * @param e
2755:      *          is the event that occured
2756:      */
2757:     public void actionPerformed(ActionEvent e)
2758:     {
2759:       Object last = tree.getLeadSelectionPath().getLastPathComponent();
2760: 
2761:       if (e.getActionCommand().equals("selectPreviousChangeLead"))
2762:         {
2763:           Object prev = getPreviousVisibleNode(last);
2764: 
2765:           if (prev != null)
2766:             {
2767:               TreePath newPath = new TreePath(getPathToRoot(prev, 0));
2768:               selectPath(tree, newPath);
2769:               tree.setLeadSelectionPath(newPath);
2770:             }
2771:         }
2772:       else if (e.getActionCommand().equals("selectPreviousExtendSelection"))
2773:         {
2774:           Object prev = getPreviousVisibleNode(last);
2775:           if (prev != null)
2776:             {
2777:               TreePath newPath = new TreePath(getPathToRoot(prev, 0));
2778:               tree.addSelectionPath(newPath);
2779:               tree.setLeadSelectionPath(newPath);
2780:             }
2781:         }
2782:       else if (e.getActionCommand().equals("selectPrevious"))
2783:         {
2784:           Object prev = getPreviousVisibleNode(last);
2785: 
2786:           if (prev != null)
2787:             {
2788:               TreePath newPath = new TreePath(getPathToRoot(prev, 0));
2789:               selectPath(tree, newPath);
2790:             }
2791:         }
2792:       else if (e.getActionCommand().equals("selectNext"))
2793:         {
2794:           Object next = getNextVisibleNode(last);
2795: 
2796:           if (next != null)
2797:             {
2798:               TreePath newPath = new TreePath(getPathToRoot(next, 0));
2799:               selectPath(tree, newPath);
2800:             }
2801:         }
2802:       else if (e.getActionCommand().equals("selectNextExtendSelection"))
2803:         {
2804:           Object next = getNextVisibleNode(last);
2805:           if (next != null)
2806:             {
2807:               TreePath newPath = new TreePath(getPathToRoot(next, 0));
2808:               tree.addSelectionPath(newPath);
2809:               tree.setLeadSelectionPath(newPath);
2810:             }
2811:         }
2812:       else if (e.getActionCommand().equals("selectNextChangeLead"))
2813:         {
2814:           Object next = getNextVisibleNode(last);
2815:           if (next != null)
2816:             {
2817:               TreePath newPath = new TreePath(getPathToRoot(next, 0));
2818:               selectPath(tree, newPath);
2819:               tree.setLeadSelectionPath(newPath);
2820:             }
2821:         }
2822:     }
2823: 
2824:     /**
2825:      * Returns true if the action is enabled.
2826:      * 
2827:      * @return true if the action is enabled.
2828:      */
2829:     public boolean isEnabled()
2830:     {
2831:       // TODO: Implement this properly
2832:       return false;
2833:     }
2834:   }
2835: 
2836:   /**
2837:    * Forwards all TreeModel events to the TreeState.
2838:    */
2839:   public class TreeModelHandler implements TreeModelListener
2840:   {
2841:     /**
2842:      * Constructor
2843:      */
2844:     public TreeModelHandler()
2845:     {
2846:       // Nothing to do here.
2847:     }
2848: 
2849:     /**
2850:      * Invoked after a node (or a set of siblings) has changed in some way. The
2851:      * node(s) have not changed locations in the tree or altered their children
2852:      * arrays, but other attributes have changed and may affect presentation.
2853:      * Example: the name of a file has changed, but it is in the same location
2854:      * in the file system. To indicate the root has changed, childIndices and
2855:      * children will be null. Use e.getPath() to get the parent of the changed
2856:      * node(s). e.getChildIndices() returns the index(es) of the changed
2857:      * node(s).
2858:      * 
2859:      * @param e
2860:      *          is the event that occured
2861:      */
2862:     public void treeNodesChanged(TreeModelEvent e)
2863:     {
2864:       validCachedPreferredSize = false;
2865:       tree.repaint();
2866:     }
2867: 
2868:     /**
2869:      * Invoked after nodes have been inserted into the tree. Use e.getPath() to
2870:      * get the parent of the new node(s). e.getChildIndices() returns the
2871:      * index(es) of the new node(s) in ascending order.
2872:      * 
2873:      * @param e
2874:      *          is the event that occured
2875:      */
2876:     public void treeNodesInserted(TreeModelEvent e)
2877:     {
2878:       validCachedPreferredSize = false;
2879:       tree.repaint();
2880:     }
2881: 
2882:     /**
2883:      * Invoked after nodes have been removed from the tree. Note that if a
2884:      * subtree is removed from the tree, this method may only be invoked once
2885:      * for the root of the removed subtree, not once for each individual set of
2886:      * siblings removed. Use e.getPath() to get the former parent of the deleted
2887:      * node(s). e.getChildIndices() returns, in ascending order, the index(es)
2888:      * the node(s) had before being deleted.
2889:      * 
2890:      * @param e
2891:      *          is the event that occured
2892:      */
2893:     public void treeNodesRemoved(TreeModelEvent e)
2894:     {
2895:       validCachedPreferredSize = false;
2896:       tree.repaint();
2897:     }
2898: 
2899:     /**
2900:      * Invoked after the tree has drastically changed structure from a given
2901:      * node down. If the path returned by e.getPath() is of length one and the
2902:      * first element does not identify the current root node the first element
2903:      * should become the new root of the tree. Use e.getPath() to get the path
2904:      * to the node. e.getChildIndices() returns null.
2905:      * 
2906:      * @param e
2907:      *          is the event that occured
2908:      */
2909:     public void treeStructureChanged(TreeModelEvent e)
2910:     {
2911:       if (e.getPath().length == 1
2912:           && !e.getPath()[0].equals(treeModel.getRoot()))
2913:         tree.expandPath(new TreePath(treeModel.getRoot()));
2914:       validCachedPreferredSize = false;
2915:       tree.repaint();
2916:     }
2917:   }// TreeModelHandler
2918: 
2919:   /**
2920:    * TreePageAction handles page up and page down events.
2921:    */
2922:   public class TreePageAction extends AbstractAction
2923:   {
2924:     /** Specifies the direction to adjust the selection by. */
2925:     protected int direction;
2926: 
2927:     /**
2928:      * Constructor
2929:      * 
2930:      * @param direction
2931:      *          up or down
2932:      * @param name
2933:      *          is the name of the direction
2934:      */
2935:     public TreePageAction(int direction, String name)
2936:     {
2937:       this.direction = direction;
2938:     }
2939: 
2940:     /**
2941:      * Invoked when an action occurs.
2942:      * 
2943:      * @param e
2944:      *          is the event that occured
2945:      */
2946:     public void actionPerformed(ActionEvent e)
2947:     {
2948:       // TODO: Implement this properly.
2949:     }
2950: 
2951:     /**
2952:      * Returns true if the action is enabled.
2953:      * 
2954:      * @return true if the action is enabled.
2955:      */
2956:     public boolean isEnabled()
2957:     {
2958:       return false;
2959:     }
2960:   }// TreePageAction
2961: 
2962:   /**
2963:    * Listens for changes in the selection model and updates the display
2964:    * accordingly.
2965:    */
2966:   public class TreeSelectionHandler implements TreeSelectionListener
2967:   {
2968:     /**
2969:      * Constructor
2970:      */
2971:     public TreeSelectionHandler()
2972:     {
2973:       // Nothing to do here.
2974:     }
2975: 
2976:     /**
2977:      * Messaged when the selection changes in the tree we're displaying for.
2978:      * Stops editing, messages super and displays the changed paths.
2979:      * 
2980:      * @param event
2981:      *          the event that characterizes the change.
2982:      */
2983:     public void valueChanged(TreeSelectionEvent event)
2984:     {
2985:       if (tree.isEditing())
2986:         tree.cancelEditing();
2987:     }
2988:   }// TreeSelectionHandler
2989: 
2990:   /**
2991:    * For the first selected row expandedness will be toggled.
2992:    */
2993:   public class TreeToggleAction extends AbstractAction
2994:   {
2995:     /**
2996:      * Constructor
2997:      * 
2998:      * @param name
2999:      *          is the name of <code>Action</code> field
3000:      */
3001:     public TreeToggleAction(String name)
3002:     {
3003:       // Nothing to do here.
3004:     }
3005: 
3006:     /**
3007:      * Invoked when an action occurs.
3008:      * 
3009:      * @param e
3010:      *          the event that occured
3011:      */
3012:     public void actionPerformed(ActionEvent e)
3013:     {
3014:       // TODO: Implement this properly.
3015:     }
3016: 
3017:     /**
3018:      * Returns true if the action is enabled.
3019:      * 
3020:      * @return true if the action is enabled, false otherwise
3021:      */
3022:     public boolean isEnabled()
3023:     {
3024:       return false;
3025:     }
3026:   } // TreeToggleAction
3027: 
3028:   /**
3029:    * TreeTraverseAction is the action used for left/right keys. Will toggle the
3030:    * expandedness of a node, as well as potentially incrementing the selection.
3031:    */
3032:   public class TreeTraverseAction extends AbstractAction
3033:   {
3034:     /**
3035:      * Determines direction to traverse, 1 means expand, -1 means collapse.
3036:      */
3037:     protected int direction;
3038: 
3039:     /**
3040:      * Constructor
3041:      * 
3042:      * @param direction
3043:      *          to traverse
3044:      * @param name
3045:      *          is the name of the direction
3046:      */
3047:     public TreeTraverseAction(int direction, String name)
3048:     {
3049:       this.direction = direction;
3050:     }
3051: 
3052:     /**
3053:      * Invoked when an action occurs.
3054:      * 
3055:      * @param e
3056:      *          the event that occured
3057:      */
3058:     public void actionPerformed(ActionEvent e)
3059:     {
3060:       Object last = tree.getLeadSelectionPath().getLastPathComponent();
3061: 
3062:       if (e.getActionCommand().equals("selectParent"))
3063:         {
3064:           TreePath path = new TreePath(getPathToRoot(last, 0));
3065:           Object p = getParent(treeModel.getRoot(), last);
3066: 
3067:           if (!treeModel.isLeaf(last))
3068:             toggleExpandState(path);
3069:           else if (p != null)
3070:             selectPath(tree, new TreePath(getPathToRoot(p, 0)));
3071:         }
3072:       else if (e.getActionCommand().equals("selectChild"))
3073:         {
3074:           TreePath path = new TreePath(getPathToRoot(last, 0));
3075: 
3076:           if (!treeModel.isLeaf(last))
3077:             toggleExpandState(path);
3078:           else
3079:             {
3080:               Object next = getNextVisibleNode(last);
3081: 
3082:               if (next != null)
3083:                 selectPath(tree, new TreePath(getPathToRoot(next, 0)));
3084:             }
3085:         }
3086:     }
3087: 
3088:     /**
3089:      * Returns true if the action is enabled.
3090:      * 
3091:      * @return true if the action is enabled, false otherwise
3092:      */
3093:     public boolean isEnabled()
3094:     {
3095:       // TODO: Implement this properly
3096:       return false;
3097:     }
3098:   }
3099: 
3100:   /**
3101:    * Returns true if the LookAndFeel implements the control icons. Package
3102:    * private for use in inner classes.
3103:    * 
3104:    * @returns true if there are control icons
3105:    */
3106:   boolean hasControlIcons()
3107:   {
3108:     if (expandedIcon != null || collapsedIcon != null)
3109:       return true;
3110:     return false;
3111:   }
3112: 
3113:   /**
3114:    * Returns control icon. It is null if the LookAndFeel does not implements the
3115:    * control icons. Package private for use in inner classes.
3116:    * 
3117:    * @return control icon if it exists.
3118:    */
3119:   Icon getCurrentControlIcon(TreePath path)
3120:   {
3121:     if (tree.isExpanded(path))
3122:       return expandedIcon;
3123:     return collapsedIcon;
3124:   }
3125: 
3126:   /**
3127:    * Returns the parent of the current node
3128:    * 
3129:    * @param root
3130:    *          is the root of the tree
3131:    * @param node
3132:    *          is the current node
3133:    * @return is the parent of the current node
3134:    */
3135:   Object getParent(Object root, Object node)
3136:   {
3137:     if (root == null || node == null || root.equals(node))
3138:       return null;
3139: 
3140:     if (node instanceof TreeNode)
3141:       return ((TreeNode) node).getParent();
3142:     return findNode(root, node);
3143:   }
3144: 
3145:   /**
3146:    * Recursively checks the tree for the specified node, starting at the root.
3147:    * 
3148:    * @param root
3149:    *          is starting node to start searching at.
3150:    * @param node
3151:    *          is the node to search for
3152:    * @return the parent node of node
3153:    */
3154:   private Object findNode(Object root, Object node)
3155:   {
3156:     if (!treeModel.isLeaf(root) && !root.equals(node))
3157:       {
3158:         int size = treeModel.getChildCount(root);
3159:         for (int j = 0; j < size; j++)
3160:           {
3161:             Object child = treeModel.getChild(root, j);
3162:             if (node.equals(child))
3163:               return root;
3164: 
3165:             Object n = findNode(child, node);
3166:             if (n != null)
3167:               return n;
3168:           }
3169:       }
3170:     return null;
3171:   }
3172: 
3173:   /**
3174:    * Get previous visible node in the tree. Package private for use in inner
3175:    * classes.
3176:    * 
3177:    * @param node -
3178:    *          current node
3179:    * @return the next visible node in the JTree. Return null if there are no
3180:    *         more.
3181:    */
3182:   Object getPreviousVisibleNode(Object node)
3183:   {
3184:     if (currentVisiblePath != null)
3185:       {
3186:         Object[] nodes = currentVisiblePath.getPath();
3187:         int i = 0;
3188:         while (i < nodes.length && !node.equals(nodes[i]))
3189:           i++;
3190:         // return the next node
3191:         if (i - 1 >= 0)
3192:           return nodes[i - 1];
3193:       }
3194:     return null;
3195:   }
3196: 
3197:   /**
3198:    * Returns the next node in the tree Package private for use in inner classes.
3199:    * 
3200:    * @param curr -
3201:    *          current node
3202:    * @return the next node in the tree
3203:    */
3204:   Object getNextNode(Object curr)
3205:   {
3206:     if (!treeModel.isLeaf(curr) && treeModel.getChildCount(curr) > 0)
3207:       return treeModel.getChild(curr, 0);
3208: 
3209:     Object node = curr;
3210:     Object sibling = null;
3211:     do
3212:       {
3213:         sibling = getNextSibling(node);
3214:         node = getParent(treeModel.getRoot(), node);
3215:       }
3216:     while (sibling == null && node != null);
3217: 
3218:     return sibling;
3219:   }
3220: 
3221:   /**
3222:    * Returns the previous node in the tree Package private for use in inner
3223:    * classes.
3224:    * 
3225:    * @param node
3226:    *          current node
3227:    * @return the previous node in the tree
3228:    */
3229:   Object getPreviousNode(Object node)
3230:   {
3231:     Object parent = getParent(treeModel.getRoot(), node);
3232:     if (parent == null)
3233:       return null;
3234: 
3235:     Object sibling = getPreviousSibling(node);
3236: 
3237:     if (sibling == null)
3238:       return parent;
3239: 
3240:     int size = 0;
3241:     if (!treeModel.isLeaf(sibling))
3242:       size = treeModel.getChildCount(sibling);
3243:     while (size > 0)
3244:       {
3245:         sibling = treeModel.getChild(sibling, size - 1);
3246:         if (!treeModel.isLeaf(sibling))
3247:           size = treeModel.getChildCount(sibling);
3248:         else
3249:           size = 0;
3250:       }
3251: 
3252:     return sibling;
3253:   }
3254: 
3255:   /**
3256:    * Returns the next sibling in the tree Package private for use in inner
3257:    * classes.
3258:    * 
3259:    * @param node -
3260:    *          current node
3261:    * @return the next sibling in the tree
3262:    */
3263:   Object getNextSibling(Object node)
3264:   {
3265:     Object parent = getParent(treeModel.getRoot(), node);
3266:     if (parent == null)
3267:       return null;
3268: 
3269:     int index = treeModel.getIndexOfChild(parent, node) + 1;
3270: 
3271:     int size = 0;
3272:     if (!treeModel.isLeaf(parent))
3273:       size = treeModel.getChildCount(parent);
3274:     if (index == 0 || index >= size)
3275:       return null;
3276: 
3277:     return treeModel.getChild(parent, index);
3278:   }
3279: 
3280:   /**
3281:    * Returns the previous sibling in the tree Package private for use in inner
3282:    * classes.
3283:    * 
3284:    * @param node -
3285:    *          current node
3286:    * @return the previous sibling in the tree
3287:    */
3288:   Object getPreviousSibling(Object node)
3289:   {
3290:     Object parent = getParent(treeModel.getRoot(), node);
3291:     if (parent == null)
3292:       return null;
3293: 
3294:     int index = treeModel.getIndexOfChild(parent, node) - 1;
3295: 
3296:     int size = 0;
3297:     if (!treeModel.isLeaf(parent))
3298:       size = treeModel.getChildCount(parent);
3299:     if (index < 0 || index >= size)
3300:       return null;
3301: 
3302:     return treeModel.getChild(parent, index);
3303:   }
3304: 
3305:   /**
3306:    * Selects the specified path in the tree depending on modes. Package private
3307:    * for use in inner classes.
3308:    * 
3309:    * @param tree
3310:    *          is the tree we are selecting the path in
3311:    * @param path
3312:    *          is the path we are selecting
3313:    */
3314:   void selectPath(JTree tree, TreePath path)
3315:   {
3316:     if (path != null)
3317:       {
3318:         if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION)
3319:           {
3320:             tree.getSelectionModel().clearSelection();
3321:             tree.addSelectionPath(path);
3322:             tree.setLeadSelectionPath(path);
3323:           }
3324:         else if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION)
3325:           {
3326:             // TODO
3327:           }
3328:         else
3329:           {
3330:             tree.addSelectionPath(path);
3331:             tree.setLeadSelectionPath(path);
3332:             tree.getSelectionModel().setSelectionMode(
3333:                                                       TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
3334:           }
3335:       }
3336:   }
3337: 
3338:   /**
3339:    * Returns the path from node to the root. Package private for use in inner
3340:    * classes.
3341:    * 
3342:    * @param node
3343:    *          the node to get the path to
3344:    * @param depth
3345:    *          the depth of the tree to return a path for
3346:    * @return an array of tree nodes that represent the path to node.
3347:    */
3348:   Object[] getPathToRoot(Object node, int depth)
3349:   {
3350:     if (node == null)
3351:       {
3352:         if (depth == 0)
3353:           return null;
3354: 
3355:         return new Object[depth];
3356:       }
3357: 
3358:     Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node),
3359:                                   depth + 1);
3360:     path[path.length - depth - 1] = node;
3361:     return path;
3362:   }
3363: 
3364:   /**
3365:    * Returns the level of the node in the tree.
3366:    * 
3367:    * @param node -
3368:    *          current node
3369:    * @return the number of the level
3370:    */
3371:   int getLevel(Object node)
3372:   {
3373:     int count = -1;
3374: 
3375:     Object current = node;
3376: 
3377:     if (treeModel != null)
3378:       {
3379:         Object root = treeModel.getRoot();
3380:         if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root)))
3381:           count--;
3382: 
3383:         do
3384:           {
3385:             current = getParent(root, current);
3386:             count++;
3387:           }
3388:         while (current != null);
3389:       }
3390:     return count;
3391:   }
3392: 
3393:   /**
3394:    * Draws a vertical line using the given graphic context
3395:    * 
3396:    * @param g
3397:    *          is the graphic context
3398:    * @param c
3399:    *          is the component the new line will belong to
3400:    * @param x
3401:    *          is the horizonal position
3402:    * @param top
3403:    *          specifies the top of the line
3404:    * @param bottom
3405:    *          specifies the bottom of the line
3406:    */
3407:   protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
3408:                                    int bottom)
3409:   {
3410:     // FIXME: Check if drawing a dashed line or not.
3411:     g.setColor(getHashColor());
3412:     g.drawLine(x, top, x, bottom);
3413:   }
3414: 
3415:   /**
3416:    * Draws a horizontal line using the given graphic context
3417:    * 
3418:    * @param g
3419:    *          is the graphic context
3420:    * @param c
3421:    *          is the component the new line will belong to
3422:    * @param y
3423:    *          is the vertical position
3424:    * @param left
3425:    *          specifies the left point of the line
3426:    * @param right
3427:    *          specifies the right point of the line
3428:    */
3429:   protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left,
3430:                                      int right)
3431:   {
3432:     // FIXME: Check if drawing a dashed line or not.
3433:     g.setColor(getHashColor());
3434:     g.drawLine(left, y, right, y);
3435:   }
3436: 
3437:   /**
3438:    * Draws an icon at around a specific position
3439:    * 
3440:    * @param c
3441:    *          is the component the new line will belong to
3442:    * @param g
3443:    *          is the graphic context
3444:    * @param icon
3445:    *          is the icon which will be drawn
3446:    * @param x
3447:    *          is the center position in x-direction
3448:    * @param y
3449:    *          is the center position in y-direction
3450:    */
3451:   protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y)
3452:   {
3453:     x -= icon.getIconWidth() / 2;
3454:     y -= icon.getIconHeight() / 2;
3455: 
3456:     if (x < 0)
3457:       x = 0;
3458:     if (y < 0)
3459:       y = 0;
3460: 
3461:     icon.paintIcon(c, g, x, y);
3462:   }
3463: 
3464:   /**
3465:    * Draws a dashed horizontal line.
3466:    * 
3467:    * @param g -
3468:    *          the graphics configuration.
3469:    * @param y -
3470:    *          the y location to start drawing at
3471:    * @param x1 -
3472:    *          the x location to start drawing at
3473:    * @param x2 -
3474:    *          the x location to finish drawing at
3475:    */
3476:   protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2)
3477:   {
3478:     g.setColor(getHashColor());
3479:     for (int i = x1; i < x2; i += 2)
3480:       g.drawLine(i, y, i + 1, y);
3481:   }
3482: 
3483:   /**
3484:    * Draws a dashed vertical line.
3485:    * 
3486:    * @param g -
3487:    *          the graphics configuration.
3488:    * @param x -
3489:    *          the x location to start drawing at
3490:    * @param y1 -
3491:    *          the y location to start drawing at
3492:    * @param y2 -
3493:    *          the y location to finish drawing at
3494:    */
3495:   protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2)
3496:   {
3497:     g.setColor(getHashColor());
3498:     for (int i = y1; i < y2; i += 2)
3499:       g.drawLine(x, i, x, i + 1);
3500:   }
3501: 
3502:   /**
3503:    * Paints the expand (toggle) part of a row. The receiver should NOT modify
3504:    * clipBounds, or insets.
3505:    * 
3506:    * @param g -
3507:    *          the graphics configuration
3508:    * @param clipBounds -
3509:    * @param insets -
3510:    * @param bounds -
3511:    *          bounds of expand control
3512:    * @param path -
3513:    *          path to draw control for
3514:    * @param row -
3515:    *          row to draw control for
3516:    * @param isExpanded -
3517:    *          is the row expanded
3518:    * @param hasBeenExpanded -
3519:    *          has the row already been expanded
3520:    * @param isLeaf -
3521:    *          is the path a leaf
3522:    */
3523:   protected void paintExpandControl(Graphics g, Rectangle clipBounds,
3524:                                     Insets insets, Rectangle bounds,
3525:                                     TreePath path, int row, boolean isExpanded,
3526:                                     boolean hasBeenExpanded, boolean isLeaf)
3527:   {
3528:     if (shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf))
3529:       {
3530:         Icon icon = getCurrentControlIcon(path);
3531:         int iconW = icon.getIconWidth();
3532:         int x = bounds.x - rightChildIndent + iconW / 2;
3533:         if (x + iconW > bounds.x)
3534:           x = bounds.x - rightChildIndent - gap;
3535:         icon.paintIcon(tree, g, x, bounds.y + bounds.height / 2
3536:                                    - icon.getIconHeight() / 2);
3537:       }
3538:   }
3539: 
3540:   /**
3541:    * Paints the horizontal part of the leg. The receiver should NOT modify
3542:    * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not
3543:    * visible.
3544:    * 
3545:    * @param g -
3546:    *          the graphics configuration
3547:    * @param clipBounds -
3548:    * @param insets -
3549:    * @param bounds -
3550:    *          bounds of the cell
3551:    * @param path -
3552:    *          path to draw leg for
3553:    * @param row -
3554:    *          row to start drawing at
3555:    * @param isExpanded -
3556:    *          is the row expanded
3557:    * @param hasBeenExpanded -
3558:    *          has the row already been expanded
3559:    * @param isLeaf -
3560:    *          is the path a leaf
3561:    */
3562:   protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
3563:                                           Insets insets, Rectangle bounds,
3564:                                           TreePath path, int row,
3565:                                           boolean isExpanded,
3566:                                           boolean hasBeenExpanded,
3567:                                           boolean isLeaf)
3568:   {
3569:     if (row != 0)
3570:       paintHorizontalLine(g, tree, bounds.y + bounds.height / 2, bounds.x - gap
3571:                                                                  - 2, bounds.x);
3572:   }
3573: 
3574:   /**
3575:    * Paints the vertical part of the leg. The receiver should NOT modify
3576:    * clipBounds, insets.
3577:    * 
3578:    * @param g -
3579:    *          the graphics configuration.
3580:    * @param clipBounds -
3581:    * @param insets -
3582:    * @param path -
3583:    *          the path to draw the vertical part for.
3584:    */
3585:   protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
3586:                                         Insets insets, TreePath path)
3587:   {
3588:     int max = tree.getVisibleRowCount();
3589:     for (int i = 0; i < max; i++)
3590:       {
3591:         Object curr = path.getPathComponent(i);
3592:         TreePath currPath = new TreePath(getPathToRoot(curr, 0));
3593:         int numChild = treeModel.getChildCount(curr);
3594:         if (numChild > 0 && tree.isExpanded(currPath))
3595:           {
3596:             Rectangle bounds = getPathBounds(tree, currPath);
3597:             Rectangle lastChildBounds = getPathBounds(
3598:                                                       tree,
3599:                                                       new TreePath(
3600:                                                                    getPathToRoot(
3601:                                                                                  treeModel.getChild(
3602:                                                                                                     curr,
3603:                                                                                                     numChild - 1),
3604:                                                                                  0)));
3605:             paintVerticalLine(g, tree, bounds.x + gap + 2, bounds.y
3606:                                                            + bounds.height - 2,
3607:                               lastChildBounds.y + lastChildBounds.height / 2);
3608:           }
3609:       }
3610:   }
3611: 
3612:   /**
3613:    * Paints the renderer part of a row. The receiver should NOT modify
3614:    * clipBounds, or insets.
3615:    * 
3616:    * @param g -
3617:    *          the graphics configuration
3618:    * @param clipBounds -
3619:    * @param insets -
3620:    * @param bounds -
3621:    *          bounds of expand control
3622:    * @param path -
3623:    *          path to draw control for
3624:    * @param row -
3625:    *          row to draw control for
3626:    * @param isExpanded -
3627:    *          is the row expanded
3628:    * @param hasBeenExpanded -
3629:    *          has the row already been expanded
3630:    * @param isLeaf -
3631:    *          is the path a leaf
3632:    */
3633:   protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets,
3634:                           Rectangle bounds, TreePath path, int row,
3635:                           boolean isExpanded, boolean hasBeenExpanded,
3636:                           boolean isLeaf)
3637:   {
3638:     boolean selected = tree.isPathSelected(path);
3639:     boolean hasIcons = false;
3640:     Object node = path.getLastPathComponent();
3641: 
3642:     if (tree.isVisible(path))
3643:       {
3644:         if (!validCachedPreferredSize)
3645:           updateCachedPreferredSize();
3646: 
3647:         paintExpandControl(g, clipBounds, insets, bounds, path, row,
3648:                            isExpanded, hasBeenExpanded, isLeaf);
3649: 
3650:         if (row != 0)
3651:           bounds.x += gap;
3652:         bounds.width = preferredSize.width + bounds.x;
3653:         if (editingComponent != null && editingPath != null && isEditing(tree)
3654:             && node.equals(editingPath.getLastPathComponent()))
3655:           {
3656:             rendererPane.paintComponent(g, editingComponent.getParent(), null,
3657:                                         bounds);
3658:           }
3659:         else
3660:           {
3661:             TreeCellRenderer dtcr = tree.getCellRenderer();
3662:             if (dtcr == null)
3663:               dtcr = createDefaultCellRenderer();
3664: 
3665:             Component c = dtcr.getTreeCellRendererComponent(tree, node,
3666:                                                             selected,
3667:                                                             isExpanded, isLeaf,
3668:                                                             row,
3669:                                                             tree.hasFocus());
3670:             rendererPane.paintComponent(g, c, c.getParent(), bounds);
3671:           }
3672:       }
3673:   }
3674: 
3675:   /**
3676:    * Prepares for the UI to uninstall.
3677:    */
3678:   protected void prepareForUIUninstall()
3679:   {
3680:     // TODO: Implement this properly.
3681:   }
3682: 
3683:   /**
3684:    * Returns true if the expand (toggle) control should be drawn for the
3685:    * specified row.
3686:    * 
3687:    * @param path -
3688:    *          current path to check for.
3689:    * @param row -
3690:    *          current row to check for.
3691:    * @param isExpanded -
3692:    *          true if the path is expanded
3693:    * @param hasBeenExpanded -
3694:    *          true if the path has been expanded already
3695:    * @param isLeaf -
3696:    *          true if the row is a lead
3697:    */
3698:   protected boolean shouldPaintExpandControl(TreePath path, int row,
3699:                                              boolean isExpanded,
3700:                                              boolean hasBeenExpanded,
3701:                                              boolean isLeaf)
3702:   {
3703:     Object node = path.getLastPathComponent();
3704:     return (!isLeaf && getLevel(node) != 0 && hasControlIcons());
3705:   }
3706: 
3707:   /**
3708:    * Updates the cached current TreePath of all visible nodes in the tree.
3709:    */
3710:   void updateCurrentVisiblePath()
3711:   {
3712:     if (treeModel == null)
3713:       return;
3714: 
3715:     Object next = treeModel.getRoot();
3716:     if (next == null)
3717:       return;
3718: 
3719:     TreePath rootPath = new TreePath(next);
3720:     Rectangle bounds = getPathBounds(tree, rootPath);
3721: 
3722:     // If root is not a valid size to be visible, or is
3723:     // not visible and the tree is expanded, then the next node acts
3724:     // as the root
3725:     if ((bounds.width == 0 && bounds.height == 0)
3726:         || (!isRootVisible() && tree.isExpanded(new TreePath(next))))
3727:       {
3728:         next = getNextNode(next);
3729:         rootPath = new TreePath(next);
3730:       }
3731: 
3732:     Object root = next;
3733:     TreePath current = null;
3734:     while (next != null)
3735:       {
3736:         if (current == null)
3737:           current = rootPath;
3738:         else
3739:           current = current.pathByAddingChild(next);
3740: 
3741:         do
3742:           {
3743:             TreePath path = new TreePath(getPathToRoot(next, 0));
3744:             if ((tree.isVisible(path) && tree.isExpanded(path))
3745:                 || treeModel.isLeaf(next))
3746:               next = getNextNode(next);
3747:             else
3748:               {
3749:                 Object pNext = next;
3750:                 next = getNextSibling(pNext);
3751:                 // if no next sibling, check parent's next sibling.
3752:                 if (next == null)
3753:                   {
3754:                     Object parent = getParent(root, pNext);
3755:                     while (next == null && parent != null)
3756:                       {
3757:                         next = getNextSibling(parent);
3758:                         if (next == null)
3759:                           parent = getParent(root, parent);
3760:                       }
3761:                   }
3762:               }
3763:           }
3764:         while (next != null
3765:                && !tree.isVisible(new TreePath(getPathToRoot(next, 0))));
3766:       }
3767: 
3768:     currentVisiblePath = current;
3769:     tree.setVisibleRowCount(getRowCount(tree));
3770: 
3771:     if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0
3772:         && currentVisiblePath != null)
3773:       selectPath(
3774:                  tree,
3775:                  new TreePath(
3776:                               getPathToRoot(
3777:                                             currentVisiblePath.getPathComponent(0),
3778:                                             0)));
3779:   }
3780: 
3781:   /**
3782:    * Get next visible node in the currentVisiblePath. Package private for use in
3783:    * inner classes.
3784:    * 
3785:    * @param node
3786:    *          current node
3787:    * @return the next visible node in the JTree. Return null if there are no
3788:    *         more.
3789:    */
3790:   Object getNextVisibleNode(Object node)
3791:   {
3792:     if (currentVisiblePath != null)
3793:       {
3794:         Object[] nodes = currentVisiblePath.getPath();
3795:         int i = 0;
3796:         while (i < nodes.length && !node.equals(nodes[i]))
3797:           i++;
3798:         // return the next node
3799:         if (i + 1 < nodes.length)
3800:           return nodes[i + 1];
3801:       }
3802:     return null;
3803:   }
3804: } // BasicTreeUI