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