home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / Visual Cafe Pro v1.0 / SOURCE.BIN / MultiList.java < prev    next >
Encoding:
Java Source  |  1997-06-19  |  62.0 KB  |  2,213 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.Font;
  4. import java.awt.FontMetrics;
  5. import java.awt.Scrollbar;
  6. import java.awt.Panel;
  7. import java.awt.Color;
  8. import java.awt.Image;
  9. import java.awt.Frame;
  10. import java.awt.Graphics;
  11. import java.awt.Dimension;
  12. import java.awt.Event;
  13. import java.awt.BorderLayout;
  14. import java.awt.Rectangle;
  15. import java.awt.Container;
  16. import java.util.BitSet;
  17. import java.util.Vector;
  18.  
  19. // ===================================================================================
  20. // = Component Notes
  21. // ===================================================================================
  22.  
  23. // To visually use this component, you should use the following steps:
  24. //        1. Create a MultiList component
  25. //        2. Set the headings via 'Column Headings' property in the property list
  26. //        3. (Optionally) Set the alignment of the headings via the 'Column Alignments' property
  27. //        4. (Optionally) Set the size of the columns via the 'Column Sizes' property.
  28. //           Otherwise, leave this blank and the columns will be automatically sized
  29. //        5. Set the contents by using the 'List Items' property in the propery list:
  30. //           (Note: the format for this property is:
  31. //                 row1col1;row1col2;row1col2
  32. //                 row2col1;row2col2;row2col2
  33. //            or in other words seperate the columns by using a semi-colon.)
  34.  
  35.  
  36. // ===================================================================================
  37. // = Revision History
  38. // ===================================================================================
  39.  
  40. // 02/03/97    TWB        Added reshape override to reset the columns during runtime
  41. //                    Added a get/set ColumnSizes properties/methods so that widths
  42. //                      can be determined at design time
  43. //                    Added a get/set ColumnAlignments properties/methods so that alignments
  44. //                      can be determined at design time
  45. // 02/06/97    TWB        Added check for null strings in setListItems
  46. //                    Integrated TGL's change for getListItems
  47. // 03/14/97    RKM        Made changeSelection public as per customer request
  48. // 03/17/97 TGL     Fixed multilists in tab panels (actually, multilists added to anything
  49. //                    without a peer)
  50. // 03/20/97    RKM        Added forceRedraw flag to handle more efficient redraws
  51. //                    Set forceRedraw in addTextCell, addImageCell, & addCell so changes would
  52. //                      redrawn into offscreen
  53. //                    Changed all redraw followed by repaints to use forceRedraw flag
  54. // 03/21/97 RKM        Removed repaint hack from paint
  55. // 03/25/97 RKM        Removed forced call to redraw from repaint when designing (Why was that there???)
  56. // 03/27/97 TNM     Added horizontal ScrollBar
  57. // 03/27/97 RKM     Changed getCellText to return empty String if the elementAt throws
  58. //                    instead of throwing or as the code thought, return null
  59. //                    Added getCellImage
  60. // 03/27/97 RKM     Changed hard coded numbers for sbVWidth & sbHHeight to use prefferedSize of scrollbars
  61. // 04/09/97 RKM     Changed CompareCells' lessThan method to handle nulls passed to it (this removes the nullPointException, but doesn't fix the sort problem)
  62. // 04/17/97    LAB        Added Don Haney's (haney@tiac.net) fixes that addressed:
  63. //                    Column widths revert to default after hide()/show(); modified setHeading(String h, int i, int pixels)
  64. //                    Return from getColumnSize() inconsistent with setHeading(); modified getColumnSize() to return splitters[i + 1], rather than splitters[i]
  65. //                    Scrollbar values and number of items to draw incorrectly set.  Fix in redraw().
  66. // 05/02/97 RKM     Changed setHeadings to accept String of ; as well as array
  67. //                    Added calcHeadings to handle a delayed calulation of headings when add had not been called before setHeadings
  68. //                    Generalized handling of either String[] or ; separated tokens and added it to setHeadings, setColumnAlignments, & setColumnSizes
  69. //                    Added removeRow (people on the newsgroup wanted this)
  70. // 05/22/97 RKM     Changed setSelectedRow to call changeSelection
  71.  
  72. /**
  73.  * Multi-column listbox component.
  74.  * Use to create a box that displays a matrix of items that the user can
  75.  * select. The user cannot type or edit a selection in a list box.
  76.  * <p>
  77.  * The user can resize a column at run-time by dragging the column boundary
  78.  * to a new position.
  79.  * <p>
  80.  * @version 1.01, Feb 3, 1997
  81.  * @author Symantec
  82.  */
  83.  
  84.  
  85. public class MultiList
  86.     extends Panel
  87. {
  88.     /**
  89.      * Time to register a double click (in milliseconds).
  90.      */
  91.     public final static long CLICKTHRESHOLD = 250;
  92.  
  93.     /**
  94.      * Left-justify column alignment constant.
  95.      */
  96.     public final static int LEFT = 0;
  97.  
  98.     /**
  99.      * Center-justify column alignment constant.
  100.      */
  101.     public final static int CENTER = 1;
  102.  
  103.     /**
  104.      * Right-justify column alignment constant.
  105.      */
  106.     public final static int RIGHT = 2;
  107.  
  108.  
  109.     static CompareCells func = new CompareCells();
  110.  
  111.     Color           colorBg = Color.white;
  112.     Color           colorFg = Color.black;
  113.     Color           colorHBg = Color.blue;
  114.     Color           colorHFg = Color.white;
  115.     Matrix          cells = new Matrix();
  116.     boolean        multiSelect = false; //allow multiple selections
  117.     int             splitters[];
  118.     String            headings[];
  119.     int                align[];
  120.     Image           cellIm[];
  121.     int                columnSizes[];
  122.     Color           headingBg = Color.lightGray;
  123.     Color           headingFg = Color.black;
  124.     Font            headingFont;
  125.     Font            cellFont;
  126.     Scrollbar       sbV;
  127.     Scrollbar       sbH;
  128.     int             cursor = Frame.DEFAULT_CURSOR;
  129.     int             colClick = -1;
  130.     int             memoryClick = -1;
  131.     long            clickTime;
  132.     BitSet          highlightedRows = new BitSet();
  133.     int             selectedRow; //???RKM??? What if multiSelect is true ???
  134.     int             topRow;
  135.     int             dragColumn = -1;
  136.     int             xDragLast = -1;
  137.     boolean         isDragging;
  138.     int             headingHeight = 0;
  139.     int             cellHeight = 0;
  140.     int             cellAscent = 0;
  141.     int             cellDescent = 0;
  142.     int             clickMargin = 5;
  143.     int             currentCursor = Frame.DEFAULT_CURSOR;
  144.     Image            im;
  145.     Graphics        gg;
  146.     int                height = -1;
  147.     int                width = -1;
  148.     int                sbVPosition;
  149.     int                sbHPosition;
  150.     int                sbVWidth = 0;
  151.     int                sbHHeight = 0;
  152.     boolean        sbVShow = false;
  153.     boolean        sbHShow = false;
  154.     int                minColumnWidth = 10;
  155.     long            scrollbarTimer;
  156.     boolean        forceRedraw = false;
  157.  
  158.     //Members used to calc headings, when size of component is not known (pre add)
  159.     boolean        needDelayedHeadingsCalc = false;
  160.     String[]        delayedHeadings = null;
  161.  
  162.     /**
  163.      * Default constructor for MultiList.
  164.      */
  165.     public MultiList()
  166.     {
  167.         this(0, false, Color.white);
  168.     }
  169.  
  170.     /**
  171.      * Constructs a new MultiList with the specified number of columns.
  172.      * @param cols the number of columns
  173.      */
  174.     public MultiList(int cols)
  175.     {
  176.         this(cols, false, Color.white);
  177.     }
  178.  
  179.     /**
  180.      * Constructs a new MultiList with the spcified number of columns
  181.      * and whether multiple row selection allowed.
  182.      * @param cols the number of columns
  183.      * @param multi true for multiple row selection, false otherwise
  184.      */
  185.     public MultiList(int cols, boolean multi)
  186.     {
  187.         this(cols, multi, Color.white);
  188.     }
  189.  
  190.     /**
  191.      * Constructs a new MultiList with the spcified number of columns,
  192.      * whether multiple row selection allowed, and background color.
  193.      * @param cols the number of columns
  194.      * @param multi true for multiple row selection, false otherwise
  195.      * @param bg the background color
  196.      */
  197.     public MultiList(int cols, boolean multi, Color bg)
  198.     {
  199.         createColumns(cols);
  200.         multiSelect = multi;
  201.  
  202.         setLayout(null);
  203.         Font defaultFont = new Font("Helvetica", Font.PLAIN, 12);
  204.         setHeadingFont(defaultFont);
  205.         setCellFont(defaultFont);
  206.         setBackground(colorBg = bg);
  207.         sbV = new Scrollbar(Scrollbar.VERTICAL);
  208.         sbV.hide();
  209.         add(sbV);
  210.         sbH = new Scrollbar(Scrollbar.HORIZONTAL);
  211.         sbH.hide();
  212.         add(sbH);
  213.     }
  214.  
  215.     /**
  216.      * Selects all the elements in the MuliList. Multiple select must be
  217.      * enabled for this to have any effect.
  218.      */
  219.     public void selectAll()
  220.     {
  221.         if (getNumberOfRows() == 0)
  222.             return;
  223.  
  224.         if (multiSelect)
  225.         {
  226.             changeSelection(0, 0);
  227.             changeSelection(getNumberOfRows()-1, Event.SHIFT_MASK);
  228.         }
  229.     }
  230.  
  231.     /**
  232.      * Sets the number of columns.
  233.      * @param i the new number of columns
  234.      */
  235.     public void setColumns(int i)
  236.     {
  237.         createColumns(i);
  238.     }
  239.  
  240.     /**
  241.      * Gets the current number of columns.
  242.      * @return the current number of columns
  243.      */
  244.     public int getNumberOfRows()
  245.     {
  246.         return cells.rows();
  247.     }
  248.  
  249.     /**
  250.      * Sets a column's heading text and width.
  251.      * @param h the new column heading text
  252.      * @param i the zero-relative index of the column
  253.      * @param pixels the new width of the column
  254.      * @see #getHeading
  255.      */
  256.     public void setHeading(String h, int i, int pixels)
  257.     {
  258.         headings[i] = h;
  259.         splitters[i+1] = pixels;
  260.         if (columnSizes == null)
  261.             columnSizes = new int[headings.length];
  262.  
  263.         // Update all; the user might not set headings in sequence 0 to n
  264.  
  265.         for (int j = 0; j < headings.length; j++)
  266.             columnSizes[j] = splitters[j + 1] - splitters[j];
  267.  
  268.         //Set forceRedraw so contents of offscreen is updated
  269.         forceRedraw = true;
  270.         repaint();
  271.     }
  272.  
  273.     /**
  274.      * Returns the heading text of the specified column.
  275.      * @see #setHeading
  276.      */
  277.     public String getHeading(int i)
  278.     {
  279.         return headings[i];
  280.     }
  281.  
  282.     /**
  283.      * Adjusts the columns to default width.
  284.      */
  285.     public void adjustHeadings()
  286.     {
  287.         if (headings.length == 0) {
  288.             createColumns(0);
  289.             invalidate();
  290.         } else {
  291.             Dimension d = size();
  292.             int w = 0, width = (d.width-sbVWidth)/ headings.length, r = (d.width-sbVWidth) - headings.length*width;
  293.             splitters[0] = 0;
  294.             for (int i = 0; i < headings.length; ++i)
  295.                 splitters[i+1] = w += width+(i<r?1:0);
  296.         }
  297.     }
  298.  
  299.     /**
  300.      * For each string in the given list, create a column, use the string as
  301.      * its heading, and give it a default width.
  302.      * @param list array of heading strings
  303.      * @see #getHeadings
  304.      */
  305.     public void setHeadings(String[] list)
  306.     {
  307.         //Handle String Arrays as well as array of one string with ; separators
  308.         list = tokenizeStringArrayIfNeeded(list);
  309.  
  310.         if (list.length == 0)
  311.         {
  312.             createColumns(0);
  313.             invalidate();
  314.         }
  315.         else
  316.         {
  317.             calcHeadings(list);
  318.         }
  319.  
  320.         //Set forceRedraw so contents of offscreen is updated
  321.         forceRedraw = true;
  322.         repaint();
  323.     }
  324.  
  325.     /**
  326.      * Get a String array of all of the column headings.
  327.      * @return an array of all of the current headings
  328.      * @see #setHeadings
  329.      */
  330.     public String[] getHeadings()
  331.     {
  332.         return headings;
  333.     }
  334.  
  335.     /**
  336.      * Sets the font of the heading text.
  337.      * @param f font instance for heading text
  338.      * @see #getHeadingFont
  339.      */
  340.     public void setHeadingFont(Font f)
  341.     {
  342.         headingFont = f;
  343.         headingHeight = getFontMetrics(f).getHeight() + 4;
  344.  
  345.         //Set forceRedraw so contents of offscreen is updated
  346.         forceRedraw = true;
  347.         repaint();
  348.     }
  349.  
  350.     /**
  351.      * Returns the font of the heading text.
  352.      * @see #setHeadingFont
  353.      */
  354.     public Font getHeadingFont()
  355.     {
  356.         return headingFont;
  357.     }
  358.  
  359.     /**
  360.      * Sets the font used for text in all the cells.
  361.      * @param f the font for cell text
  362.      * @see #getCellFont
  363.      */
  364.     public void setCellFont(Font f)
  365.     {
  366.         cellFont = f;
  367.         cellAscent = getFontMetrics(f).getAscent();
  368.         cellDescent = getFontMetrics(f).getDescent();
  369.         cellHeight = getFontMetrics(f).getHeight();
  370.  
  371.         //Set forceRedraw so contents of offscreen is updated
  372.         forceRedraw = true;
  373.         repaint();
  374.     }
  375.  
  376.     /**
  377.      * Returns the font currently used for text in all the cells.
  378.      * @see #setCellFont
  379.      */
  380.     public Font getCellFont()
  381.     {
  382.         return cellFont;
  383.     }
  384.  
  385.     /**
  386.      * Sets the column heading foreground and background colors.
  387.      * @param fg foreground Color for heading text
  388.      * @param bg background Color for heading text
  389.      * @see #setHeadingFg
  390.      * @see #setHeadingBg
  391.      */
  392.     public void setHeadingColors(Color fg, Color bg)
  393.     {
  394.         headingFg = fg;
  395.         headingBg = bg;
  396.  
  397.         //Set forceRedraw so contents of offscreen is updated
  398.         forceRedraw = true;
  399.         repaint();
  400.     }
  401.  
  402.     /**
  403.      * Sets the column heading foreground color.
  404.      * @param c foreground Color for heading text
  405.      * @see #getHeadingFg
  406.      * @see #setHeadingBg
  407.      * @see #setHeadingColors
  408.      */
  409.     public void setHeadingFg(Color c)
  410.     {
  411.         headingFg = c;
  412.  
  413.         //Set forceRedraw so contents of offscreen is updated
  414.         forceRedraw = true;
  415.         repaint();
  416.     }
  417.  
  418.     /**
  419.      * Sets the column heading background color.
  420.      * @param c backgroun Color for heading text
  421.      * @see #getHeadingBg
  422.      * @see #setHeadingFg
  423.      * @see #setHeadingColors
  424.      */
  425.     public void setHeadingBg(Color c)
  426.     {
  427.         headingBg = c;
  428.  
  429.         //Set forceRedraw so contents of offscreen is updated
  430.         forceRedraw = true;
  431.         repaint();
  432.     }
  433.  
  434.     /**
  435.      * Returns the color of the column heading foreground.
  436.      * @return current column heading foreground color
  437.      * @see #setHeadingFg
  438.      */
  439.     public Color getHeadingFg()
  440.     {
  441.         return headingFg;
  442.     }
  443.  
  444.     /**
  445.      * Returns the color of the column heading background.
  446.      * @return current column heading background color
  447.      * @see #setHeadingBg
  448.      */
  449.     public Color getHeadingBg()
  450.     {
  451.         return headingBg;
  452.     }
  453.  
  454.     /**
  455.      * Sets foreground and background colors of cell text.
  456.      * @param fg foreground color of cell text
  457.      * @param bg background color of cell text
  458.      * @see #setCellFg
  459.      * @see #setCellBg
  460.      */
  461.     public void setCellColors(Color fg, Color bg)
  462.     {
  463.         colorFg = fg;
  464.         colorBg = bg;
  465.  
  466.         //Set forceRedraw so contents of offscreen is updated
  467.         forceRedraw = true;
  468.         repaint();
  469.     }
  470.  
  471.     /**
  472.      * Sets the foreground color of cell text.
  473.      * @param c foreground Color of cell text
  474.      * @see #getCellFg
  475.      * @see #setCellBg
  476.      * @see #setCellColors
  477.      */
  478.     public void setCellFg(Color c)
  479.     {
  480.         colorFg = c;
  481.  
  482.         //Set forceRedraw so contents of offscreen is updated
  483.         forceRedraw = true;
  484.         repaint();
  485.     }
  486.  
  487.     /**
  488.      * Sets the background color of cell text.
  489.      * @param c background Color of cell text
  490.      * @see #getCellBg
  491.      * @see #setCellFg
  492.      * @see #setCellColors
  493.      */
  494.     public void setCellBg(Color c)
  495.     {
  496.         colorBg = c;
  497.  
  498.         //Set forceRedraw so contents of offscreen is updated
  499.         forceRedraw = true;
  500.         repaint();
  501.     }
  502.  
  503.     /**
  504.      * Gets cell foreground color.
  505.      * @return the current cell foreground color
  506.      * @see #setCellFg
  507.      */
  508.     public Color getCellFg()
  509.     {
  510.         return colorFg;
  511.     }
  512.  
  513.     /**
  514.      * Gets cell background color.
  515.      * @return the current cell background color
  516.      * @see #setCellBg
  517.      */
  518.     public Color getCellBg()
  519.     {
  520.         return colorBg;
  521.     }
  522.  
  523.     /**
  524.      * Gets a string array containing the alignments of each of the columns.
  525.      * The possible values for each element are "Left", "Center", or "Right".
  526.      * @return an array of strings containing the alignment for each column.
  527.      * @see #setColumnAlignments
  528.      */
  529.     public String[] getColumnAlignments()
  530.     {
  531.         String[] list = new String[align.length];
  532.  
  533.         for (int i = 0; i < list.length; i++)
  534.         {
  535.             if (align[i] == LEFT)
  536.                 list[i] = "Left";
  537.             else if (align[i] == CENTER)
  538.                 list[i] = "Center";
  539.             else if (align[i] == RIGHT)
  540.                 list[i] = "Right";
  541.             else
  542.                 list[i] = "Left";
  543.         }
  544.  
  545.         return list;
  546.     }
  547.  
  548.     /**
  549.      * Sets the the alignments of each of the columns.
  550.      * @param list each item should contain one of the following: "Left", "Center", or "Right"
  551.      * @see #getColumnAlignments
  552.      */
  553.     public void setColumnAlignments(String[] list)
  554.     {
  555.         //Handle String Arrays as well as array of one string with ; separators
  556.         list = tokenizeStringArrayIfNeeded(list);
  557.  
  558.         if (list.length > 0)
  559.         {
  560.             // Initialize the columnSizes array again
  561.             align = new int[list.length];
  562.  
  563.             for (int i = 0; i < list.length; i++)
  564.             {
  565.                 if (list[i] != null)
  566.                 {
  567.                     if (list[i].equalsIgnoreCase("Left"))
  568.                         align[i] = LEFT;
  569.                     else if (list[i].equalsIgnoreCase("Center"))
  570.                         align[i] = CENTER;
  571.                     else if (list[i].equalsIgnoreCase("Right"))
  572.                         align[i] = RIGHT;
  573.                     else
  574.                         align[i] = LEFT;
  575.                 }
  576.                 else
  577.                     align[i] = LEFT;
  578.             }
  579.         }
  580.         else
  581.         {
  582.             // Initialize the columnSizes array again
  583.             align = new int[headings.length];
  584.  
  585.             for (int i = 0; i < headings.length; i++)
  586.                 align[i] = LEFT;
  587.         }
  588.     }
  589.  
  590.     /**
  591.      * Returns a string array containing the sizes of each of the columns in pixels.
  592.      * @see #setColumnSizes
  593.      */
  594.     public String[] getColumnSizes()
  595.     {
  596.         String[] list = new String[columnSizes.length];
  597.  
  598.         for (int i = 0; i < list.length; i++)
  599.         {
  600.             try
  601.             {
  602.                 list[i] = String.valueOf(columnSizes[i]);
  603.             }
  604.  
  605.             catch (Exception e)
  606.             {
  607.                 list[i] = "";
  608.             }
  609.         }
  610.  
  611.         return list;
  612.     }
  613.  
  614.     /**
  615.      * Sets the the sizes of each of the columns.
  616.      * @param list a string array containing column sizes in pixels
  617.      * @see #getColumnSizes
  618.      */
  619.     public void setColumnSizes(String[] list)
  620.     {
  621.         //Handle String Arrays as well as array of one string with ; separators
  622.         list = tokenizeStringArrayIfNeeded(list);
  623.  
  624.         if (list.length > 0)
  625.         {
  626.             // Initialize the columnSizes array again
  627.             columnSizes = new int[list.length];
  628.  
  629.             int total = 0;
  630.  
  631.             for (int i = 0; i < list.length; i++)
  632.             {
  633.                 try
  634.                 {
  635.                     if (list[i] != null)
  636.                     {
  637.                         columnSizes[i] = Integer.parseInt(list[i]);
  638.  
  639.                         if (splitters.length >= i)
  640.                             splitters[i+1] = total += columnSizes[i];
  641.                     }
  642.                 }
  643.  
  644.                 catch (Exception e)
  645.                 {
  646.                     columnSizes[i] = 10;
  647.  
  648.                     if (splitters.length >= i)
  649.                         splitters[i+1] = total += 10;
  650.                 }
  651.             }
  652.  
  653.             splitters[0] = 0;
  654.         }
  655.         else
  656.         {
  657.             columnSizes = null;
  658.             adjustHeadings();
  659.         }
  660.     }
  661.     
  662.     /**
  663.      * Get the column size in pixels for the specified column.
  664.      * @param i the zero-relative column index
  665.      */
  666.     public int getColumnSize(int i)
  667.     {
  668.         return splitters[i + 1];
  669.     }
  670.     
  671.     /**
  672.      * Set the justification of the text for the specified column.
  673.      * @param i the zero-relative column index
  674.      * @param align one of the values LEFT, CENTER, or RIGHT
  675.      */
  676.     public void setColumnAlignment(int i, int align)
  677.     {
  678.         // align must be LEFT, CENTER, or RIGHT
  679.         this.align[i] = align;
  680.     }
  681.     
  682.     /**
  683.      * Returns the current number of columns.
  684.      */
  685.     public int getNumberOfCols()
  686.     {
  687.         return headings.length;
  688.     }
  689.     
  690.     /**
  691.      * Sets the specified row as the selected row.
  692.      * @param r the zero-relative index of the row to select
  693.      * @see #getSelectedRow
  694.      */
  695.     public void setSelectedRow(int r)
  696.         throws IllegalArgumentException
  697.     {
  698.         if (selectedRow != r)
  699.         {
  700.             if (r > cells.rows())
  701.             {
  702.                 throw new IllegalArgumentException(r + " is not a valid row number");
  703.             }
  704.             
  705.             selectedRow = r;
  706.             
  707.             changeSelection(selectedRow, 0);
  708.         }
  709.     }
  710.     
  711.     /**
  712.      * Returns the zero-relative index of the currently selected row.
  713.      * @see #setSelectedRow
  714.      */
  715.     public int getSelectedRow()
  716.     {
  717.         //???RKM??? This seems wrong since there are other interfaces to set the selected row
  718.         return selectedRow;
  719.     }
  720.  
  721.     /**
  722.      * Returns an array of integer indexes representing all highlighted rows.
  723.      * @return the zero-relative indexes of the selected rows.
  724.      * May return null (no rows selected)
  725.      * @see #getSelectedRow
  726.      * @see #setSelectedRow
  727.      */
  728.     public int[] getSelectedRows()
  729.     {
  730.         int size = highlightedRows.size();
  731.         int count = 0;
  732.         
  733.         for (int i = 0; i < size; i++)
  734.         {
  735.             if (highlightedRows.get(i))
  736.             {
  737.                 count++;
  738.             }
  739.         }
  740.  
  741.         int[] selections = new int[count];
  742.         count = 0;
  743.  
  744.         for (int i = 0; i < size; i++)
  745.         {
  746.             if (highlightedRows.get(i))
  747.             {
  748.                 selections[count++] = i;
  749.             }
  750.         }
  751.  
  752.         return selections;
  753.     }
  754.  
  755.     /**
  756.      * Sets the minimum allowable column width.
  757.      * @param size the minimum column width in pixels
  758.      * @see #getMinColumnWidth
  759.      */
  760.     public void setMinColumnWidth(int size)
  761.     {
  762.         if (size > 0)
  763.             minColumnWidth = size;
  764.     }
  765.  
  766.     /**
  767.      * Returns the minimum allowable column width in pixels.
  768.      * @see #setMinColumnWidth
  769.      */
  770.     public int getMinColumnWidth()
  771.     {
  772.         return minColumnWidth;
  773.     }
  774.  
  775.     /**
  776.      * Processes MOUSE_DOWN events.
  777.      * This is a standard Java AWT method which gets called by the AWT
  778.      * method handleEvent() in response to receiving a MOUSE_DOWN
  779.      * event. These events occur when the mouse button is pressed while
  780.      * inside this component.
  781.      * 
  782.      * @param e the event
  783.      * @param x the component-relative horizontal coordinate of the mouse
  784.      * @param y the component-relative vertical coordinate of the mouse
  785.      * 
  786.      * @return always true since the event was handled
  787.      * 
  788.      * @see #mouseUp
  789.      * @see #handleEvent
  790.      */
  791.     public boolean mouseDown(Event e, int x, int y)
  792.     {
  793.         if (y < headingHeight)
  794.         {
  795.             // It's a click on the heading area:
  796.             // is it close enough to be a column size drag?
  797.             for (int i=1; i<splitters.length; i++)
  798.             {
  799.                 if ((x < Math.min(splitters[i]-sbHPosition + clickMargin, size().width-sbVWidth)) &&
  800.                     (x > splitters[i]-sbHPosition - clickMargin))
  801.                 {
  802.                     dragColumn = i;
  803.                     isDragging = true;
  804.                     mouseDrag(e, x, y); //draw drag line immediately
  805.                     return true;
  806.                 }
  807.             }
  808.  
  809.             // check for sort button push
  810.             for (int i = 0; i < headings.length; i++)
  811.             {
  812.                 //int max = i == headings.length - 1 ? size().width : splitters[i+1]-sbHPosition;
  813.  
  814.                 if ((x > splitters[i]-sbHPosition) && (x < splitters[i+1]-sbHPosition))
  815.                 {
  816.                    colClick = i;
  817.                    drawHeading(true);
  818.                    repaint();
  819.                    return true;
  820.                 }
  821.             }
  822.             return true;
  823.         }
  824.  
  825.         //else it's a click in the row area
  826.         if (x > width)
  827.         {
  828.             //woops, might have hit the scrollbar
  829.             return false;
  830.         }
  831.  
  832.         int oldRowSelect = selectedRow;  //save off the old selection
  833.  
  834.         // set new row selection
  835.         changeSelection(((int) (y - headingHeight - 4)/cellHeight)+topRow, e.modifiers);
  836.  
  837.         // detect a double click
  838.         if ((selectedRow == oldRowSelect) || (oldRowSelect < 0))
  839.         { // if clicked on same row track time elapsed since last mouseDown
  840.             long clickSpeed = e.when - clickTime;
  841.  
  842.             if (clickSpeed < CLICKTHRESHOLD)
  843.             {
  844.                 //action event
  845.                 postEvent(Event.ACTION_EVENT, selectedRow);
  846.             }
  847.         }
  848.  
  849.         clickTime  = e.when;
  850.  
  851.         if (e.modifiers == Event.META_MASK)
  852.         {
  853.             //right mouse click
  854.         }
  855.  
  856.         return false;
  857.     }
  858.  
  859.     /**
  860.      * Processes MOUSE_DRAG events.
  861.      * This is a standard Java AWT method which gets called by the AWT
  862.      * method handleEvent() in response to receiving a MOUSE_DRAG
  863.      * event. These events occur when the mouse is moved around inside this
  864.      * component while the button is pressed.
  865.      * 
  866.      * @param e the event
  867.      * @param x the component-relative horizontal coordinate of the mouse
  868.      * @param y the component-relative vertical coordinate of the mouse
  869.      * 
  870.      * @return always true since the event was handled
  871.      * 
  872.      * @see #mouseMove
  873.      * @see #handleEvent
  874.      */
  875.     public boolean mouseDrag(Event e, int x, int y)
  876.     {
  877.         Dimension s = size();
  878.  
  879.         if (!isDragging)
  880.         {
  881.             // check for sort button push outside of button
  882.             int max;
  883.             int min;
  884.  
  885.             if (colClick > -1)
  886.             {
  887.                 if (colClick == 0)
  888.                 {
  889.                     min = 0;
  890.                 }
  891.                 else
  892.                 {
  893.                     min = splitters[colClick]-sbHPosition;
  894.                 }
  895.  
  896.                 if (colClick == headings.length - 1)
  897.                 {
  898.                     max = s.width;
  899.                 }
  900.                 else
  901.                 {
  902.                     max = splitters[colClick+1]-sbHPosition;
  903.                 }
  904.  
  905.                 if ((x < min) || (x > max) || (y > headingHeight) || (y < 0))
  906.                 {
  907.                    memoryClick = colClick;
  908.                    colClick = -1;
  909.                    drawHeading(false);
  910.                    repaint();
  911.                 }
  912.             }
  913.             else if (memoryClick > -1)
  914.             {
  915.                 if (memoryClick == 0)
  916.                 {
  917.                     min = 0;
  918.                 }
  919.                 else
  920.                 {
  921.                     min = splitters[memoryClick]-sbHPosition;
  922.                 }
  923.  
  924.                 if (memoryClick == headings.length - 1)
  925.                 {
  926.                     max = s.width;
  927.                 }
  928.                 else
  929.                 {
  930.                     max = splitters[memoryClick+1]-sbHPosition;
  931.                 }
  932.  
  933.                 if ((x > min) && (x < max) && (y < headingHeight) && (y > 0))
  934.                 {
  935.                     colClick = memoryClick;
  936.                     memoryClick = -1;
  937.                     drawHeading(false);
  938.                     repaint();
  939.                 }
  940.             }
  941.  
  942.             return true;
  943.         }
  944.  
  945.         //fix any drag that went off the left side
  946.         if (x < 0)
  947.         {
  948.             x=0;
  949.         }
  950.         if (x+sbHPosition < splitters[dragColumn-1]+minColumnWidth)
  951.             return true;
  952.         //erase prvious drag line and draw in new position
  953.         Graphics gg1 = getGraphics();
  954.         gg1.setColor(colorBg);
  955.         gg1.setXORMode(Color.black);
  956.         gg1.drawLine(xDragLast,0,xDragLast,s.height);
  957.         gg1.drawLine(x, 0, x, s.height);
  958.         gg1.setColor(Color.black);
  959.         gg1.setPaintMode();
  960.         gg1.dispose();
  961.         //save x position of drag line
  962.         xDragLast = x;
  963.         //repaint();
  964.  
  965.         return true;
  966.     }
  967.  
  968.     /**
  969.      * Processes MOUSE_UP events.
  970.      * This is a standard Java AWT method which gets called by the AWT
  971.      * method handleEvent() in response to receiving a MOUSE_UP
  972.      * event. These events occur when the mouse button is released while 
  973.      * inside this component.
  974.      * 
  975.      * @param e the event
  976.      * @param x the component-relative horizontal coordinate of the mouse
  977.      * @param y the component-relative vertical coordinate of the mouse
  978.      * 
  979.      * @return true if the event was handled
  980.      * 
  981.      * @see #mouseDown
  982.      * @see #handleEvent
  983.      */
  984.     public boolean mouseUp(Event e, int x, int y)
  985.     {
  986.         if (isDragging)
  987.         {
  988.             //fix any drag that went off the left side
  989.             if (x < 0)
  990.             {
  991.                 x = 0;
  992.             }
  993.  
  994.             //erase drag line
  995.             //gg.setColor(colorBg);
  996.             //gg.setXORMode(Color.black);
  997.             //gg.drawLine(xDragLast, 0, xDragLast, size().height);
  998.             //gg.setColor(Color.black);
  999.             //gg.setPaintMode();
  1000.             //turn off dragging
  1001.             xDragLast = Math.max(splitters[dragColumn-1]+minColumnWidth,x+sbHPosition);
  1002.             for(int i = dragColumn+1;i<splitters.length;i++)
  1003.                 splitters[i] += xDragLast - splitters[dragColumn];
  1004.             splitters[dragColumn] = xDragLast;
  1005.  
  1006.             xDragLast = -1;
  1007.             isDragging = false;
  1008.  
  1009.             //set column width to dragged position
  1010.  
  1011.             //Set forceRedraw so contents of offscreen is updated
  1012.             forceRedraw = true;
  1013.             repaint();
  1014.  
  1015.             return true;
  1016.         }
  1017.  
  1018.         if (colClick != -1)
  1019.         {
  1020.             // it was a sorting request
  1021.             highlightedRows = new BitSet();
  1022.             selectedRow = -1;
  1023.             cells.sort(func, colClick);
  1024.             colClick = -1;
  1025.  
  1026.             //Set forceRedraw so contents of offscreen is updated
  1027.             forceRedraw = true;
  1028.             repaint();
  1029.  
  1030.             return true;
  1031.         }
  1032.  
  1033.         return false;
  1034.     }
  1035.  
  1036.     /**
  1037.      * Processes MOUSE_MOVE events.
  1038.      * This is a standard Java AWT method which gets called by the AWT
  1039.      * method handleEvent() in response to receiving a MOUSE_MOVE
  1040.      * event. These events occur when the mouse is moved around inside this
  1041.      * component while the button is NOT pressed.
  1042.      * 
  1043.      * @param e the event
  1044.      * @param x the component-relative horizontal coordinate of the mouse
  1045.      * @param y the component-relative vertical coordinate of the mouse
  1046.      * 
  1047.      * @return always true since the event was handled
  1048.      * 
  1049.      * @see #mouseDrag
  1050.      * @see #handleEvent
  1051.      */
  1052.     public boolean mouseMove(Event ev, int x, int y)
  1053.     {
  1054.         boolean isCloseEnough = false;
  1055.  
  1056.         // Use resize cursor ?
  1057.         if (y < headingHeight)
  1058.         {
  1059.             // Moving mouse around in header area
  1060.             // is it close enough to be a column size drag?
  1061.             for (int i=1; i<splitters.length; i++)
  1062.             {
  1063.                 if ((x < Math.min(splitters[i]-sbHPosition + clickMargin, size().width-sbVWidth)) &&
  1064.                     (x > splitters[i]-sbHPosition - clickMargin))
  1065.                 {
  1066.                     isCloseEnough = true;
  1067.                 }
  1068.             }
  1069.         }
  1070.  
  1071.         int newCursor = (isCloseEnough? Frame.W_RESIZE_CURSOR : Frame.DEFAULT_CURSOR);
  1072.  
  1073.         if (newCursor != currentCursor)
  1074.         {
  1075.             Frame f = frame();
  1076.             if (f != null)
  1077.                 f.setCursor(currentCursor = newCursor);
  1078.         }
  1079.  
  1080.         return false;
  1081.     }
  1082.  
  1083.     /**
  1084.      * Processes KEY_PRESS and KEY_ACTION events.
  1085.      * This is a standard Java AWT method which gets called by the AWT
  1086.      * method handleEvent() in response to receiving a KEY_PRESS or
  1087.      * KEY_ACTION event. These events occur when this component has the focus
  1088.      * and the user presses a "normal" or an "action" (F1, page up, etc) key.
  1089.      * 
  1090.      * @param ev the Event
  1091.      * @param key the key that was pressed
  1092.      * @return true if the event was handled
  1093.      * @see java.awt.Component#keyUp
  1094.      * @see #handleEvent
  1095.      */
  1096.     public boolean keyDown(Event ev, int key)
  1097.     {
  1098.  
  1099.         switch (key)
  1100.         {
  1101.             case Event.DOWN:
  1102.             {
  1103.                 if (selectedRow < cells.rows() - 1)
  1104.                 {
  1105.                     changeSelection(selectedRow + 1, ev.modifiers);
  1106.                 }
  1107.  
  1108.                 break;
  1109.             }
  1110.  
  1111.             case Event.PGDN:
  1112.             {
  1113.                 changeSelection(selectedRow + getPageSize(), ev.modifiers);
  1114.                 break;
  1115.             }
  1116.  
  1117.             case Event.UP:
  1118.             {
  1119.                 if (selectedRow > 0)
  1120.                 {
  1121.                     changeSelection(selectedRow - 1, ev.modifiers);
  1122.                 }
  1123.  
  1124.                 break;
  1125.             }
  1126.  
  1127.             case Event.PGUP:
  1128.             {
  1129.                 changeSelection(selectedRow - getPageSize(), ev.modifiers);
  1130.                 break;
  1131.             }
  1132.  
  1133.             case '\n':
  1134.             {
  1135.                 if (selectedRow > -1)
  1136.                 {
  1137.                     postEvent(Event.ACTION_EVENT, selectedRow);
  1138.                 }
  1139.                 break;
  1140.             }
  1141.         }
  1142.  
  1143.         return false;
  1144.     }
  1145.  
  1146.     /**
  1147.      * Processes events for this component.
  1148.      * This is a standard Java AWT method which gets called by the AWT
  1149.      * to handle this component's events. The default handler for 
  1150.      * components dispatches to one of the following methods as needed:
  1151.      * action(), gotFocus(), lostFocus(), keyDown(), keyUp(), mouseEnter(),
  1152.      * mouseExit(), mouseMove(), mouseDrag(), mouseDown(), or mouseUp().
  1153.      *
  1154.      * @param e the event to handle
  1155.      * @return true if the event was handled and no further action is needed,
  1156.      * false to pass the event to this component's parent
  1157.      * @see java.awt.Component#action
  1158.      * @see java.awt.Component#gotFocus
  1159.      * @see java.awt.Component#lostFocus
  1160.      * @see keyDown
  1161.      * @see java.awt.Component#keyUp
  1162.      * @see java.awt.Component#mouseEnter
  1163.      * @see java.awt.Component#mouseExit
  1164.      * @see #mouseMove
  1165.      * @see #mouseDrag
  1166.      * @see #mouseDown
  1167.      * @see #mouseUp
  1168.      */
  1169.     public boolean handleEvent(Event e)
  1170.     {
  1171.         if (e.target == sbV && e.arg != null)
  1172.         {
  1173.             //e.when = System.currentTimeMillis();
  1174.  
  1175.             //if (((e.when - scrollbarTimer) < 300) && (e.id ==Event.SCROLL_ABSOLUTE))
  1176.             //{
  1177.             //    return false;
  1178.             //}
  1179.  
  1180.             //scrollbarTimer = e.when;
  1181.  
  1182.             if (topRow != sbV.getValue())
  1183.             {
  1184.                 topRow = sbV.getValue();
  1185.                 sbVPosition = topRow;
  1186.  
  1187.                 //Set forceRedraw so contents of offscreen is updated
  1188.                 forceRedraw = true;
  1189.                 repaint();
  1190.             }
  1191.         }
  1192.         if (e.target == sbH && e.arg != null)
  1193.         {
  1194.             //e.when = System.currentTimeMillis();
  1195.  
  1196.             //if (((e.when - scrollbarTimer) < 300) && (e.id ==Event.SCROLL_ABSOLUTE))
  1197.             //{
  1198.             //    return false;
  1199.             //}
  1200.  
  1201.             //scrollbarTimer = e.when;
  1202.  
  1203.             if (sbHPosition != sbH.getValue())
  1204.             {
  1205.                 sbHPosition = sbH.getValue();
  1206.  
  1207.                 //Set forceRedraw so contents of offscreen is updated
  1208.                 //forceRedraw = true;
  1209.                 //originX = -sbH.getValue();
  1210.                 repaint();
  1211.             }
  1212.         }
  1213.         return super.handleEvent(e);
  1214.     }
  1215.  
  1216.     /**
  1217.      * Creates the specified number of columns in the MultiList.
  1218.      * @param i the number of columns to create
  1219.      */
  1220.     public void createColumns(int i)
  1221.     {
  1222.         headings = new String[i];
  1223.         align = new int[i];
  1224.         splitters = new int[i+1];
  1225.         cellIm = new Image[i];
  1226.         columnSizes = null;
  1227.     }
  1228.  
  1229.     /**
  1230.      * Removes all rows from the MultiList.
  1231.      */
  1232.     public void clear()
  1233.     {
  1234.         cells.removeAllElements();
  1235.         xDragLast = -1;
  1236.         isDragging = false;
  1237.         selectedRow = 0;
  1238.         highlightedRows = new BitSet();
  1239.         topRow = 0;
  1240.         sbVPosition = 0;
  1241.  
  1242.         //Set forceRedraw so contents of offscreen is updated
  1243.         forceRedraw = true;
  1244.         repaint();
  1245.     }
  1246.  
  1247.     /**
  1248.      * Remove a row from the MultiList.
  1249.      * @param row the zero-relatve row index to remove
  1250.      */
  1251.     public void removeRow(int row)
  1252.     {
  1253.         //If we don't have a selection, return
  1254.         if (row == -1)
  1255.             return;
  1256.  
  1257.         int numRows = cells.rows();
  1258.  
  1259.         //Remove the row from the matrix
  1260.         cells.removeRow(row);
  1261.  
  1262.         //If the row is selected, remove it from the selection
  1263.         if (highlightedRows.get(row))
  1264.             highlightedRows.clear(row);
  1265.  
  1266.         //Move selections down
  1267.         for (int i = row + 1;i < numRows;i++)
  1268.         {
  1269.             if (highlightedRows.get(i))
  1270.             {
  1271.                 highlightedRows.clear(i);
  1272.                 highlightedRows.set(i - 1);
  1273.             }
  1274.         }
  1275.  
  1276.         //Set forceRedraw so contents of offscreen is updated
  1277.         forceRedraw = true;
  1278.         repaint();
  1279.     }
  1280.  
  1281.     /**
  1282.      * Adds or changes the text of cell at the given row and column position.
  1283.      * @param r the zero-relatve row index
  1284.      * @param c the zero-relatve column index
  1285.      * @param s the new cell text
  1286.      * @see #addImageCell
  1287.      * @see #addCell
  1288.      */
  1289.     public void addTextCell(int r, int c, String s)
  1290.     {
  1291.         Cell cell = new Cell(this, s);
  1292.         cells.updateElement(r, c, cell);
  1293.  
  1294.         //Set forceRedraw so contents of offscreen is updated
  1295.         forceRedraw = true;
  1296.         repaint();
  1297.     }
  1298.  
  1299.     /**
  1300.      * Adds or changes the image of a cell at the given row and column position.
  1301.      * @param r the zero-relatve row index
  1302.      * @param c the zero-relatve column index
  1303.      * @param i the new cell image
  1304.      * @see #addTextCell
  1305.      * @see #addCell
  1306.      */
  1307.     public void addImageCell(int r, int c, Image i)
  1308.     {
  1309.         Cell cell = new Cell(this, i);
  1310.         cells.updateElement(r, c, cell);
  1311.  
  1312.         //Set forceRedraw so contents of offscreen is updated
  1313.         forceRedraw = true;
  1314.         repaint();
  1315.     }
  1316.  
  1317.     /**
  1318.      * Adds or changes contents of a cell, both text and image.
  1319.      * @param r the zero-relatve row index
  1320.      * @param c the zero-relatve column index
  1321.      * @parma s the new cell text
  1322.      * @param i the new cell image
  1323.      * @see #addTextCell
  1324.      * @see #addImageCell
  1325.      */
  1326.     public void addCell(int r, int c, String s, Image i)
  1327.     {
  1328.         Cell cell = new Cell(this, s, i);
  1329.         cells.updateElement(r, c, cell);
  1330.  
  1331.         //Set forceRedraw so contents of offscreen is updated
  1332.         forceRedraw = true;
  1333.         repaint();
  1334.     }
  1335.  
  1336.     /**
  1337.      * Returns the text of the specified cell.
  1338.      * @param r the zero-relatve row index
  1339.      * @param c the zero-relatve column index
  1340.      * @return the cell text or the empty string ("") if the cell is not allocated
  1341.      * @see #getCellImage
  1342.      */
  1343.     public String getCellText(int r, int c)
  1344.     {
  1345.         try
  1346.         {
  1347.             //Calling elementAt will throw if the element is not allocated
  1348.             Cell cell = (Cell)cells.elementAt(r,c);
  1349.  
  1350.             //The element may be allocated, but that doesn't mean there's an object there
  1351.             if (cell != null)
  1352.                 return cell.text;
  1353.         }
  1354.         catch(ArrayIndexOutOfBoundsException e)
  1355.         {
  1356.             //Fall through, so an empty string is returned
  1357.         }
  1358.  
  1359.         return "";
  1360.     }
  1361.  
  1362.     /**
  1363.      * Returns the image of the specified cell, if any.
  1364.      * @param r the zero-relatve row index
  1365.      * @param c the zero-relatve column index
  1366.      * @return the cell image or null if the cell is not allocated
  1367.      * @see #getCellText
  1368.      */
  1369.     public Image getCellImage(int r, int c)
  1370.     {
  1371.         try
  1372.         {
  1373.             //Calling elementAt will throw if the element is not allocated
  1374.             Cell cell = (Cell)cells.elementAt(r,c);
  1375.  
  1376.             //The element may be allocated, but that doesn't mean there's an object there
  1377.             if (cell != null)
  1378.                 return cell.im;
  1379.         }
  1380.         catch(ArrayIndexOutOfBoundsException e)
  1381.         {
  1382.             //Fall through, so null is returned
  1383.         }
  1384.  
  1385.         return null;
  1386.     }
  1387.  
  1388.     /**
  1389.      * Initializes all the MultiList cells with text. Each string in the provided
  1390.      * array initializes one row. The contents of each column are separated by
  1391.      * ";". For example, the string "col0;col1;col2" would result in "col0"
  1392.      * being placed in column 0, "col1" in column 1, and "col2" in column 2.
  1393.      * Any remaining columns will get cleared.
  1394.      * @param items the String array used to initialize all cell contents
  1395.      * @see #getListItems
  1396.      */
  1397.     public void setListItems(String[] items)
  1398.     {
  1399.         clear();
  1400.         for (int row = 0; row < items.length; row++)
  1401.         {
  1402.             String s = items[row];
  1403.  
  1404.             // Make sure that we handle null strings by creating an empty string in it's place
  1405.             if (s == null)
  1406.                 s = "";
  1407.  
  1408.             int len = s.length();
  1409.             int start, end, col;
  1410.  
  1411.             for (end = col = start = 0; end <= len && col < headings.length; ++end)
  1412.             {
  1413.                 if (end == len || s.charAt(end) == ';')
  1414.                 {
  1415.                     addCell(row, col++, s.substring(start, end), null);
  1416.                     start = end + 1;
  1417.                 }
  1418.            }
  1419.            while (col < headings.length)
  1420.                addCell(row, col++, "", null);
  1421.         }
  1422.  
  1423.         //Set forceRedraw so contents of offscreen is updated
  1424.         forceRedraw = true;
  1425.         repaint();
  1426.     }
  1427.  
  1428.     /**
  1429.      * Returns the text contents of all the cells as a string array. A new
  1430.      * string is used for each row. The contents of each row's column are
  1431.      * separated by ";". For example, the string "col0;;col2" would result
  1432.      * from a "col0" in column 0, an empty column 1, and  "col2" in column 2.
  1433.      * @return the String array containing the text of all the cells
  1434.      * @see #setListItems
  1435.      */
  1436.     public String[] getListItems()
  1437.     {
  1438.         Vector v = new Vector();
  1439.         String s;
  1440.         boolean lastNull = false;
  1441.         int row, col;
  1442.         String rowString;
  1443.         String[] items = new String[cells.rows()];
  1444.  
  1445.         for (row = 0; row < cells.rows(); row++) {
  1446.             rowString = "";
  1447.  
  1448.             for (col = 0; col < headings.length; col++) {
  1449.                 s = getCellText(row, col);
  1450.                 if (col != 0) rowString += ";";
  1451.                 rowString += (s != null) ? s : "";
  1452.             }
  1453.  
  1454.             items[row] = rowString;
  1455.         }
  1456.  
  1457.         return items;
  1458.     }
  1459.  
  1460.     /**
  1461.      * Handles redrawing of this component on the screen.
  1462.      * This is a standard Java AWT method which gets called by the Java
  1463.      * AWT (repaint()) to handle repainting this component on the screen.
  1464.      * The graphics context clipping region is set to the bounding rectangle
  1465.      * of this component and its <0,0> coordinate is this component's 
  1466.      * top-left corner.
  1467.      * Typically this method paints the background color to clear the
  1468.      * component's drawing space, sets graphics context to be the foreground
  1469.      * color, and then calls paint() to draw the component.
  1470.      * 
  1471.      * It is overridden here to eliminate the unneeded repainting of the background.
  1472.      *
  1473.      * @param g the graphics context
  1474.      * @see java.awt.Component#repaint
  1475.      * @see #paint
  1476.      */
  1477.     public void update(Graphics g)
  1478.     {
  1479.         paint(g);
  1480.     }
  1481.  
  1482.     /**
  1483.      * Paints this component using the given graphics context.
  1484.      * This is a standard Java AWT method which typically gets called
  1485.      * by the AWT to handle painting this component. It paints this component
  1486.      * using the given graphics context. The graphics context clipping region
  1487.      * is set to the bounding rectangle of this component and its <0,0>
  1488.      * coordinate is this component's top-left corner.
  1489.      * 
  1490.      * @param g the graphics context used for painting
  1491.      * @see java.awt.Component#repaint
  1492.      * @see #update
  1493.      */
  1494.     public void paint(Graphics g)
  1495.     {
  1496.         // we can enter here without an image if the multilist is created on
  1497.         // a panel without a peer (i.e., a tab panel).  Just to make sure, create
  1498.         // it if we need one...
  1499.  
  1500.         if (im == null) redraw();
  1501.  
  1502.         if (im != null)
  1503.         {
  1504.             Font f = g.getFont();
  1505.             if (f == null)
  1506.             {
  1507.                 f = headingFont;
  1508.                 setFont(f);
  1509.                 return;
  1510.             }
  1511.  
  1512.               Dimension s = size();
  1513.  
  1514.             if (width != s.width || height != s.height || forceRedraw || symantec.beans.Beans.isDesignTime())
  1515.             {
  1516.                 redraw();
  1517.             }
  1518.             g.translate(-sbHPosition, 0);
  1519.             if (sbVShow && sbHShow)
  1520.             {
  1521.                 g.setColor(Color.lightGray);
  1522.                 g.fillRect(sbHPosition+s.width-sbVWidth, s.height-sbHHeight, sbVWidth, sbHHeight);
  1523.             }
  1524.             g.clipRect(sbHPosition,0,s.width-sbVWidth,s.height-sbHHeight);
  1525.             g.drawImage(im, 0, 0, this);
  1526.             g.setColor(Color.black);
  1527.             g.drawRect(sbHPosition,0, s.width-1, s.height-1);
  1528.         }
  1529.     }
  1530.  
  1531.     public void redraw()
  1532.     {
  1533.         Dimension s = size();
  1534.  
  1535.         //Set redraw to false
  1536.         forceRedraw = false;
  1537.  
  1538.         if (s.width == 0 || s.height == 0)
  1539.             return;
  1540.  
  1541.         //Handle delayed headings calculations
  1542.         if (needDelayedHeadingsCalc)
  1543.             calcHeadings(delayedHeadings);
  1544.  
  1545.         if (cellHeight > 0 && (cells.rows() * cellHeight) > s.height-headingHeight)
  1546.         {
  1547.             sbVShow = true;
  1548.             sbVWidth = sbV.preferredSize().width;
  1549.         }
  1550.         else
  1551.         {
  1552.             sbVShow = false;
  1553.             sbVWidth = 0;
  1554.             sbVPosition = 0;
  1555.         }
  1556.  
  1557.         if ((width != s.width-sbVWidth)&&(columnSizes == null)) adjustHeadings();
  1558.  
  1559.         if (splitters[splitters.length-1] > s.width-sbVWidth)
  1560.         {
  1561.             sbHShow = true;
  1562.             sbHHeight = sbH.preferredSize().height;
  1563.         }
  1564.         else
  1565.         {
  1566.             sbHShow = false;
  1567.             sbHHeight = 0;
  1568.             sbHPosition = 0;
  1569.         }
  1570.  
  1571.         if (width != s.width-sbVWidth || height != s.height || gg == null || (im.getWidth(this) != splitters[splitters.length-1]))
  1572.         {
  1573.             im = createImage(Math.max(splitters[splitters.length-1], s.width+sbHPosition), s.height);
  1574.             width = s.width-sbVWidth;
  1575.             height = s.height;
  1576.  
  1577.             //If there previously was a graphics, dispose it
  1578.             if (gg != null)
  1579.             {
  1580.                 gg.dispose();
  1581.                 gg = null;
  1582.             }
  1583.  
  1584.             if (im != null)
  1585.                 gg = im.getGraphics();
  1586.         }
  1587.  
  1588.         if (gg != null)
  1589.         {
  1590.             gg.setColor(colorBg);
  1591.             gg.fillRect(0,0,Math.max(splitters[splitters.length-1], s.width+sbHPosition), s.height);
  1592.         }
  1593.  
  1594.         if (sbVShow)
  1595.         {
  1596.             int vis = (height - headingHeight) / cellHeight;
  1597.  
  1598.             sbV.reshape(s.width-sbVWidth,0,sbVWidth,s.height-sbHHeight);
  1599.             sbV.setValues(sbVPosition, vis, 0, cells.rows() - vis);
  1600.             sbV.setPageIncrement(1);
  1601.             sbV.show();
  1602.         }
  1603.         else
  1604.         {
  1605.             topRow = 0;
  1606.             sbV.hide();
  1607.         }
  1608.  
  1609.         if(sbHShow)
  1610.         {
  1611.             sbH.reshape(0,s.height-sbHHeight,s.width-sbVWidth,sbHHeight);
  1612.             sbH.setValues(sbHPosition, s.width-sbVWidth, 0, splitters[splitters.length-1] - s.width + sbVWidth);
  1613.             sbH.setPageIncrement(1);
  1614.             sbH.show();
  1615.         }
  1616.         else
  1617.         {
  1618.             sbH.hide();
  1619.         }
  1620.  
  1621.         if (gg != null)
  1622.         {
  1623.             gg.setFont(headingFont);
  1624.             drawHeading(false);
  1625.             gg.clipRect(0,0,splitters[splitters.length-1], s.height);
  1626.         }
  1627.  
  1628.         int count = topRow + (cellHeight > 0 ? (int)((height-3)/cellHeight) - 1 : 0);
  1629.  
  1630.         if (count > cells.rows())
  1631.         {
  1632.             count = cells.rows();
  1633.         }
  1634.  
  1635.         if (gg != null)
  1636.         {
  1637.             gg.setFont(cellFont);
  1638.             drawRows(0, count, topRow);
  1639.         }
  1640.     }
  1641.  
  1642.     /**
  1643.      * Moves and/or resizes this component.
  1644.      * This is a standard Java AWT method which gets called to move and/or 
  1645.      * resize this component. Components that are in containers with layout
  1646.      * managers should not call this method, but rely on the layout manager
  1647.      * instead.
  1648.      * 
  1649.      * @param x horizontal position in the parent's coordinate space
  1650.      * @param y vertical position in the parent's coordinate space
  1651.      * @param width the new width
  1652.      * @param height the new height
  1653.      */
  1654.     public synchronized void reshape(int x, int y, int width, int height)
  1655.     {
  1656.         // Inherited
  1657.         super.reshape(x,y,width,height);
  1658.  
  1659.         // Now adjust the column headings
  1660.         if (columnSizes == null)
  1661.             adjustHeadings();
  1662.     }
  1663.  
  1664.     void drawRows(int r, int count, int cellRow)
  1665.     {
  1666.         MatrixEnumeration    e = cells.elements();
  1667.         Cell                c = null;
  1668.         int                    x, w;
  1669.         int                    cols = headings.length;
  1670.  
  1671.         if (cellRow > 0)
  1672.         {
  1673.             c  = (Cell)e.advanceTo(cellRow);
  1674.         }
  1675.  
  1676.         //iterate the rows and paint each one
  1677.         while (e.hasMoreElements() || c != null)
  1678.         {
  1679.             x = 1;
  1680.             //iterate the cols of the current row
  1681.             for (int i=0; i<cols; i++)
  1682.             {
  1683.                 //if (i < cols-1)
  1684.                 //{
  1685.                     w = splitters[i+1] - splitters[i];
  1686.                 //}
  1687.                 //else
  1688.                 //{
  1689.                 //    w = size().width - splitters[i]-4;
  1690.                 //}
  1691.  
  1692.                 //x = i==0 ?1  :splitters[i]-3;
  1693.                 gg.setColor(highlightedRows.get(cellRow)
  1694.                             ?colorHBg
  1695.                             :colorBg);
  1696.                 gg.fillRect(x, headingHeight + r*cellHeight+4, w+4, cellHeight);
  1697.                 gg.setColor(highlightedRows.get(cellRow)?colorHFg :colorFg);
  1698.  
  1699.                 if (c == null)
  1700.                 {
  1701.                     c = (Cell)e.nextElement();
  1702.                 }
  1703.  
  1704.                 if ((c != null) && (e.currRow() == cellRow) && (e.currCol() == i))
  1705.                 {
  1706.                     c.drawCell(gg, align[i], splitters[i]+3,
  1707.                               headingHeight + r*cellHeight + 4, w, cellHeight, cellAscent);
  1708.                     c = null;
  1709.                 }
  1710.  
  1711.                 if (c != null && e.currRow() < cellRow)
  1712.                 {
  1713.                     c = null;
  1714.                 }
  1715.                 x = splitters[i+1]-3;
  1716.             }
  1717.  
  1718.             if (--count == 0)
  1719.             {
  1720.                 break;
  1721.             }
  1722.  
  1723.             cellRow++;
  1724.             r++;
  1725.         }
  1726.     }
  1727.  
  1728.     boolean postEvent(int id, int num)
  1729.     {
  1730.         return postEvent(new Event(this, id, new Integer(num)));
  1731.     }
  1732.  
  1733.     int getPageSize()
  1734.     {
  1735.         return ((int)(size().height/cellHeight));
  1736.     }
  1737.  
  1738.     void draw3DBox(Rectangle r, boolean up)
  1739.     {
  1740.         int x0 = r.x;
  1741.         int y0 = r.y;
  1742.         int x1 = r.x + r.width;
  1743.         int y1 = r.y + r.height;
  1744.  
  1745.         gg.setColor(up?Color.black: Color.white);
  1746.         gg.drawLine(x1,   y0, x1,   y1); //right
  1747.         gg.drawLine(x1+1, y0, x1+1, y1); //right
  1748.  
  1749.         gg.drawLine(x0,y1,x1,y1);        //bottom
  1750.         gg.drawLine(x0,y1+1, x1, y1+1);  //bottom
  1751.  
  1752.         gg.setColor(up?Color.white:Color.gray);
  1753.         gg.drawLine(x0,y0,x1-2,y0);     //top
  1754.         gg.drawLine(x0,y0,x0,y1-1);     //left
  1755.     }
  1756.  
  1757.     void drawHeading(boolean down)
  1758.     {
  1759.         int x0,y0,x1,y1;
  1760.         x0 = 1;
  1761.         y0 = 1;
  1762.         int xx;
  1763.         Font f = getFont();
  1764.         gg.setFont(headingFont);
  1765.         FontMetrics fm = gg.getFontMetrics();
  1766.  
  1767.         Dimension s = size();
  1768.         Rectangle r;
  1769.         for (int i=0; i<headings.length; i++)
  1770.         {
  1771.             x0 = splitters[i];
  1772.  
  1773.             //if (i < headings.length - 1)
  1774.             //{
  1775.                 xx = splitters[i+1];
  1776.                 x1 = xx - x0;
  1777.             //}
  1778.             //else
  1779.             //{
  1780.             //    //width of last column expands to extreme right
  1781.             //    xx = s.width;
  1782.             //    x1 = xx - x0;
  1783.             //}
  1784.  
  1785.             r = new Rectangle(x0,y0,x1-2, headingHeight);
  1786.  
  1787.             draw3DBox(r, (colClick==i)?false:true);
  1788.             gg.setColor(headingBg);
  1789.             gg.fillRect(r.x+1, r.y+1, x1-3, headingHeight-1);
  1790.             gg.setColor(headingFg);
  1791.  
  1792.             if (headings[i] == null)
  1793.                 continue;
  1794.  
  1795.             int sw = fm.stringWidth(headings[i]);
  1796.             int w = x1-3;
  1797.             int offset = 0;
  1798.             int shift = down && colClick == i ? 1 : 0;
  1799.  
  1800.             switch (align[i])
  1801.             {
  1802.                 case MultiList.LEFT:
  1803.                 {
  1804.                     gg.drawString(headings[i], x0+8+shift, headingHeight-3+shift);
  1805.                     break;
  1806.                 }
  1807.  
  1808.                 case MultiList.CENTER:
  1809.                 {
  1810.                     if (sw>w)
  1811.                     {
  1812.                         gg.drawString(headings[i], x0+8+shift, headingHeight-3+shift);
  1813.                     }
  1814.                     else
  1815.                     {
  1816.                         gg.drawString(headings[i], x0 + (w-sw)/2+shift, headingHeight-3+shift);
  1817.                     }
  1818.  
  1819.                     break;
  1820.                 }
  1821.  
  1822.                 case MultiList.RIGHT:
  1823.                 {
  1824.                     if (sw > w)
  1825.                     {
  1826.                         gg.drawString(headings[i], x0+8+shift, headingHeight-3+shift);
  1827.                     }
  1828.                     else
  1829.                     {
  1830.                         gg.drawString(headings[i], x0+w-sw-6+shift, headingHeight-3+shift);
  1831.                     }
  1832.  
  1833.                     break;
  1834.                 }
  1835.             }
  1836.  
  1837.             if (colClick == i)
  1838.             {
  1839.                 gg.drawLine(r.x+1, headingHeight+2, xx-3,  headingHeight+2);
  1840.             }
  1841.         }
  1842.         if (splitters[splitters.length-1]-2<sbHPosition+s.width-sbVWidth)
  1843.         {
  1844.             r = new Rectangle(splitters[splitters.length-1],y0,sbHPosition+s.width-sbVWidth-splitters[splitters.length-1]-2, headingHeight);
  1845.             draw3DBox(r, true);
  1846.             gg.setColor(headingBg);
  1847.             gg.fillRect(r.x+1, r.y+1, r.width-1, headingHeight-1);
  1848.         }
  1849.         //draw an outside border
  1850.         //gg.setColor(Color.black);
  1851.         //gg.drawRect(0,0, s.width-1, s.height-1);
  1852.         gg.setFont(f);
  1853.     }
  1854.  
  1855.     Frame frame()
  1856.     {
  1857.         Container c = this;
  1858.  
  1859.         while (c != null && !(c instanceof Frame))
  1860.         {
  1861.             c = c.getParent();
  1862.         }
  1863.  
  1864.         return (Frame)c;
  1865.     }
  1866.  
  1867.     /**
  1868.      * This routine changes the selected row, handling multiple selections
  1869.      * as needed.
  1870.      * @param newSelection the zero-relative index of the newly selected row
  1871.      * @param meta the state of the modifier keys from Event.modifiers
  1872.      * @see #setSelectedRow
  1873.      * @see Event.CTRL_MASK
  1874.      * @see Event.SHIFT_MASK
  1875.      */
  1876.     public void changeSelection(int newSelection, int meta)
  1877.     {
  1878.         if (multiSelect)
  1879.         {
  1880.             switch (meta)
  1881.             {
  1882.                 case Event.CTRL_MASK:
  1883.                 {
  1884.                     if (highlightedRows.get(newSelection))
  1885.                     {
  1886.                         highlightedRows.clear(newSelection);
  1887.                     }
  1888.                     else
  1889.                     {
  1890.                         highlightedRows.set(newSelection);
  1891.                     }
  1892.                     break;
  1893.                 }
  1894.                 case Event.SHIFT_MASK:
  1895.                 {
  1896.                     for (int i=Math.min(selectedRow, newSelection);
  1897.                          i<=Math.max(selectedRow, newSelection);
  1898.                          i++)
  1899.                     {
  1900.                         if (i>=0)
  1901.                         {
  1902.                             highlightedRows.set(i);
  1903.                         }
  1904.                     }
  1905.                     break;
  1906.                 }
  1907.                 default:
  1908.                 {
  1909.                     highlightedRows = new BitSet();
  1910.                     highlightedRows.set(newSelection);
  1911.                 }
  1912.             }//switch
  1913.         }
  1914.         else
  1915.         {
  1916.             if (selectedRow >= 0)
  1917.             {
  1918.                 highlightedRows.clear(selectedRow);
  1919.             }
  1920.             highlightedRows.set(newSelection);
  1921.         }
  1922.  
  1923.         int rc = cells.rows();
  1924.  
  1925.         if (newSelection > rc-1)
  1926.         {
  1927.             newSelection = rc-1;
  1928.         }
  1929.         else if (newSelection < 0)
  1930.         {
  1931.             newSelection = 0;
  1932.         }
  1933.  
  1934.         // scroll a page UPWARD
  1935.         if (newSelection < topRow)
  1936.         {
  1937.             topRow = newSelection -1;
  1938.  
  1939.             if (topRow < 0)
  1940.             {
  1941.                 topRow = 0;
  1942.             }
  1943.  
  1944.             sbVPosition = topRow;
  1945.             sbV.setValue(sbVPosition);
  1946.         }
  1947.  
  1948.         // scroll a page DOWNWARD
  1949.         if (newSelection >=  -(sbHShow?3:1) + topRow + getPageSize())
  1950.         {
  1951.             topRow = topRow + 1;
  1952.  
  1953.             if (topRow > rc-1)
  1954.             {
  1955.                 topRow = newSelection;
  1956.             }
  1957.  
  1958.             sbVPosition = topRow;
  1959.             sbV.setValue(sbVPosition);
  1960.         }
  1961.  
  1962.  
  1963.         if (selectedRow != newSelection)
  1964.         {
  1965.             if (meta > 0)
  1966.             {
  1967.                 //generate a list deselection event
  1968.                 postEvent(Event.LIST_DESELECT, selectedRow);
  1969.             }
  1970.         }
  1971.  
  1972.         //redraw tree
  1973.         selectedRow = newSelection;
  1974.  
  1975.         //Set forceRedraw so contents of offscreen is updated
  1976.         forceRedraw = true;
  1977.         repaint();
  1978.  
  1979.         //generate a list selection event here!!!
  1980.         postEvent(Event.LIST_SELECT, newSelection);
  1981.     }
  1982.  
  1983.     /**
  1984.      * Returns the recommended dimensions to properly display this component.
  1985.      * This is a standard Java AWT method which gets called to determine
  1986.      * the recommended size of this component. 
  1987.      *
  1988.      * @see #minimumSize
  1989.      */
  1990.     public synchronized Dimension preferredSize()
  1991.     {
  1992.         return new Dimension(175, 125);
  1993.     }
  1994.  
  1995.     /**
  1996.      * Returns the minimum dimensions to properly display this component.
  1997.      * This is a standard Java AWT method which gets called to determine
  1998.      * the minimum size of this component. 
  1999.      * 
  2000.      * @see #preferredSize
  2001.      */
  2002.     public synchronized Dimension minimumSize()
  2003.     {
  2004.         return new Dimension(50, 50);
  2005.     }
  2006.  
  2007.     private String[] tokenizeStringArrayIfNeeded(String[] list)
  2008.     {
  2009.         //Handle case where user thinks ; separates entries
  2010.         if (list.length == 1 && list[0].indexOf(";") != -1)
  2011.         {
  2012.             //Convert the first element into an array of strings
  2013.             java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(list[0],";");
  2014.  
  2015.             int numColumns = tokenizer.countTokens();
  2016.  
  2017.             String[] convertedList = new String[numColumns];
  2018.  
  2019.             int currCol = 0;
  2020.             while (tokenizer.hasMoreTokens())
  2021.             {
  2022.                 String columnHeader = tokenizer.nextToken();
  2023.  
  2024.                 convertedList[currCol] = columnHeader;
  2025.  
  2026.                 currCol++;
  2027.             }
  2028.  
  2029.             list = convertedList;
  2030.         }
  2031.  
  2032.         return list;
  2033.     }
  2034.  
  2035.     private void calcHeadings(String[] list)
  2036.     {
  2037.         //    RKM    In order to reduce chance of a bug injection, I've allowed the calculation below to
  2038.         //        occur, even in the case where the result is bogus
  2039.  
  2040.         int width = size().width / list.length;
  2041.  
  2042.         splitters[0] = 0;
  2043.         createColumns(list.length);
  2044.  
  2045.         int w = 0;
  2046.         for (int i = 0; i < list.length; ++i)
  2047.             setHeading(list[i], i, w += width);
  2048.  
  2049.         //If we are not in a container, then we can't calc the headings properly
  2050.         if (getParent() == null)
  2051.         {
  2052.             needDelayedHeadingsCalc = true;
  2053.             delayedHeadings = list;
  2054.             return;
  2055.         }
  2056.  
  2057.         needDelayedHeadingsCalc = false;
  2058.         delayedHeadings = null;
  2059.     }
  2060. }
  2061.  
  2062. class CompareCells
  2063.     implements CompareFunc
  2064. {
  2065.     public boolean lessThan(Object o1, Object o2)
  2066.     {
  2067.         //If we get a null, allocated an empty cell
  2068.         if (o1 == null)
  2069.             o1 = new Cell(null);
  2070.         if (o2 == null)
  2071.             o2 = new Cell(null);
  2072.  
  2073.         if (!(o1 instanceof Cell && o2 instanceof Cell))
  2074.         {
  2075.             throw new IllegalArgumentException("Objects to compare must " +
  2076.                                  "be Cell instances");
  2077.         }
  2078.  
  2079.         Cell c1 = (Cell)o1;
  2080.         Cell c2 = (Cell)o2;
  2081.  
  2082.         return c1.text.compareTo(c2.text) < 0;
  2083.     }
  2084. }
  2085.  
  2086.  
  2087. class Cell
  2088.     implements java.awt.image.ImageObserver
  2089. {
  2090.     static String empty = "";
  2091.  
  2092.     MultiList   list;
  2093.     String      text = empty;
  2094.     Image       im = null;
  2095.  
  2096.     public Cell(MultiList l)
  2097.     {
  2098.         list = l;
  2099.     }
  2100.  
  2101.     public Cell(MultiList l, String s)
  2102.     {
  2103.         list = l;
  2104.         text = s;
  2105.     }
  2106.  
  2107.     public Cell(MultiList l, Image i)
  2108.     {
  2109.         list = l;
  2110.         im = i;
  2111.     }
  2112.  
  2113.     public Cell(MultiList l, String s, Image i)
  2114.     {
  2115.         list = l;
  2116.         text = s;
  2117.         im = i;
  2118.     }
  2119.  
  2120.     public void drawCell(Graphics g, int align, int x, int y, int w, int h, int asc)
  2121.     {
  2122.         FontMetrics fm = g.getFontMetrics();
  2123.         int sw = fm.stringWidth(text);
  2124.         int offset = 0;
  2125.  
  2126.         switch(align)
  2127.         {
  2128.             case MultiList.LEFT:
  2129.             {
  2130.                 if (im != null)
  2131.                 {
  2132.                     g.drawImage(im, x, y, this);
  2133.                     offset = im.getWidth(this) + 2;
  2134.                 }
  2135.  
  2136.                 g.drawString(text, x + offset, y+asc);
  2137.                 break;
  2138.             }
  2139.  
  2140.             case MultiList.CENTER:
  2141.             {
  2142.                 if (sw > w)
  2143.                 {
  2144.                     g.drawString(text, x, y+asc);
  2145.                 }
  2146.                 else
  2147.                 {
  2148.                     g.drawString(text, x + (w-sw)/2, y+asc);
  2149.                 }
  2150.  
  2151.                 break;
  2152.             }
  2153.  
  2154.             case MultiList.RIGHT:
  2155.             {
  2156.                 if (sw > w)
  2157.                 {
  2158.                     g.drawString(text, x, y+asc);
  2159.                 }
  2160.                 else
  2161.                 {
  2162.                     g.drawString(text, x+w-sw-6, y+asc);
  2163.                 }
  2164.  
  2165.                 break;
  2166.             }
  2167.         }
  2168.     }
  2169.  
  2170.     /**
  2171.      * Incrementally updates an image as image data becomes available.
  2172.      * This is a standard Java AWT method which gets called by the AWT to
  2173.      * incrementally draw an image as more image data becomes available.
  2174.      * @param img the image being drawn
  2175.      * @param flags image update flags (see class ImageObserver)
  2176.      * @param x horizontal position
  2177.      * @param y vertical position
  2178.      * @param w width
  2179.      * @param h height
  2180.      *
  2181.      * @return true if image has been completely loaded
  2182.      *
  2183.      * @see java.awt.image.ImageObserver
  2184.      * @see java.awt.image.ImageObserver#imageUpdate
  2185.      */
  2186.     public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h)
  2187.     {
  2188.         if ((flags & (ALLBITS|ABORT)) != 0)
  2189.         {
  2190.             list.redraw();
  2191.             list.repaint();
  2192.             return false;
  2193.         }
  2194.         else
  2195.         {
  2196.             return true;
  2197.         }
  2198.     }
  2199.  
  2200.     /**
  2201.      * Returns a string representation of this component.
  2202.      * This is a standard Java AWT method which gets called to generate
  2203.      * a string that represents this component.
  2204.      * 
  2205.      * @return a meaningful string about this object
  2206.      */
  2207.     public String toString()
  2208.     {
  2209.         return text;
  2210.     }
  2211. }
  2212.  
  2213.