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