home *** CD-ROM | disk | FTP | other *** search
/ CD Ware Multimedia 1999 February / CDW0299.iso / Demos / Cafe / Source.bin / TreeView.java < prev    next >
Encoding:
Java Source  |  1998-03-18  |  63.6 KB  |  2,283 lines

  1. package symantec.itools.awt;
  2.  
  3.  
  4. import java.awt.BorderLayout;
  5. import java.awt.Color;
  6. import java.awt.SystemColor;
  7. import java.awt.Dimension;
  8. import java.awt.Event;
  9. import java.awt.FontMetrics;
  10. import java.awt.Font;
  11. import java.awt.Graphics;
  12. import java.awt.Image;
  13. import java.awt.LayoutManager;
  14. import java.awt.Scrollbar;
  15. import java.awt.Panel;
  16. import java.awt.Rectangle;
  17. import java.awt.ItemSelectable;
  18. import java.awt.AWTEventMulticaster;
  19. import java.awt.event.ItemEvent;
  20. import java.awt.event.ItemListener;
  21. import java.awt.event.KeyEvent;
  22. import java.awt.event.KeyAdapter;
  23. import java.awt.event.MouseEvent;
  24. import java.awt.event.MouseAdapter;
  25. import java.awt.event.FocusEvent;
  26. import java.awt.event.FocusAdapter;
  27. import java.awt.event.AdjustmentEvent;
  28. import java.awt.event.AdjustmentListener;
  29. import java.awt.event.ActionEvent;
  30. import java.awt.event.ActionListener;
  31. import java.util.Vector;
  32. import java.beans.PropertyVetoException;
  33. import java.beans.PropertyChangeListener;
  34. import java.beans.VetoableChangeListener;
  35. import java.beans.PropertyChangeEvent;
  36. import java.util.ResourceBundle;
  37.  
  38.  
  39. //    01/15/97    RKM    Changed drawTree to make certain g1 has a font, before calling getFontMetrics on it
  40. //    01/15/07    RKM    Added invalidate to setTreeStructure
  41. //    01/29/97    TWB    Integrated changes from Windows and RKM's changes
  42. //     01/29/97    TWB    Integrated changes from Macintosh
  43. //  02/05/97    MSH Changed so that draws from first visible node
  44. //  02/27/97    MSH Merged change to add SEL_CHANGED
  45. //  04/02/97    TNM Draw all vertical lines
  46. //  04/14/97    RKM Changed bogus invalidates to repaint
  47. //                RKM    Changed hard coded sbVWidth to use preferredSize.width
  48. //                RKM Changed getTreeStructure so it actually returned a representation of what was in the treeview
  49. //                RKM    Rearranged a lot of stuff to get this to work
  50. //                RKM Changed g1.drawRect in drawTree to not overlap the scrollbar
  51. //                RKM Changed parseTreeStructure to not force a root node
  52. //  05/02/97    RKM Add arg to addSibling so caller could control whether the sible was added as the last sibling or not
  53. //                RKM    Changed insert to call addSibling with false when handling NEXT
  54. //                RKM    Kept addSibling with two params for compatibility
  55. //    05/31/97    RKM    Updated to support Java 1.1
  56. //                    Made properties bound & constrained
  57. //                    Removed get/setBackground & get/setForeground overrides, getter did nothing but call the super
  58. //                    and setters were calling repaint, no one else does this
  59. //                    Deprecated foreground and background hilite colors, used system colors instead
  60. //                    Deprecated SEL_CHANGED, replaced by ItemSelectable interface
  61. //                    Hid scrollbar on creation, to avoid ugly redraw problems
  62. //                    Changed to triggered redraw
  63. //                    NOTE: SystemColor seems to be broken on Mac
  64. //  06/01/97    RKM    Changed symantec.beans references to java.beans
  65. //  05/13/97    TNM Added horizontal scrollbar
  66. //  05/15/97    TNM Check for vendor to corect scrollbar problem
  67. //  06/09/97    CAR Modified check for vendor to include java.version 1.1.x
  68. //  06/19/97    TNM Merging changes
  69. //  07/25/97    CAR marked fields transient as needed will have to re-test after event handling code is changed
  70. //  08/14/97    RKM Changed hiliteColor to be consistent when on Mac
  71. //  08/20/97    LAB Changed protection of triggerRedraw to public from protected to allow the node
  72. //                    to trigger redraws.  Addresses Mac Bug #4372.  Changed privates to protecteds.
  73. //                    Reorganized the code in correspondence to the GoodBean Spec.  Deleted null
  74. //                    methods that had been deprecated.  Made some package level functions public.
  75. //                    Separated the internals of event hadling into protected handle<event> methods
  76. //                    to facilitate overriding.  Updated the InvalidTreeNodeException class to take
  77. //                    a message string for more detailed exceptions.  Fixed findLastPreSpace to handle
  78. //                    being passed null strings, or strings with no lenght (Addresses Mac Bug #4005).
  79. //                    Added clear method (Addresses Mac Bug #7369).  Fixed so selection is only set
  80. //                    if a single or double click occurs on a node, or if selected node was made
  81. //                    invisible by node collapse.  deprecated preferredSize and minimumSize in favor
  82. //                    of getPreferredSize and getMiniumumSize.  Made getPreferredSize calculate
  83. //                    the size TreeView should be based on the parts of the TreeView that are visible.
  84. //                    Added newTreeNode function to allow interception of the creation of nodes (Addresses
  85. //                    Win Bug #4174).  Took out isDesignTime code in paint that caused looping paints
  86. //                    (i.e. flickering) at design time.  Added paintTree(TreeNode, boolean) that
  87. //                    allows tree to be output with indenting (Win Bug #13095).  Fixed append to
  88. //                    make sure the node to append didn't already exist in the tree (Win Bug #13050).
  89. //                    Updated javadoc on append, insert, remove, removeSelected, etc. (Win Bug #12663).
  90. //  08/22/97    LAB    Call resetVector() before sending Action events so internal state is reset
  91. //                    (Addresses Win Bug #12666).  redraw now only calls resetVector if needed.
  92. //  08/26/97    CAR added null and zero length parameter checks to setTreeStructure
  93. //  08/28/97    CAR fixed bug re: horizontal scrolling not working
  94. //  08/28/97    RKM Added isFocusTraversable override (Yep, you can tab to this one)
  95. //  08/29/97    CAR modified getPreferredSize and getMinimumSize
  96. //  09/24/97    RKM Properly set isSun1_1 for Apple MRJ 2.0
  97. //                    Set isSun1_1 same as MutliList did (changed to static)
  98. //  10/04/97    LAB    Added ItemEvent firing when a node is expaned or collapsed.  Added
  99. //                    NODE_TOGGLED, NODE_EXPANDED, and NODE_COLLAPSED constants to support this.
  100. //  12/09/97    DS  Added check for a null itemListener in handleMousePressed
  101. //  12/19/97    DS  Added more checks for a null itemListener in handleMousePressed
  102. //  02/09/98    DS  Added support for hiding/showing nodes (TreeNode.setHidden(boolean)
  103. //                  Added support for deslecting a node (setSelectedNode(Tree Node)
  104.  
  105.  
  106. /**
  107.  * Creates an "outline view" of text headings and, optionally, images.
  108.  * The headings are arranged in a hierarchical fashion, and can be
  109.  * expanded to show their sub-headings or collapsed, hiding their
  110.  * sub-headings.
  111.  * <p>
  112.  * A TreeView is typically used to display information that is organized in a
  113.  * hierarchical fashion like an index or table of contents.
  114.  * <p>
  115.  * A TreeNode object is used for each heading.
  116.  * @see TreeNode
  117.  */
  118. public class TreeView extends Panel implements ItemSelectable
  119. {
  120.     // constants for insertion
  121.     /**
  122.      * Constant indicating that the new node is to be a child
  123.      * of the existing node.
  124.      * @see #insert
  125.      */
  126.     public static final int CHILD   = 0;
  127.     /**
  128.      * Constant indicating that the new node is to be the next
  129.      * sibling of the existing node.
  130.      * @see #insert
  131.      */
  132.     public static final int NEXT    = CHILD + 1;
  133.     /**
  134.      * Constant indicating that the new node is to be the last
  135.      * sibling of the existing node.
  136.      * @see #insert
  137.      */
  138.     public static final int LAST    = CHILD + 2;
  139.     /**
  140.      * Constand used to describe ItemEvents sent when a node is toggled.
  141.      * @see #NODE_EXPANDED
  142.      * @see #NODE_COLLAPSED
  143.      */
  144.     public static final int NODE_TOGGLED = 2001;
  145.     /**
  146.      * Constand used to detail that the node referenced in a NODE_TOGGLED ItemEvent has been expanded.
  147.      * @see #NODE_TOGGLED
  148.      * @see #NODE_COLLAPSED
  149.      */
  150.     public static final int NODE_EXPANDED = 2002;
  151.     /**
  152.      * Constand used to detail that the node referenced in a NODE_TOGGLED ItemEvent has been collapsed.
  153.      * @see #NODE_TOGGLED
  154.      * @see #NODE_EXPANDED
  155.      */
  156.     public static final int NODE_COLLAPSED = 2003;
  157.     /**
  158.      * @deprecated As of JDK version 1.1,
  159.      * replaced by ItemSelectable interface.
  160.      * @see java.awt.ItemSelectable
  161.      */
  162.     public static final int SEL_CHANGED = 1006; //selection changed event
  163.  
  164.     //
  165.     // Constructors
  166.     //
  167.  
  168.     /**
  169.      * Constructs an empty TreeView.
  170.      */
  171.     public TreeView()
  172.     {
  173.         super.setLayout(null);
  174.  
  175.         verticalScrollBar = new Scrollbar(Scrollbar.VERTICAL);
  176.         verticalScrollBar.hide();
  177.         add(verticalScrollBar);
  178.  
  179.         horizontalScrollBar = new Scrollbar(Scrollbar.HORIZONTAL);
  180.         horizontalScrollBar.hide();
  181.         add(horizontalScrollBar);
  182.  
  183.         needResetVector = true;
  184.     }
  185.  
  186.     /**
  187.      * Constructs a TreeView with the given node.
  188.      *
  189.      * @param head the root node of the constructed tree
  190.      */
  191.     public TreeView(TreeNode head)
  192.     {
  193.         this();
  194.         selectedNode = rootNode = head;
  195.         count = 1;
  196.     }
  197.  
  198.     //
  199.     // Properties
  200.     //
  201.  
  202.     /**
  203.      * Initializes the TreeView from a string array.
  204.      * There is one string for each node in the array. That string
  205.      * contains the text of the node indented with same number of
  206.      * leading spaces as the depth of that node in the tree.
  207.      * @param s the string array used for initialization.
  208.      * If null, the tree will be cleared.
  209.      * @see #getTreeStructure
  210.      */
  211.     public void setTreeStructure(String s[])
  212.     {
  213.         if (s == null || s.length == 0)
  214.         {
  215.             clear();
  216.             return;
  217.         }
  218.  
  219.         rootNode = selectedNode = null;
  220.         try
  221.         {
  222.             parseTreeStructure(s);
  223.         }
  224.         catch(InvalidTreeNodeException e)
  225.         {
  226.             System.err.println(e);
  227.         }
  228.  
  229.         triggerRedraw();
  230.  
  231.         invalidate();
  232.     }
  233.  
  234.     /**
  235.      * Gets a string array that reflects the current TreeView's contents.
  236.      * There is one string for each node in the array. That string
  237.      * contains the text of the node indented with same number of
  238.      * leading spaces as the depth of that node in the tree.
  239.      * @return the string array that reflects the TreeView's contents
  240.      * @see #setTreeStructure
  241.      */
  242.     public String[] getTreeStructure()
  243.     {
  244.         //Create a vector representing current tree structure
  245.         if (rootNode==null) return null;
  246.         Vector nodesVector = new Vector(count);
  247.         rootNode.depth = 0;
  248.         vectorize(rootNode, false, false, nodesVector);
  249.  
  250.         //Convert this to a String[]
  251.         int numNodes = nodesVector.size();
  252.         String[] treeStructure = new String[numNodes];
  253.         for (int i = 0;i < numNodes;i++)
  254.         {
  255.             TreeNode thisNode = (TreeNode)nodesVector.elementAt(i);
  256.  
  257.             //Add appropriate number of blanks
  258.             String treeString = "";
  259.             for (int numBlanks = 0;numBlanks < thisNode.depth;numBlanks++)
  260.                 treeString += ' ';
  261.  
  262.             //Add tree
  263.             treeString += thisNode.text;
  264.  
  265.             //Put string into array
  266.             treeStructure[i] = treeString;
  267.         }
  268.  
  269.         return treeStructure;
  270.     }
  271.  
  272.     //
  273.     // Deprecated Properties
  274.     //
  275.  
  276.     /**
  277.      * @deprecated As of JDK version 1.1,
  278.      * replaced by use of SystemColors.textHighlightText.
  279.      */
  280.     public Color getFgHilite()
  281.     {
  282.         return SystemColor.textHighlightText;
  283.     }
  284.  
  285.     /**
  286.      * @deprecated As of JDK version 1.1,
  287.      * replaced by use of SystemColors.textHighlight.
  288.      */
  289.     public Color getBgHilite()
  290.     {
  291.         return SystemColor.textHighlight;
  292.     }
  293.  
  294.     //
  295.     // ItemSelectable interface
  296.     //
  297.  
  298.     /**
  299.      * Returns the selected items or null if no items are selected.
  300.      * <p>
  301.      * This is a standard method of the ItemSelectable interface.
  302.      */
  303.     public Object[] getSelectedObjects()
  304.     {
  305.         if (selectedNode == null)
  306.             return null;
  307.  
  308.         TreeNode[] selectedObjects = new TreeNode[1];
  309.         selectedObjects[0] = selectedNode;
  310.  
  311.         return selectedObjects;
  312.     }
  313.  
  314.     //
  315.     // Methods
  316.     //
  317.  
  318.     // Insert a new node relative to a node in the tree.
  319.     // position = CHILD inserts the new node as a child of the node
  320.     // position = NEXT inserts the new node as the next sibling
  321.     // position = LAST inserts the new node as the last sibling
  322.     /**
  323.      * Inserts a new node relative to an existing node in the tree.
  324.      * @param newNode the new node to insert into the tree
  325.      * @param relativeNode the existing node used for a position reference
  326.      * @param position where to insert the new node relative to relativeNode.
  327.      * Legal values are CHILD, NEXT and LAST.
  328.      * @see #CHILD
  329.      * @see #NEXT
  330.      * @see #LAST
  331.      * @see #append
  332.     */
  333.     public void insert(TreeNode newNode, TreeNode relativeNode, int position)
  334.     {
  335.         if (newNode == null || relativeNode == null)
  336.             return;
  337.  
  338.         if (exists(relativeNode)==false)
  339.             return;
  340.  
  341.         switch (position)
  342.         {
  343.             case CHILD:
  344.                 addChild(newNode, relativeNode);
  345.                 break;
  346.  
  347.             case NEXT:
  348.                 addSibling(newNode, relativeNode, false);
  349.                 break;
  350.  
  351.             case LAST:
  352.                 addSibling(newNode, relativeNode, true);
  353.                 break;
  354.  
  355.             default:
  356.                 // invalid position
  357.                 return;
  358.         }
  359.     }
  360.  
  361.     /**
  362.      * Clears the tree structure and redraws.
  363.      */
  364.     public void clear()
  365.     {
  366.         rootNode = selectedNode = null;
  367.         count = 0;
  368.         v = new Vector();
  369.         e = new Vector();
  370.         triggerRedraw();
  371.  
  372.         invalidate();
  373.     }
  374.  
  375.     /**
  376.      * Returns the "root" node.
  377.      * The root node is the first top-level node in the tree hierarchy.
  378.      * All other nodes are either children or siblings of that one.
  379.      * @return the root tree node
  380.      */
  381.     public TreeNode getRootNode()
  382.     {
  383.         return rootNode;
  384.     }
  385.  
  386.     /**
  387.      * Returns the total number of nodes in the tree.
  388.      */
  389.     public int getCount()
  390.     {
  391.         return count;
  392.     }
  393.  
  394.     /**
  395.      * Returns the total number of viewable nodes in the tree.
  396.      * A node is viewable if all of its parents are expanded.
  397.      */
  398.     public int getViewCount()
  399.     {
  400.         return viewCount;
  401.     }
  402.  
  403.     /**
  404.      * Determines if the given node is viewable.
  405.      * A node is viewable if all of its parents are expanded.
  406.      * @param node the node to check
  407.      * @return true if the node is visible, false if it is not
  408.      * @see #viewable(java.lang.String)
  409.      */
  410.     boolean viewable(TreeNode node)
  411.     {
  412.         for (int i=0; i<viewCount; i++)
  413.         {
  414.             if (node == v.elementAt(i))
  415.             {
  416.                 return true;
  417.             }
  418.         }
  419.  
  420.         return false;
  421.     }
  422.  
  423.     /**
  424.      * Determines if the node with the given text is viewable.
  425.      * A node is viewable if all of its parents are expanded.
  426.      * @param s the node text to find
  427.      * @return true if the node is visible, false if it is not
  428.      * @see #viewable(TreeNode)
  429.      */
  430.     boolean viewable(String s)
  431.     {
  432.         if (s==null)
  433.         {
  434.             return false;
  435.         }
  436.  
  437.         for (int i=0; i<viewCount; i++)
  438.         {
  439.             TreeNode tn = (TreeNode)v.elementAt(i);
  440.  
  441.             if (tn.text != null)
  442.             {
  443.                 if (s.equals(tn.text))
  444.                 {
  445.                     return true;
  446.                 }
  447.             }
  448.         }
  449.  
  450.         return false;
  451.     }
  452.  
  453.     /**
  454.      * Determines if the given node is in the TreeView.
  455.      * @param node the node to check
  456.      * @return true if the node is in the TreeView, false if it is not
  457.      * @see #exists(java.lang.String)
  458.      */
  459.     public boolean exists(TreeNode node)
  460.     {
  461.         recount();
  462.  
  463.         for (int i=0; i<count; i++)
  464.         {
  465.             if (node == e.elementAt(i))
  466.             {
  467.                 return true;
  468.             }
  469.         }
  470.  
  471.         return false;
  472.     }
  473.  
  474.     /**
  475.      * Determines if the node with the given text is in the TreeView.
  476.      * @param s the node text to find
  477.      * @return true if the node is in the TreeView, false if it is not
  478.      * @see #exists(symantec.itools.awt.TreeNode)
  479.      */
  480.     public boolean exists(String s)
  481.     {
  482.         recount();
  483.  
  484.         if (s==null)
  485.         {
  486.             return false;
  487.         }
  488.  
  489.         for (int i=0; i<count; i++)
  490.         {
  491.             TreeNode tn = (TreeNode)e.elementAt(i);
  492.  
  493.             if (tn.text != null)
  494.             {
  495.                 if (s.equals(tn.text))
  496.                 {
  497.                     return true;
  498.                 }
  499.             }
  500.         }
  501.  
  502.         return false;
  503.     }
  504.  
  505.     /**
  506.      * Adds a new node at root level. If there is no root node, the given
  507.      * node is made the root node. If there is a root node, the given node
  508.      * is made a sibling of the root node.
  509.      * Does not redraw the component. This allows you to call repeatedly without
  510.      * causing the component to flicker while nodes are manipulated.  After all
  511.      * manipulation is done, repaint the tree.
  512.      * @param newNode the new node to add
  513.      * @see #insert
  514.      */
  515.     public void append(TreeNode newNode)
  516.     {
  517.         if (rootNode == null)
  518.         {
  519.             rootNode = newNode;
  520.             selectedNode = rootNode;
  521.             count = 1;
  522.             redrawTriggered = true;
  523.         }
  524.         else
  525.         {
  526.             recount();
  527.             if (e.contains(newNode))
  528.                 System.err.println(new InvalidTreeNodeException("append: " + errors.getString("NodeAlreadyExists")));
  529.             else
  530.                 addSibling(newNode, rootNode, true);
  531.         }
  532.     }
  533.  
  534.     /**
  535.      * Adds the specified child node to the specified parent node
  536.      * Does not redraw the component. This allows you to call repeatedly without
  537.      * causing the component to flicker while nodes are manipulated.  After all
  538.      * manipulation is done, repaint the tree.
  539.      * @param newNode the node to add as a child
  540.      * @param relativeNode the node to add the child to.
  541.      */
  542.     public void addChild(TreeNode newNode, TreeNode relativeNode)
  543.     {
  544.         if (relativeNode.child == null)
  545.         {
  546.             relativeNode.child = newNode;
  547.             newNode.parent = relativeNode;
  548.             count++;
  549.             redrawTriggered = true;
  550.         }
  551.         else
  552.         {
  553.             addSibling(newNode, relativeNode.child, true);
  554.         }
  555.  
  556.         relativeNode.numberOfChildren++;
  557.     }
  558.  
  559.     /**
  560.      * Adds the specified node as a sibling to the specified node
  561.      * Does not redraw the component. This allows you to call repeatedly without
  562.      * causing the component to flicker while nodes are manipulated.  After all
  563.      * manipulation is done, repaint the tree.
  564.      * @param newNode the node to add as a sibling
  565.      * @param siblingNode a sibling node to the new node.
  566.      * @see #addSibling(symantec.itools.awt.TreeNode, symantec.itools.awt.TreeNode, boolean)
  567.      */
  568.     public void addSibling(TreeNode newNode, TreeNode siblingNode)
  569.     {
  570.         addSibling(newNode,siblingNode,true);
  571.     }
  572.  
  573.     /**
  574.      * Adds the specified node as a sibling to the specified node
  575.      * Does not redraw the component. This allows you to call repeatedly without
  576.      * causing the component to flicker while nodes are manipulated.  After all
  577.      * manipulation is done, repaint the tree.
  578.      * @param newNode the node to add as a sibling
  579.      * @param siblingNode a sibling node to the new node.
  580.      * @param asLastSibling if true, then add the new node as the last (bottommost)
  581.      * sibling node to the specified sibling node.
  582.      * @see #addSibling(symantec.itools.awt.TreeNode, symantec.itools.awt.TreeNode)
  583.      */
  584.     public void addSibling(TreeNode newNode, TreeNode siblingNode, boolean asLastSibling)
  585.     {
  586.         if (asLastSibling)
  587.         {
  588.             //Find last sibling
  589.             TreeNode tempNode = siblingNode;
  590.             while (tempNode.sibling != null)
  591.                 tempNode = tempNode.sibling;
  592.  
  593.             tempNode.sibling = newNode;
  594.         }
  595.         else
  596.         {
  597.             //Insert the newNode below the siblingNode
  598.             newNode.sibling = siblingNode.sibling;
  599.  
  600.             siblingNode.sibling = newNode;
  601.         }
  602.  
  603.         //Set the parent of the new node to the parent of the sibling
  604.         newNode.parent = siblingNode.parent;
  605.  
  606.         count++;
  607.         redrawTriggered = true;
  608.     }
  609.  
  610.     /**
  611.      * Removes the node with the given text from the TreeView.
  612.      * Does not redraw the component. This allows you to call repeatedly without
  613.      * causing the component to flicker while nodes are manipulated.  After all
  614.      * manipulation is done, repaint the tree.
  615.      * @param s the node text to find
  616.      * @return the TreeNode removed from this TreeView or null if not found
  617.      * @see #remove(symantec.itools.awt.TreeNode)
  618.      * @see #removeSelected
  619.      */
  620.     public TreeNode remove(String s)
  621.     {
  622.         recount();
  623.  
  624.         for (int i=0; i<count; i++)
  625.         {
  626.             TreeNode tn = (TreeNode)e.elementAt(i);
  627.  
  628.             if (tn.text != null)
  629.             {
  630.                 if (s.equals(tn.text))
  631.                 {
  632.                     remove(tn);
  633.                     redrawTriggered = true;
  634.                     return tn;
  635.                 }
  636.             }
  637.         }
  638.  
  639.         return null;
  640.     }
  641.  
  642.     /**
  643.      * Removes the currently selected node from the TreeView.
  644.      * Does not redraw the component. This allows you to call repeatedly without
  645.      * causing the component to flicker while nodes are manipulated.  After all
  646.      * manipulation is done, repaint the tree.
  647.      * @see #remove(symantec.itools.awt.TreeNode)
  648.      * @see #remove(java.lang.String)
  649.      */
  650.     public void removeSelected()
  651.     {
  652.         if (selectedNode != null)
  653.         {
  654.             remove(selectedNode);
  655.         }
  656.     }
  657.  
  658.     /**
  659.      * Removes the given node from the TreeView.
  660.      * Does not redraw the component. This allows you to call repeatedly without
  661.      * causing the component to flicker while nodes are manipulated.  After all
  662.      * manipulation is done, repaint the tree.
  663.      * @param node the node to remove
  664.      * @return the TreeNode removed from this TreeView or null if not found
  665.      * @see #remove(java.lang.String)
  666.      * @see #removeSelected
  667.      */
  668.     public void remove(TreeNode node)
  669.     {
  670.         if (!exists(node))
  671.         {
  672.             return;
  673.         }
  674.  
  675.         if (node == selectedNode)
  676.         {
  677.             int index = v.indexOf(selectedNode);
  678.  
  679.             if (index == -1)
  680.             {    //not viewable
  681.                 index = e.indexOf(selectedNode);
  682.             }
  683.  
  684.             if (index > viewCount-1)
  685.             {
  686.                 index = viewCount-1;
  687.             }
  688.  
  689.             if (index>0)
  690.             {
  691.                 setSelectedNode((TreeNode)v.elementAt(index-1));
  692.             }
  693.             else if (viewCount>1)
  694.             {
  695.                 setSelectedNode((TreeNode)v.elementAt(1));
  696.             }
  697.         }
  698.  
  699.         // remove node and its decendents
  700.         if (node.parent != null)
  701.         {
  702.             if (node.parent.child == node)
  703.             {
  704.                 if (node.sibling != null)
  705.                 {
  706.                     node.parent.child = node.sibling;
  707.                 }
  708.                 else
  709.                 {
  710.                     node.parent.child = null;
  711.                     node.parent.collapse();
  712.                 }
  713.             }
  714.             else
  715.             {
  716.                 TreeNode tn=node.parent.child;
  717.  
  718.                 while (tn.sibling != node)
  719.                 {
  720.                     tn = tn.sibling;
  721.                 }
  722.  
  723.                 if (node.sibling != null)
  724.                 {
  725.                     tn.sibling = node.sibling;
  726.                 }
  727.                 else
  728.                 {
  729.                     tn.sibling = null;
  730.                 }
  731.             }
  732.         }
  733.         else
  734.         {
  735.             if (node == rootNode)
  736.             {
  737.                 if (node.sibling == null)
  738.                 {
  739.                     rootNode=null;
  740.                 }
  741.                 else
  742.                 {
  743.                     rootNode=node.sibling;
  744.                 }
  745.             }
  746.             else
  747.             {
  748.                 TreeNode tn = rootNode;
  749.  
  750.                 while (tn.sibling != node)
  751.                 {
  752.                     tn = tn.sibling;
  753.                 }
  754.  
  755.                 if (node.sibling != null)
  756.                 {
  757.                     tn.sibling = node.sibling;
  758.                 }
  759.                 else
  760.                 {
  761.                     tn.sibling = null;
  762.                 }
  763.             }
  764.         }
  765.  
  766.         recount();
  767.         redrawTriggered = true;
  768.     }
  769.  
  770.     /**
  771.      * Print out the text of each node in the TreeView beginning with
  772.      * the given node.
  773.      * The nodes are printed out one per line with no indenting.
  774.      * @param node the first node to print
  775.      */
  776.     public void printTree(TreeNode node)
  777.     {
  778.         printTree(node, false);
  779.     }
  780.  
  781.     /**
  782.      * Print out the text of each node in the TreeView beginning with
  783.      * the given node.
  784.      * @param node the first node to print
  785.      * @param isIndented, if true, nodes will have hierarchical indenting,
  786.      * if false, all nodes will pe printed at the same level
  787.      */
  788.     public void printTree(TreeNode node, boolean isIndented)
  789.     {
  790.         if (node == null)
  791.         {
  792.             return;
  793.         }
  794.         String padding = new String();
  795.         if (isIndented)
  796.         {
  797.             for (int i = 0; i < node.depth; i++)
  798.                 padding = "  " + padding;
  799.         }
  800.  
  801.         System.out.println(padding + node.text);
  802.         printTree(node.child, isIndented);
  803.         printTree(node.sibling, isIndented);
  804.     }
  805.  
  806.     /**
  807.      * Gets the currently selected node.
  808.      * @return the currently selected node, or null if none selected
  809.      */
  810.     public TreeNode getSelectedNode()
  811.     {
  812.         return selectedNode;
  813.     }
  814.  
  815.     /**
  816.      * Gets the text of the currently selected node.
  817.      * @return the text of the currently selected node or null if no node
  818.      * is selected
  819.      */
  820.     public String getSelectedText()
  821.     {
  822.         if (selectedNode == null)
  823.             return null;
  824.  
  825.         return selectedNode.getText();
  826.     }
  827.  
  828.     // -----------------------------------------
  829.     // --------- graphics related methods ------
  830.     // -----------------------------------------
  831.     /**
  832.      * Handles redrawing of this component on the screen.
  833.      * This is a standard Java AWT method which gets called by the Java
  834.      * AWT (repaint()) to handle repainting this component on the screen.
  835.      * The graphics context clipping region is set to the bounding rectangle
  836.      * of this component and its [0,0] coordinate is this component's
  837.      * top-left corner.
  838.      * Typically this method paints the background color to clear the
  839.      * component's drawing space, sets graphics context to be the foreground
  840.      * color, and then calls paint() to draw the component.
  841.      *
  842.      * It is overridden here to reduce flicker by eliminating the uneeded
  843.      * clearing of the background.
  844.      *
  845.      * @param g the graphics context
  846.      * @see java.awt.Component#repaint
  847.      * @see #paint
  848.      */
  849.     public void update (Graphics g)
  850.     {
  851.         //(eliminates background draw to reduce flicker)
  852.         paint(g);
  853.     }
  854.  
  855.     /**
  856.      * Paints this component using the given graphics context.
  857.      * This is a standard Java AWT method which typically gets called
  858.      * by the AWT to handle painting this component. It paints this component
  859.      * using the given graphics context. The graphics context clipping region
  860.      * is set to the bounding rectangle of this component and its [0,0]
  861.      * coordinate is this component's top-left corner.
  862.      *
  863.      * @param g the graphics context used for painting
  864.      * @see java.awt.Component#repaint
  865.      * @see #update
  866.      */
  867.     public void paint (Graphics g)
  868.     {
  869.         Dimension d = size();
  870.  
  871.         if (redrawTriggered || (d.width != viewWidth) || (d.height != viewHeight))
  872.         {
  873.             // redraw needed, or size has changed
  874.             redraw(g);
  875.         }
  876.  
  877.         g.translate(-sbHPosition, 0);
  878.         g.clearRect(sbHPosition,0,d.width-sbVWidth,d.height-sbHHeight);
  879.         if (sbVShow && sbHShow)
  880.         {
  881.             g.setColor(Color.lightGray);
  882.             g.fillRect(sbHPosition+d.width-sbVWidth, d.height-sbHHeight, sbVWidth, sbHHeight);
  883.         }
  884.         g.clipRect(sbHPosition,0,d.width-sbVWidth,d.height-sbHHeight);
  885.         g.drawImage(im1, 0, 0, this);
  886.         g.setColor(Color.black);
  887.         g.drawRect(sbHPosition,0, d.width-sbVWidth-1, d.height-sbHHeight-1);
  888.  
  889.     }
  890.  
  891.     /**
  892.      * Lays out the vertical scrollbar as needed, then draws the TreeView into
  893.      * an offscreen image. This is used for cleaner refresh.
  894.      */
  895.     public void redraw()
  896.     {
  897.         //For backward compatibality. Do not allow to call only redraw() without recalculation.
  898.         triggerRedraw();
  899.     }
  900.  
  901.     /**
  902.      * Repaints this component.
  903.      * @param f <code>true</code>to re-vectorize if the node arrangement has changed
  904.      */
  905.     public void repaint(boolean f)
  906.     {
  907.         if(f)
  908.         {
  909.             needResetVector = true;
  910.         }
  911.  
  912.         triggerRedraw();
  913.     }
  914.  
  915.     /**
  916.      * Lays out the vertical scrollbar as needed, then draws the TreeView into
  917.      * an offscreen image. This is used for cleaner refresh.
  918.      * @param g the graphics object use for drawing
  919.      */
  920.     public void redraw(Graphics g)
  921.     {
  922.         Dimension d = size();
  923.  
  924.         redrawTriggered = false;
  925.  
  926.         if(needResetVector)
  927.             resetVector();
  928.         else
  929.             needResetVector = true;
  930.  
  931.         newWidth = compWidth(g);
  932.  
  933.         int inRectCount = ((d.height - sbHHeight) / cellSize);
  934.  
  935.         if (viewCount > inRectCount)
  936.         {
  937.               // need the vertical scrollbar
  938.             sbVShow  = true;
  939.             sbVWidth = verticalScrollBar.preferredSize().width;
  940.         }
  941.         else
  942.         {
  943.               sbVShow     = false;
  944.             sbVWidth    = 0;
  945.             sbVPosition = 0;
  946.         }
  947.  
  948.         if (newWidth > (d.width - sbVWidth))
  949.         {
  950.             // need the horizontal scrollbar
  951.             sbHShow = true;
  952.             sbHHeight = horizontalScrollBar.preferredSize().height;
  953.         }
  954.         else
  955.         {
  956.             sbHShow     = false;
  957.             sbHHeight   = 0;
  958.             sbHPosition = 0;
  959.         }
  960.  
  961.         drawTree();
  962.  
  963.         if (sbVShow)
  964.         {
  965.             verticalScrollBar.reshape(d.width-sbVWidth,0,sbVWidth,d.height-sbHHeight);
  966.             verticalScrollBar.setValues(sbVPosition, inRectCount, 0, viewCount-(isSun1_1?0:inRectCount));
  967.             verticalScrollBar.setPageIncrement(inRectCount-1);
  968.             verticalScrollBar.show();
  969.         }
  970.         else
  971.         {
  972.             verticalScrollBar.hide();
  973.         }
  974.  
  975.         if (sbHShow)
  976.         {
  977.             horizontalScrollBar.reshape(0,d.height-sbHHeight,d.width-sbVWidth,sbHHeight);
  978.             horizontalScrollBar.setValues(sbHPosition, d.width-sbVWidth, 0, sbHSize-(isSun1_1?0:(d.width-sbVWidth)));
  979.             horizontalScrollBar.setPageIncrement(d.width-sbVWidth);
  980.             horizontalScrollBar.setLineIncrement(sbHLineIncrement);
  981.             horizontalScrollBar.show();
  982.         }
  983.         else
  984.         {
  985.             horizontalScrollBar.hide();
  986.         }
  987.     }
  988.  
  989.     /**
  990.      * Draws the TreeView into an offscreen image. This is used for cleaner refresh.
  991.      */
  992.     public void drawTree()
  993.     {
  994.         Dimension d = size();
  995.  
  996.         if(needResetVector)
  997.             resetVector();
  998.  
  999.         if ((d.width != viewWidth) || (d.height != viewHeight) || (g1 == null) || (sbHSize != newWidth))
  1000.         {
  1001.             // size has changed, must resize image
  1002.             im1 = createImage(Math.max(sbHSize=newWidth, d.width), d.height);
  1003.  
  1004.             if (g1 != null)
  1005.             {
  1006.                 g1.dispose();
  1007.             }
  1008.  
  1009.             g1         = im1.getGraphics();
  1010.             viewWidth  = d.width;
  1011.             viewHeight = d.height;
  1012.         }
  1013.  
  1014.         Font f = getFont();  //unix version might not provide a default font
  1015.  
  1016.         //Make certain there is a font
  1017.         if (f == null)
  1018.         {
  1019.             f = new Font("Serif", Font.PLAIN, 13);
  1020.             g1.setFont(f);
  1021.             setFont(f);
  1022.         }
  1023.  
  1024.         //Make certain the graphics object has a font (Mac doesn't seem to)
  1025.         if (g1.getFont() == null)
  1026.             g1.setFont(f);
  1027.  
  1028.         fm = g1.getFontMetrics();
  1029.         g1.setColor(getBackground());
  1030.         g1.fillRect(0, 0, im1.getWidth(this), d.height);// clear image
  1031.  
  1032.         //do drawing for each visible node
  1033.         int lastOne = sbVPosition + viewHeight / cellSize + 1;
  1034.  
  1035.         if (lastOne > viewCount)
  1036.         {
  1037.             lastOne = viewCount;
  1038.         }
  1039.  
  1040.         TreeNode outerNode = null;
  1041.  
  1042.         if (!v.isEmpty())
  1043.             outerNode = (TreeNode)v.elementAt(sbVPosition);
  1044.  
  1045.         for (int i = sbVPosition; i < lastOne; i++)
  1046.         {
  1047.             TreeNode node = (TreeNode)v.elementAt(i);
  1048.             int x         = cellSize*(node.depth + 1);
  1049.             int y         = (i - sbVPosition) * cellSize;
  1050.  
  1051.             // draw lines
  1052.             g1.setColor(getForeground());
  1053.  
  1054.             // draw vertical sibling line
  1055.             if (node.sibling != null && node.isASiblingVisible())
  1056.             {
  1057.                 int k = v.indexOf(node.sibling) - i;
  1058.  
  1059.                 if (k > lastOne)
  1060.                 {
  1061.                     k = lastOne;
  1062.                 }
  1063.  
  1064.                 drawDotLine(x - cellSize/2, y + cellSize/2,
  1065.                             x - cellSize/2, y + cellSize/2 +  k*cellSize);
  1066.  
  1067.             }
  1068.  
  1069.             // if sibling is above page, draw up to top of page for this level
  1070.             for (int m = 0; m < i; m++)
  1071.             {
  1072.                 TreeNode sib = (TreeNode)v.elementAt(m);
  1073.  
  1074.                 if ((sib.sibling == node) && (m < sbVPosition))
  1075.                 {
  1076.                     drawDotLine (x - cellSize / 2, 0,
  1077.                                  x - cellSize / 2, y + cellSize / 2);
  1078.                 }
  1079.             }
  1080.  
  1081.             // draw vertical child lines
  1082.             if (node.isExpanded() && node.isAChildVisible())
  1083.             {
  1084.                 drawDotLine(x + cellSize / 2, y + cellSize - 2 ,
  1085.                             x + cellSize / 2, y + cellSize + cellSize / 2);
  1086.             }
  1087.  
  1088.             // draw node horizontal line
  1089.             g1.setColor(getForeground());
  1090.             drawDotLine(x - cellSize / 2, y + cellSize / 2,
  1091.                         x + cellSize / 2, y + cellSize / 2);
  1092.  
  1093.             // draw toggle box
  1094.             drawNodeToggle(node, x, y);
  1095.  
  1096.             // draw node image
  1097.             Image nodeImage = node.getImage();
  1098.  
  1099.             if (nodeImage != null)
  1100.             {
  1101.                 g1.drawImage(nodeImage, x + imageInset, y, this);
  1102.             }
  1103.  
  1104.             // draw node text
  1105.             if (node.text != null)
  1106.             {
  1107.                 drawNodeText(node, y, node == selectedNode, false);
  1108.             }
  1109.  
  1110.             if(outerNode.depth > node.depth)
  1111.                 outerNode = node;
  1112.         }
  1113.  
  1114.         // draw outer vertical lines
  1115.         if (outerNode != null)
  1116.         {
  1117.             while((outerNode = outerNode.parent) != null)
  1118.             {
  1119.                 if (outerNode.sibling != null && outerNode.isASiblingVisible())
  1120.                     drawDotLine (cellSize * (outerNode.depth + 1) - cellSize / 2, 0,
  1121.                                  cellSize * (outerNode.depth + 1) - cellSize / 2, d.height);
  1122.             }
  1123.         }
  1124.  
  1125.         needResetVector = true;
  1126.     }
  1127.  
  1128.     /**
  1129.      * Used to draw the toggle box of an expandable node.
  1130.      * Override to change the look of the toggle box.
  1131.      */
  1132.     protected void drawNodeToggle(TreeNode node, int x, int y)
  1133.     {
  1134.         if(node.isExpandable() && node.isAChildVisible())
  1135.         {
  1136.             g1.setColor(getBackground());
  1137.             g1.fillRect(cellSize * (node.depth) + cellSize / 4, y + clickSize / 2, clickSize, clickSize);
  1138.             g1.setColor(getForeground());
  1139.             g1.drawRect(cellSize * (node.depth) + cellSize / 4, y + clickSize / 2, clickSize, clickSize);
  1140.  
  1141.             // cross hair
  1142.             g1.drawLine(cellSize * (node.depth) + cellSize / 4 + 2,             y + cellSize / 2,
  1143.                         cellSize * (node.depth) + cellSize / 4 + clickSize - 2, y + cellSize / 2);
  1144.  
  1145.             if(!(node.isExpanded()))
  1146.             {
  1147.                 g1.drawLine(cellSize * (node.depth) + cellSize / 2, y + clickSize / 2 + 2,
  1148.                             cellSize * (node.depth) + cellSize / 2, y + clickSize / 2 + clickSize - 2);
  1149.             }
  1150.         }
  1151.     }
  1152.  
  1153.     /**
  1154.      * Returns the recommended dimensions to properly display this component.
  1155.      * This is a standard Java AWT method which gets called to determine
  1156.      * the recommended size of this component.
  1157.      *
  1158.      * @see #getMinimumSize
  1159.      */
  1160.     public synchronized Dimension getPreferredSize()
  1161.     {
  1162.         Dimension p = size();
  1163.         Dimension m = getMinimumSize();
  1164.         return new Dimension(Math.max(p.width, m.width), Math.max(p.height, m.height));
  1165.     }
  1166.  
  1167.     /**
  1168.      * @deprecated
  1169.      * @see #getPreferredSize
  1170.      */
  1171.     public synchronized Dimension preferredSize()
  1172.     {
  1173.         return getPreferredSize();
  1174.     }
  1175.  
  1176.     /**
  1177.      * Returns the minimum dimensions to properly display this component.
  1178.      * This is a standard Java AWT method which gets called to determine
  1179.      * the minimum size of this component.
  1180.      *
  1181.      * @see #getPreferredSize
  1182.      */
  1183.     public synchronized Dimension getMinimumSize()
  1184.     {
  1185.         return new Dimension(20, 40);
  1186.     }
  1187.  
  1188.     /**
  1189.      * @deprecated
  1190.      * @see #getMinimumSize
  1191.      */
  1192.     public synchronized Dimension minimumSize()
  1193.     {
  1194.         return getMinimumSize();
  1195.     }
  1196.  
  1197.     /**
  1198.      * Takes no action.
  1199.      * This is a standard Java AWT method which gets called to specify
  1200.      * which layout manager should be used to layout the components in
  1201.      * standard containers.
  1202.      *
  1203.      * Since layout managers CANNOT BE USED with this container the standard
  1204.      * setLayout has been OVERRIDDEN for this container and does nothing.
  1205.      *
  1206.      * @param lm the layout manager to use to layout this container's components
  1207.      * (IGNORED)
  1208.      * @see java.awt.Container#getLayout
  1209.      **/
  1210.     public void setLayout(LayoutManager lm)
  1211.     {
  1212.     }
  1213.  
  1214.     public boolean isFocusTraversable()
  1215.     {
  1216.         return true;
  1217.     }
  1218.  
  1219.     /**
  1220.      * Tells this component that it has been added to a container.
  1221.      * This is a standard Java AWT method which gets called by the AWT when
  1222.      * this component is added to a container. Typically, it is used to
  1223.      * create this component's peer.
  1224.      *
  1225.      * It has been overridden here to hook-up event listeners.
  1226.      *
  1227.      * @see #removeNotify
  1228.      */
  1229.     public synchronized void addNotify()
  1230.     {
  1231.         super.addNotify();
  1232.         errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  1233.  
  1234.         //Hook up listeners
  1235.         if (mouse == null)
  1236.         {
  1237.             mouse = new Mouse();
  1238.             addMouseListener(mouse);
  1239.         }
  1240.         if (key == null)
  1241.         {
  1242.             key = new Key();
  1243.             addKeyListener(key);
  1244.         }
  1245.         if (adjustment == null)
  1246.         {
  1247.             adjustment = new Adjustment();
  1248.             verticalScrollBar.addAdjustmentListener(adjustment);
  1249.             horizontalScrollBar.addAdjustmentListener(adjustment);
  1250.         }
  1251.         if (focus == null)
  1252.         {
  1253.             focus = new Focus();
  1254.             addFocusListener(focus);
  1255.         }
  1256.  
  1257.     }
  1258.  
  1259.     /**
  1260.      * Tells this component that it is being removed from a container.
  1261.      * This is a standard Java AWT method which gets called by the AWT when
  1262.      * this component is removed from a container. Typically, it is used to
  1263.      * destroy the peers of this component and all its subcomponents.
  1264.      *
  1265.      * It has been overridden here to unhook event listeners.
  1266.      *
  1267.      * @see #addNotify
  1268.      */
  1269.     public synchronized void removeNotify()
  1270.     {
  1271.         //Unhook listeners
  1272.         if (mouse != null)
  1273.         {
  1274.             removeMouseListener(mouse);
  1275.             mouse = null;
  1276.         }
  1277.         if (key != null)
  1278.         {
  1279.             removeKeyListener(key);
  1280.             key = null;
  1281.         }
  1282.         if (adjustment != null)
  1283.         {
  1284.             verticalScrollBar.removeAdjustmentListener(adjustment);
  1285.             horizontalScrollBar.removeAdjustmentListener(adjustment);
  1286.             adjustment = null;
  1287.         }
  1288.         if (focus != null)
  1289.         {
  1290.             removeFocusListener(focus);
  1291.             focus = null;
  1292.         }
  1293.         super.removeNotify();
  1294.     }
  1295.  
  1296.     /**
  1297.      * Triggers redrawing the entire image, even if the size of the component
  1298.      * has not changed.
  1299.      */
  1300.     public void triggerRedraw()
  1301.     {
  1302.         redrawTriggered = true;
  1303.         repaint();
  1304.     }
  1305.  
  1306.     // -----------------------------------------
  1307.     // --------- event related methods ---------
  1308.     // -----------------------------------------
  1309.  
  1310.     /**
  1311.      * Adds the specified action listener to receive action events
  1312.      * from this button.
  1313.      * @param l the action listener
  1314.      */
  1315.     public synchronized void addActionListener(ActionListener l)
  1316.     {
  1317.         actionListener = AWTEventMulticaster.add(actionListener, l);
  1318.     }
  1319.  
  1320.     /**
  1321.      * Removes the specified action listener so it no longer receives
  1322.      * action events from this button.
  1323.      * @param l the action listener
  1324.      */
  1325.     public synchronized void removeActionListener(ActionListener l)
  1326.     {
  1327.         actionListener = AWTEventMulticaster.remove(actionListener, l);
  1328.     }
  1329.  
  1330.     /**
  1331.      * Add a listener to recieve item events when the state of
  1332.      * an item changes.
  1333.      * <p>
  1334.      * This is a standard method of the ItemSelectable interface.
  1335.      * @param l the listener to recieve events
  1336.      * @see ItemEvent
  1337.      */
  1338.     public synchronized void addItemListener(ItemListener l)
  1339.     {
  1340.         itemListener = AWTEventMulticaster.add(itemListener, l);
  1341.     }
  1342.  
  1343.     /**
  1344.      * Removes an item listener.
  1345.      * <p>
  1346.      * This is a standard method of the ItemSelectable interface.
  1347.      * @param l the listener being removed
  1348.      * @see ItemEvent
  1349.      */
  1350.     public synchronized void removeItemListener(ItemListener l)
  1351.     {
  1352.         itemListener = AWTEventMulticaster.remove(itemListener, l);
  1353.     }
  1354.  
  1355.     class Adjustment implements AdjustmentListener, java.io.Serializable
  1356.     {
  1357.         public void adjustmentValueChanged(AdjustmentEvent event)
  1358.         {
  1359.             handleAdjustmentEvent(event);
  1360.         }
  1361.     }
  1362.  
  1363.     class Mouse extends MouseAdapter implements java.io.Serializable
  1364.     {
  1365.         /**
  1366.          * Processes MOUSE_DOWN events.
  1367.          * This is a standard Java AWT method which gets called by the AWT
  1368.          * method handleEvent() in response to receiving a MOUSE_DOWN
  1369.          * event. These events occur when the mouse button is pressed while
  1370.          * inside this component.
  1371.          *
  1372.          * @param event the event
  1373.          * @param x the component-relative horizontal coordinate of the mouse
  1374.          * @param y the component-relative vertical coordinate of the mouse
  1375.          *
  1376.          * @return true if the event was handled
  1377.          *
  1378.          * @see java.awt.Component#mouseUp
  1379.          */
  1380.         public void mousePressed(MouseEvent event)
  1381.         {
  1382.             handleMousePressed(event);
  1383.         }
  1384.  
  1385.         public void mouseReleased(MouseEvent event)
  1386.         {
  1387.             handleMouseReleased(event);
  1388.         }
  1389.     }
  1390.  
  1391.     class Key extends KeyAdapter implements java.io.Serializable
  1392.     {
  1393.         /**
  1394.          * Processes KEY_PRESS and KEY_ACTION events.
  1395.          * This is a standard Java AWT method which gets called by the AWT
  1396.          * method handleEvent() in response to receiving a KEY_PRESS or
  1397.          * KEY_ACTION event. These events occur when this component has the focus
  1398.          * and the user presses a "normal" or an "action" (F1, page up, etc) key.
  1399.          *
  1400.          * @param event the Event
  1401.          * @param key the key that was pressed
  1402.          * @return true if the event was handled
  1403.          * @see java.awt.Component#keyUp
  1404.          * @see #handleEvent
  1405.          */
  1406.         public void keyPressed(KeyEvent event)
  1407.         {
  1408.             handleKeyPressed(event);
  1409.         }
  1410.     }
  1411.  
  1412.     class Focus extends FocusAdapter implements java.io.Serializable
  1413.     {
  1414.         public void focusGained(FocusEvent event)
  1415.         {
  1416.             handleFocusGained(event);
  1417.         }
  1418.  
  1419.         public void focusLost(FocusEvent event)
  1420.         {
  1421.             handleFocusLost(event);
  1422.         }
  1423.     }
  1424.  
  1425.     /**
  1426.      * Handles mouse pressed events.
  1427.      * This function will get called when the component recives a mouse pressed event.
  1428.      * Override to change the way mouse pressed is handled.
  1429.      * @param event the MouseEvent
  1430.      * @see #handleMouseReleased
  1431.      */
  1432.     protected void handleMousePressed(MouseEvent event)
  1433.     {
  1434.         requestFocus();
  1435.  
  1436.         int x = event.getX();
  1437.         int y = event.getY();
  1438.  
  1439.         int index = (y / cellSize) + sbVPosition;
  1440.  
  1441.         //If clicked below the last node
  1442.         if (index > viewCount-1)
  1443.             return;
  1444.  
  1445.         TreeNode oldNode = selectedNode;
  1446.  
  1447.         TreeNode newNode = (TreeNode)v.elementAt(index);
  1448.  
  1449.         int newDepth = newNode.getDepth();
  1450.  
  1451.         // check for toggle box click
  1452.         // todo: make it a bit bigger
  1453.         Rectangle toggleBox = new Rectangle(cellSize*newDepth + cellSize/4,
  1454.                                             (index-sbVPosition)*cellSize + clickSize/2,
  1455.                                             clickSize, clickSize);
  1456.  
  1457.         if (toggleBox.inside(x,y))
  1458.         {
  1459.             newNode.toggle();
  1460.             resetVector();
  1461.             if(!newNode.isExpanded())
  1462.             {
  1463.                 if (!v.contains(selectedNode))
  1464.                     setSelectedNode(newNode);
  1465.             }
  1466.             triggerRedraw();
  1467.             invalidate();
  1468.             sendActionEvent();
  1469.  
  1470.             if(itemListener != null)
  1471.             {
  1472.                 itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, newNode, newNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1473.             }
  1474.         }
  1475.         else
  1476.         {
  1477.             setSelectedNode(newNode);
  1478.  
  1479.             // check for double click
  1480.             long currentTime = event.getWhen();
  1481.  
  1482.             if ((newNode==oldNode) && ((event.getWhen() - timeMouseDown)<doubleClickResolution))
  1483.             {
  1484.                 newNode.toggle();
  1485.                 resetVector();
  1486.                 triggerRedraw();
  1487.                 invalidate();
  1488.                 sendActionEvent();
  1489.  
  1490.                 if(itemListener != null)
  1491.                 {
  1492.                     itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, newNode, newNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1493.                 }
  1494.  
  1495.                 return;
  1496.             }
  1497.             else
  1498.             {
  1499.                 //single click action could be added here
  1500.                 timeMouseDown = event.getWhen();
  1501.             }
  1502.  
  1503.         }
  1504.     }
  1505.  
  1506.     /**
  1507.      * Handles mouse released events.
  1508.      * This function will get called when the component recives a mouse released event.
  1509.      * Override to change the way mouse released is handled.
  1510.      * @param event the MouseEvent
  1511.      * @see #handleMousePressed
  1512.      */
  1513.     protected void handleMouseReleased(MouseEvent event)
  1514.     {
  1515.     }
  1516.  
  1517.     /**
  1518.      * Handles adjustment events.
  1519.      * This function will get called when the component recives a adjustment event.
  1520.      * Override to change the way adjustment is handled.
  1521.      * @param event the AdjustmentEvent
  1522.      */
  1523.     protected void handleAdjustmentEvent(AdjustmentEvent event)
  1524.     {
  1525.         if (event.getAdjustable() == verticalScrollBar)
  1526.         {
  1527.             if (sbVPosition != verticalScrollBar.getValue())
  1528.             {
  1529.                 sbVPosition = verticalScrollBar.getValue();
  1530.                 triggerRedraw();
  1531.             }
  1532.         }
  1533.         else
  1534.         if (event.getAdjustable() == horizontalScrollBar)
  1535.         {
  1536.             if (sbHPosition != horizontalScrollBar.getValue())
  1537.             {
  1538.                 sbHPosition = horizontalScrollBar.getValue();
  1539.                 //repaint();
  1540.                 triggerRedraw();
  1541.             }
  1542.         }
  1543.     }
  1544.  
  1545.     /**
  1546.      * Handles key pressed events.
  1547.      * This function will get called when the component recives a key pressed event.
  1548.      * Override to change the way key pressed is handled.
  1549.      * @param event the KeyEvent
  1550.      */
  1551.     protected void handleKeyPressed(KeyEvent event)
  1552.     {
  1553.         int index = v.indexOf(selectedNode);
  1554.  
  1555.         switch (event.getKeyCode())
  1556.         {
  1557.             case KeyEvent.VK_ENTER:    //enter key
  1558.             case 13:
  1559.                 sendActionEvent();
  1560.                 requestFocus();
  1561.                 break;
  1562.             case KeyEvent.VK_LEFT:    //left arrow
  1563.                 if(event.isControlDown())
  1564.                 {
  1565.                     if(sbHPosition > 0)
  1566.                     {
  1567.                         horizontalScrollBar.setValue(Math.max(sbHPosition-=sbHLineIncrement,0));
  1568.                         repaint();
  1569.                     }
  1570.                     break;
  1571.                 }
  1572.                 else
  1573.                 if (selectedNode.isExpanded())
  1574.                 {
  1575.                     selectedNode.toggle();
  1576.  
  1577.                     if(itemListener != null)
  1578.                     {
  1579.                         itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, selectedNode, selectedNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1580.                         triggerRedraw();
  1581.                     }
  1582.  
  1583.                     break;
  1584.                 }
  1585.  
  1586.                 // else drop through to "UP" with no "break;"
  1587.             case KeyEvent.VK_UP:
  1588.                 if (index > 0)
  1589.                 {
  1590.                     index--;
  1591.                     setSelectedNode((TreeNode)v.elementAt(index));
  1592.                     requestFocus();
  1593.                 }
  1594.                 break;
  1595.             case KeyEvent.VK_RIGHT:
  1596.                 if(event.isControlDown())
  1597.                 {
  1598.                     int max = horizontalScrollBar.getMaximum()-(isSun1_1?size().width-sbVWidth:0);
  1599.                     if(sbHShow && sbHPosition < max)
  1600.                     {
  1601.                         horizontalScrollBar.setValue(Math.min(sbHPosition+=sbHLineIncrement, max));
  1602.                         repaint();
  1603.                     }
  1604.                     break;
  1605.                 }
  1606.                 else
  1607.                 if (selectedNode.isExpandable() && (!selectedNode.isExpanded()))
  1608.                 {
  1609.                     selectedNode.toggle();
  1610.                     sendActionEvent();
  1611.  
  1612.                     if(itemListener != null)
  1613.                     {
  1614.                         itemListener.itemStateChanged(new ItemEvent(this, NODE_TOGGLED, selectedNode, selectedNode.isExpanded ? NODE_EXPANDED : NODE_COLLAPSED));
  1615.                         triggerRedraw();
  1616.                     }
  1617.  
  1618.                     break;
  1619.                 }
  1620.  
  1621.                 if (!selectedNode.isExpandable())
  1622.                 {
  1623.                     break;
  1624.                 }
  1625.                 // else drop thru' to DOWN
  1626.             case KeyEvent.VK_DOWN:
  1627.                 if (index < viewCount-1)
  1628.                 {
  1629.                     index++;
  1630.                     setSelectedNode((TreeNode)v.elementAt(index));
  1631.                     requestFocus();
  1632.                 }
  1633.                 break;
  1634.         }
  1635.     }
  1636.  
  1637.     /**
  1638.      * Handles focus gained events.
  1639.      * This function will get called when the component recives a focus gained event.
  1640.      * Override to change the way focus gained is handled.
  1641.      * @param event the FocusEvent
  1642.      * @see #handleFocusLost
  1643.      */
  1644.     protected void handleFocusGained(FocusEvent event)
  1645.     {
  1646.         hasFocus = true;
  1647.         if (selectedNode != null && v != null)
  1648.             drawNodeText(selectedNode, (v.indexOf(selectedNode) - sbVPosition)*cellSize, true, false);
  1649.     }
  1650.  
  1651.     /**
  1652.      * Handles focus lost events.
  1653.      * This function will get called when the component recives a focus lost event.
  1654.      * Override to change the way focus lost is handled.
  1655.      * @param event the FocusEvent
  1656.      * @see #handleFocusGained
  1657.      */
  1658.     protected void handleFocusLost(FocusEvent event)
  1659.     {
  1660.         hasFocus = false;
  1661.         if (selectedNode != null && v != null)
  1662.             drawNodeText(selectedNode, (v.indexOf(selectedNode) - sbVPosition)*cellSize, true, false);
  1663.     }
  1664.  
  1665.     /**
  1666.      * Internal helper method.
  1667.      * Draws the text of the given tree node.
  1668.      * @param node the tree node whose text is drawn
  1669.      * @param yPosition the vertical drawing position for the node
  1670.      * @param eraseBackground <code>true</code> to erase the background behind the text
  1671.      * @param eraseLines <code>true</code> to erase the node's text lines before redrawing
  1672.      */
  1673.     protected void drawNodeText(TreeNode node, int yPosition, boolean eraseBackground, boolean eraseLines)
  1674.     {
  1675.         if (node == null)
  1676.             return;
  1677.  
  1678.         Color fg, bg;
  1679.         int depth=node.depth;
  1680.         Image nodeImage = node.getImage();
  1681.         int textOffset = ((depth + 1) * (cellSize)) + cellSize + textInset - (nodeImage==null ? 12:0);
  1682.  
  1683.         if (node == selectedNode && hasFocus)
  1684.         {
  1685.             //??RKM?? Temp until these return some good values
  1686.             if (symantec.itools.lang.OS.isMacintosh())
  1687.                 fg = Color.white;
  1688.             else
  1689.                 fg = SystemColor.textHighlightText;
  1690.  
  1691.             if (symantec.itools.lang.OS.isMacintosh())
  1692.                 bg = new Color(0,0,128);
  1693.             else
  1694.                 bg = SystemColor.textHighlight;
  1695.         }
  1696.         else
  1697.         {
  1698.             fg = getForeground();
  1699.             bg = getBackground();
  1700.         }
  1701.  
  1702.         if (eraseBackground)
  1703.         {
  1704.             g1.setColor(bg);
  1705.             g1.fillRect(textOffset-1, yPosition+1, fm.stringWidth(node.text)+4, cellSize-1);
  1706.         }
  1707.  
  1708.         if (node == selectedNode)
  1709.         {
  1710.             g1.setColor(getForeground());
  1711.             g1.drawRect(textOffset-1, yPosition+1, fm.stringWidth(node.text)+3, cellSize-2);
  1712.             repaint(Math.max(0,textOffset-1-sbHPosition), yPosition+1, fm.stringWidth(node.text)+4, cellSize-1);
  1713.         }
  1714.  
  1715.         if (eraseLines)
  1716.         {
  1717.             g1.setColor(getBackground());
  1718.             g1.drawRect(textOffset-1, yPosition+1, fm.stringWidth(node.text)+3, cellSize-2);
  1719.             repaint(Math.max(0,textOffset-1-sbHPosition), yPosition+1, fm.stringWidth(node.text)+4, cellSize-1);
  1720.         }
  1721.  
  1722.         g1.setColor(fg);
  1723.         g1.drawString(node.text, textOffset, yPosition + cellSize - textBaseLine);
  1724.     }
  1725.  
  1726.     /**
  1727.      * Sends an action performed event to any action listeners, as needed.
  1728.      */
  1729.     protected void sendActionEvent()
  1730.     {
  1731.         if (actionListener != null)
  1732.             actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, new String(selectedNode.getText())));
  1733.     }
  1734.  
  1735.     /**
  1736.      * Calculates the maximum width of the tree.
  1737.      * @param gg the Graphics context to use for the calculation
  1738.      * @return the maximum tree width, in pixels
  1739.      */
  1740.     protected int compWidth(Graphics gg)
  1741.     {
  1742.         int size = 0;
  1743.         int textOffset;
  1744.         TreeNode node;
  1745.  
  1746.            Font f = getFont();  //unix version might not provide a default font
  1747.         //Make certain there is a font
  1748.         if (f == null)
  1749.         {
  1750.             f = new Font("Serif", Font.PLAIN, 13);
  1751.             if(gg != null)
  1752.                    gg.setFont(f);
  1753.             setFont(f);
  1754.         }
  1755.  
  1756.         if(gg == null)
  1757.             fm = null;
  1758.         else
  1759.             fm = gg.getFontMetrics();
  1760.  
  1761.         if(fm == null)
  1762.             fm = getFontMetrics(f);
  1763.  
  1764.         if(fm == null || v == null)
  1765.             size = 100;
  1766.         else
  1767.         {
  1768.             for (int i=0; i < v.size(); i++)
  1769.             {
  1770.                 node = (TreeNode)v.elementAt(i);
  1771.                    textOffset = ((node.depth + 1) * (cellSize)) + cellSize + textInset - (node.getImage()==null ? 12:0);
  1772.                    if (size < (textOffset+fm.stringWidth(node.text)+6))
  1773.                     size = textOffset+fm.stringWidth(node.text)+6;
  1774.             }
  1775.         }
  1776.  
  1777.         return size;
  1778.     }
  1779.  
  1780.     /**
  1781.      * Internal helper method.
  1782.      * Draws a dotted line.
  1783.      * @param x0 starting x position
  1784.      * @param y0 starting y position
  1785.      * @param x1 ending x position
  1786.      * @param y1 ending y position
  1787.      */
  1788.     protected void drawDotLine(int x0, int y0, int x1, int y1)
  1789.     {
  1790.        if (y0==y1)
  1791.        {
  1792.             for (int i = x0; i<x1; i+=2)
  1793.             {
  1794.                g1.drawLine(i,y0, i, y1);
  1795.             }
  1796.         }
  1797.         else
  1798.         {
  1799.             for (int i = y0; i<y1; i+=2)
  1800.             {
  1801.                 g1.drawLine(x0, i, x1, i);
  1802.             }
  1803.         }
  1804.     }
  1805.  
  1806.     /**
  1807.      * Handles selecting the given node.
  1808.      * @param node the node to select
  1809.      */
  1810.     protected void changeSelection(TreeNode node)
  1811.     {
  1812.         setSelectedNode(node);
  1813.     }
  1814.  
  1815.     /**
  1816.      * Handles selecting the given node.
  1817.      * @param node the node to select
  1818.      */
  1819.     public void setSelectedNode(TreeNode node)
  1820.     {
  1821.         if(node == null)
  1822.         {
  1823.             if(selectedNode != null)
  1824.             {
  1825.                 drawNodeText(selectedNode, (v.indexOf(selectedNode) - sbVPosition) * cellSize, true, true);
  1826.  
  1827.                 if(itemListener != null)
  1828.                 {
  1829.                     itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectedNode, ItemEvent.DESELECTED));
  1830.                 }
  1831.  
  1832.                 selectedNode = null;
  1833.             }
  1834.  
  1835.             return;
  1836.         }
  1837.  
  1838.         if (node == selectedNode)
  1839.             return;
  1840.  
  1841.         TreeNode oldNode = selectedNode;
  1842.         selectedNode = node;
  1843.  
  1844.         if(oldNode != null)
  1845.         {
  1846.             drawNodeText(oldNode, (v.indexOf(oldNode) - sbVPosition)*cellSize, true, false);
  1847.         }
  1848.  
  1849.         drawNodeText(node, (v.indexOf(node) - sbVPosition)*cellSize, true, false);
  1850.  
  1851.         // send select event
  1852.  
  1853.         int index = v.indexOf(selectedNode);
  1854.  
  1855.         if (itemListener != null)
  1856.         {
  1857.             if(oldNode != null)
  1858.             {
  1859.                 itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, oldNode, ItemEvent.DESELECTED));
  1860.             }
  1861.  
  1862.             itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, selectedNode, ItemEvent.SELECTED));
  1863.         }
  1864.  
  1865.         if (index < sbVPosition)
  1866.         { //scroll up
  1867.             sbVPosition--;
  1868.             verticalScrollBar.setValue(sbVPosition);
  1869.             triggerRedraw();
  1870.             return;
  1871.         }
  1872.  
  1873.         if (index >= sbVPosition + (viewHeight-cellSize/2)/cellSize)
  1874.         {
  1875.             sbVPosition++;
  1876.             verticalScrollBar.setValue(sbVPosition);
  1877.             triggerRedraw();
  1878.             return;
  1879.         }
  1880.  
  1881.         repaint();
  1882.     }
  1883.  
  1884.     /**
  1885.      * Generates a new tree node.
  1886.      * Override if you want to return your own instantiation of a TreeNode subclas.
  1887.      * @param text the node text
  1888.      * @param treeView a reference to the parent TreeView (usually "this").
  1889.      * @return a new TreeNode
  1890.      */
  1891.     protected TreeNode newTreeNode(String text, TreeView treeView)
  1892.     {
  1893.         return new TreeNode(text, treeView);
  1894.     }
  1895.  
  1896.     /**
  1897.      * Internal helper method.
  1898.      * Process the given array of Strings, creating TreeNodes and appending them to this
  1899.      * component. Existing TreeNodes are kept.
  1900.      * @param tempStructure the String array to parse
  1901.      * @exception InvalidTreeNodeException if there is a problem with the String array values
  1902.      */
  1903.     protected void parseTreeStructure(String tempStructure[]) throws InvalidTreeNodeException
  1904.     {
  1905.         for(int i = 0; i < tempStructure.length; i++)
  1906.         {
  1907.             String entry = tempStructure[i];
  1908.             int indentLevel = findLastPreSpace(entry)/*+1*/;
  1909.  
  1910.             if (indentLevel == -1)
  1911.                 throw new InvalidTreeNodeException("parseTreeStructure: " + errors.getString("EmptyStrings"));
  1912.  
  1913.             TreeNode node = newTreeNode(entry.trim(), this);
  1914.             node.setDepth(indentLevel);
  1915.  
  1916.             if (rootNode == null)
  1917.             {
  1918.                 if (indentLevel != 0)
  1919.                     throw new InvalidTreeNodeException("parseTreeStructure: " + errors.getString("NoRootLevelNode"));
  1920.  
  1921.                 append(node);
  1922.             }
  1923.             else
  1924.             {
  1925.                 TreeNode currentNode = rootNode;
  1926.                 while(currentNode.sibling != null)
  1927.                     currentNode = currentNode.sibling;
  1928.  
  1929.                 for(int j = 1; j < indentLevel; j++)
  1930.                 {
  1931.                     int numberOfChildren = currentNode.numberOfChildren;
  1932.                     TreeNode tempNode = null;
  1933.  
  1934.                     if (numberOfChildren > 0)
  1935.                     {
  1936.                         tempNode = currentNode.child;
  1937.  
  1938.                         while(tempNode.sibling != null)
  1939.                             tempNode = tempNode.sibling;
  1940.                     }
  1941.  
  1942.                     if (tempNode != null)
  1943.                         currentNode = tempNode;
  1944.                     else
  1945.                         break;
  1946.                 }
  1947.  
  1948.                 int diff = indentLevel - currentNode.getDepth();
  1949.  
  1950.                 if (diff > 1)
  1951.                     throw new InvalidTreeNodeException("parseTreeStructure: " + errors.getString("NoParent") + entry.trim());
  1952.  
  1953.                 if (diff == 1)
  1954.                     insert(node, currentNode, CHILD);
  1955.                 else
  1956.                     insert(node, currentNode, NEXT);
  1957.             }
  1958.         }
  1959.     }
  1960.  
  1961.     /**
  1962.      * Internal helper method.
  1963.      * Rebuilds the Vector e of nodes, adjusting node depth and maintaining an
  1964.      * overall count as it goes along.
  1965.      * Calls the recursive method traverse to do the meat of the job.
  1966.      */
  1967.     protected void recount()
  1968.     {
  1969.         count = 0;
  1970.         e = new Vector();
  1971.  
  1972.         if (rootNode != null)
  1973.         {
  1974.             rootNode.depth=0;
  1975.             traverse(rootNode);
  1976.         }
  1977.     }
  1978.  
  1979.     /**
  1980.      * Internal helper method.
  1981.      * Recursive method that rebuilds the Vector e of nodes,
  1982.      * adjusting node depth and maintaining an
  1983.      * overall count as it goes along.
  1984.      */
  1985.     protected void traverse(TreeNode node)
  1986.     {
  1987.         count++;
  1988.  
  1989.         if(!node.isHidden())
  1990.         {
  1991.             e.addElement(node);
  1992.  
  1993.             if (node.child != null)
  1994.             {
  1995.                 node.child.depth = node.depth+1;
  1996.                 traverse(node.child);
  1997.             }
  1998.         }
  1999.  
  2000.         if (node.sibling != null)
  2001.         {
  2002.             node.sibling.depth = node.depth;
  2003.             traverse(node.sibling);
  2004.         }
  2005.     }
  2006.  
  2007.     /**
  2008.      * Internal helper method.
  2009.      * Traverses tree to put nodes into vector v
  2010.      * for internal processing. Depths of nodes are set,
  2011.      * and viewCount and viewWidest is set.
  2012.      * Calls the recursive method vectorize to do the meat of the job.
  2013.      */
  2014.     protected void resetVector()
  2015.     {
  2016.         // Traverses tree to put nodes into vector v
  2017.         // for internal processing. Depths of nodes are set,
  2018.         // and viewCount and viewWidest is set.
  2019.         v = new Vector(count);
  2020.         viewWidest = 30;
  2021.  
  2022.         if (count < 1)
  2023.         {
  2024.             viewCount = 0;
  2025.             return;
  2026.         }
  2027.  
  2028.         rootNode.depth = 0;
  2029.         vectorize(rootNode, true, true, v);
  2030.         viewCount = v.size();
  2031.  
  2032.         needResetVector = false;
  2033.     }
  2034.  
  2035.     /**
  2036.      * Internal helper method.
  2037.      * Traverses tree to put nodes into the given vector for for processing.
  2038.      * Depths of nodes are set.
  2039.      * @param node the current node
  2040.      * @param respectExpanded <code>true</code> to only follow expanded child links
  2041.      * @param repectHidden <code>true</code> to not follow hidden child links
  2042.      * @param nodeVector Vector to add the processed nodes to
  2043.      */
  2044.     protected void vectorize(TreeNode node, boolean respectExpanded, boolean repectHidden, Vector nodeVector)
  2045.     {
  2046.         if(node == null)
  2047.         {
  2048.             return;
  2049.         }
  2050.  
  2051.         if(!repectHidden || !node.isHidden())
  2052.         {
  2053.             nodeVector.addElement(node);
  2054.  
  2055.             if((!respectExpanded && node.child != null) || node.isExpanded())
  2056.             {
  2057.                 node.child.depth = node.depth + 1;
  2058.                 vectorize(node.child, respectExpanded, repectHidden, nodeVector);
  2059.             }
  2060.         }
  2061.  
  2062.         if (node.sibling != null)
  2063.         {
  2064.             node.sibling.depth = node.depth;
  2065.             vectorize(node.sibling, respectExpanded, repectHidden, nodeVector);
  2066.         }
  2067.     }
  2068.  
  2069.     /**
  2070.      * Internal use only.
  2071.      */
  2072.     protected void debugVector()
  2073.     {
  2074.         int vSize = v.size();
  2075.  
  2076.         for (int i=0; i<count; i++)
  2077.         {
  2078.             TreeNode node = (TreeNode) v.elementAt(i);
  2079.             System.out.println(node.text);
  2080.         }
  2081.     }
  2082.  
  2083.     /**
  2084.      * Internal helper method.
  2085.      * Counts the number of spaces before the node text starts.
  2086.      * @param s the node text
  2087.      * @return the number of spaces before the texxt starts
  2088.      */
  2089.     protected int findLastPreSpace(String s)
  2090.     {
  2091.         if(s != null && s.length() > 0)
  2092.         {
  2093.             int length;
  2094.  
  2095.             length = s.length();
  2096.  
  2097.             if(s.charAt(0) != ' ' && s.charAt(0) != '\t')
  2098.             {
  2099.                 return 0;
  2100.             }
  2101.  
  2102.             for(int i = 1; i < length; i++)
  2103.             {
  2104.                 if(s.charAt(i) != ' ' && s.charAt(i) != '\t')
  2105.                 {
  2106.                     return i;
  2107.                 }
  2108.             }
  2109.         }
  2110.         return -1;
  2111.     }
  2112.  
  2113.  
  2114.     int        sbVPosition                = 0;    // hold value of vertical scrollbar
  2115.     int        sbVWidth;                           // width of vertical scrollbar
  2116.     int        sbHPosition                = 0;    // hold value of horizontal scrollbar
  2117.     int        sbHHeight                = 0;    // height of horizontal scrollbar
  2118.     long    sbVTimer                = -1;    // time of last vert scrollbar event
  2119.     int        cellSize                   = 16;   // size of node image
  2120.     int        clickSize               = 8;    // size of mouse toggle (plus or minus)
  2121.     int        imageInset                = 3;    // left margin of node image
  2122.     int        textInset               = 6;    // left margin for text
  2123.     int        textBaseLine            = 3;    // position of font baseline from bottom of cell
  2124.     int        doubleClickResolution    = 333;    // double-click speed in milliseconds
  2125.  
  2126.     /**
  2127.      * root node of tree
  2128.      */
  2129.     protected TreeNode rootNode;
  2130.     /**
  2131.      * highlighted node
  2132.      */
  2133.     protected TreeNode selectedNode;
  2134.     /**
  2135.      * first node in window
  2136.      */
  2137.     protected TreeNode topVisibleNode;
  2138.     /**
  2139.      * The vertical scrollbar.
  2140.      */
  2141.     protected Scrollbar    verticalScrollBar;
  2142.     /**
  2143.      * show or hide vertical scrollbar
  2144.      */
  2145.     protected boolean sbVShow = false;
  2146.     /**
  2147.      * Number of nodes in the tree.
  2148.      */
  2149.     protected int count = 0;
  2150.     /**
  2151.      * Number of viewable nodes in the tree.
  2152.      * A node is viewable if all of its parents are expanded.
  2153.      */
  2154.     protected int    viewCount = 0;
  2155.     /**
  2156.      * The horizontal scrollbar
  2157.      */
  2158.     protected Scrollbar horizontalScrollBar;
  2159.     /**
  2160.      * size of horizontal scrollbar
  2161.      */
  2162.     protected int sbHSize;
  2163.     /**
  2164.      * for horizontal scrollbar
  2165.      */
  2166.     protected int newWidth = 0;
  2167.     /**
  2168.      * show or hide horizontal scrollbar
  2169.      */
  2170.        protected boolean sbHShow = false;
  2171.        /**
  2172.         * Keeps track of if drawTree needs to call resetVector or not.
  2173.         */
  2174.        protected boolean needResetVector;
  2175.  
  2176.     /**
  2177.      * Horizontal scroll bar unit increment.
  2178.      */
  2179.     protected int sbHLineIncrement = 4;
  2180.        /**
  2181.         * pixel size of tree display
  2182.         */
  2183.     protected int viewHeight = 300;
  2184.        /**
  2185.         * pixel size of tree display
  2186.         */
  2187.     protected int viewWidth  = 300;
  2188.        /**
  2189.         * widest item displayable (for horz scroll)
  2190.         */
  2191.     protected int viewWidest = 0 ;
  2192.     /**
  2193.      * The this component's key event listener.
  2194.      */
  2195.     protected Key key = null;
  2196.     /**
  2197.      * The this component's mouse event listener.
  2198.      */
  2199.     protected Mouse mouse = null;
  2200.     /**
  2201.      * The this component's adjustment event listener.
  2202.      */
  2203.     protected Adjustment adjustment = null;
  2204.     /**
  2205.      * The action listener to keep track of listeners for our action event.
  2206.      */
  2207.     protected ActionListener actionListener = null;
  2208.     /**
  2209.      * The action listener to keep track of listeners for our item event.
  2210.      */
  2211.     protected ItemListener itemListener = null;
  2212.     /**
  2213.      * The this component's focus event listener.
  2214.      */
  2215.     protected Focus focus = null;
  2216.     /**
  2217.      * v is vector of viewable nodes
  2218.      */
  2219.     protected Vector v;
  2220.     /**
  2221.      * e is vector of existing nodes
  2222.      */
  2223.     protected Vector e;
  2224.     /**
  2225.      * Flag indicating repaint() shoud redraw the entire image even
  2226.      * if the component size has not changed.
  2227.      */
  2228.     transient protected boolean redrawTriggered = false;
  2229.     transient boolean hasFocus = false;
  2230.        /**
  2231.         * current font metrics
  2232.         */
  2233.     transient protected FontMetrics fm;
  2234.        /**
  2235.         * save time of last mouse down (for double click)
  2236.         */
  2237.     transient long timeMouseDown;
  2238.     /**
  2239.      * Offscreen Image used for buffering the painting process.
  2240.      */
  2241.     transient protected Image im1;
  2242.     /**
  2243.      * Offscreen graphics context used for buffering the painting process.
  2244.      */
  2245.     transient protected Graphics g1 = null;
  2246.     /**
  2247.      * Checks for scrollbars that have different max value.
  2248.      */
  2249.     static protected boolean isSun1_1;
  2250.  
  2251.     /**
  2252.      * Error strings.
  2253.      */
  2254.     transient protected ResourceBundle errors;
  2255.  
  2256.     static
  2257.     {
  2258.         //Calc it once
  2259.         String vendor = System.getProperty("java.vendor");
  2260.         String version = System.getProperty("java.version");
  2261.  
  2262.         isSun1_1 = ((vendor.startsWith("Sun Microsystems Inc.") ||
  2263.                     (vendor.startsWith("Apple"))                ||
  2264.                     (vendor.startsWith("Symantec Corporation")) ||
  2265.                     (vendor.startsWith("Netscape"))) &&
  2266.                      ((version.startsWith("11")) ||
  2267.                     (version.startsWith("1.1"))));
  2268.     }
  2269. }
  2270.  
  2271. class InvalidTreeNodeException extends Exception
  2272. {
  2273.     public InvalidTreeNodeException()
  2274.     {
  2275.         super();
  2276.     }
  2277.  
  2278.     public InvalidTreeNodeException(String message)
  2279.     {
  2280.         super(message);
  2281.     }
  2282. }
  2283.