home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / Source.bin / MultiList.java < prev    next >
Text File  |  1998-03-18  |  112KB  |  3,843 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.*;
  4. import java.awt.event.*;
  5. import java.util.BitSet;
  6. import java.util.Vector;
  7. import java.util.Hashtable;
  8. import java.beans.PropertyVetoException;
  9. import java.beans.PropertyChangeListener;
  10. import java.beans.VetoableChangeListener;
  11. import symantec.itools.awt.multiList.Cell;
  12. import symantec.itools.awt.multiList.TextAndImageCell;
  13. import symantec.itools.awt.multiList.CompareTextAndImageCells;
  14. import symantec.itools.beans.VetoableChangeSupport;
  15. import symantec.itools.beans.PropertyChangeSupport;
  16. import java.text.MessageFormat;
  17. import java.util.ResourceBundle;
  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 verticalScrollbarWidth & scrollbarHeight 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. //  05/30/97    LAB    Updated to Java 1.1
  72. //  06/01/97    RKM    Changed symantec.beans references to java.beans
  73. //  05/30/97    LAB    Now fires ItemEvents (as it should), now has listener registration
  74. //                    functions for bound and constrained property listeners.
  75. //  05/15/97    TNM    Check for vendor to correct scrollbar problem.
  76. //  06/20/97    TNM    Merged changes
  77. //  06/20/97    TNM    Allowed reverse sorting. Changed repaint.
  78. //  06/26/97    CAR    Bug fix in calcHeadnings when list.length is 0
  79. //  07/14/97    LAB    Updated cursor handling for JDK 1.1.  Removed frame() method and changed calls to
  80. //                    the frame's setCursor() to be calls to Component's setCursor() as per JDK 1.1.
  81. //                    Updated version to 1.1.  Added add/removeNotify to handle event listener registration,
  82. //                    and removed old event listener registration in the Constructor.
  83. //                    Separated CompareCells and Cell classes, and made then public to accommodate
  84. //                    future extendibility of the MultiList class.  Made most data members protected
  85. //                    for this purpose also.  Added AllowSorting property.
  86. //  07/18/97    LAB    Changed incorrect calls to invalidate() to repaint().
  87. //  07/23/97    CAR changed visibility of field "hasFocus" from private to package
  88. //                  marked fields transient as needed
  89. //                  inner classes implement java.io.Serializable
  90. //  08/04/97    RKM    Added override of setLayout - so users could not change it
  91. //                    Renamed a couple of things for more readable code
  92. //                    Fixed a couple of the setters to compare objects properly
  93. //                    Reworked drawing of headings (screwed Window's drawing - NEED TO FIX)
  94. //                    Fixed click and drag in and out of column heading - it would depress only first time
  95. //                    Fixed font save/restore bug in drawHeading (was typo)
  96. //                    Lots of tweaks to make drawing better (not platform specific)
  97. //                    Fixed setSelectedRow to work correctly
  98. //                    Added allowResizingOfColumns, did not make it a property
  99. //                    Fixed bug where dragging out of the column heading and then clicking in a cell and dragging into heading
  100. //                        resulted in the heading sorting
  101. //  08/05/97    RKM    Added selectAll & deselectAll
  102. //                    Fixed drawing of the selection border
  103. //                    Added setMultipleMode & isMultipleMode
  104. //                    Reworked drawRows
  105. //                    Made selectRow & deSelectRow public
  106. //                    Removed propertyChanged & veto stuff from setSelectedRow
  107. //                    Fixed scrolling up via arrows bug
  108. //                    Fixed clicking below last cell bug
  109. //                    Fixed clicking in column header and swapping of sort order (even if clicking in another cell) bug
  110. //  08/06/97    RKM    Added mechanisms to allow users to plug in own drawing and sorting routines (not as great as it could be)
  111. //                    Cleaned up redraw() to not recreate offscreen image every time (was a bug)
  112. //  08/07/97    RKM    Changed auto resizing mechanism to never allocate columnSizes
  113. //                    Added auto-resize property
  114. //  08/09/97    RKM    Temp fixed for column alignment problem - I need to come back to this and make column
  115. //                        alignment behave as column sizes
  116. //  08/10/97    RKM    Change stategy on redraws/repaints
  117. //  08/11/97    RKM    Change column alignments to allow for better default column alignment support
  118. //                    Fixed injection caused by setSupressRedraw, tiggerRedraw was not getting called
  119. //  08/16/97    RKM    Added ability for user (extender) to override the calculation of the header height
  120. //  08/17/97    RKM    Added property to show or hide the headings - user request
  121. //                     Added property to control the visual indication of focus
  122. //  08/19/97    CAR Changed addTextCell, if String object is null set to ""
  123. //  08/25/97    RKM    Changed drawing - no headings, no drawing
  124. //                    Added internalCreateColumns, so CreateColumns could triggerRedraw
  125. //                    Added headings as needed in setListItems (allows users to set items before headings)
  126. //                    Fixed bug in getColumnAlignment, it was not returning the alignment of the last column
  127. //                    Fixed setNumberOfColumns to function better
  128. //                    Added isFocusTraversable override
  129. //  08/27/97    CAR Fixed bug in multiple mode pertaining to mouse input event modifiers
  130. //  09/03/97    RKM Add recalc of column widths when adding columns automatically in resizeHeadings
  131. //  09/11/97    RKM Changed default font from Helvetica to SansSerif
  132. //  09/24/97    RKM Properly set isSun1_1, now that MRJ is working well!!!!!!!!
  133. //  10/20/97    LAB Added suppress redraws around calls to calcVerticalScrollbarPosition in redraw
  134. //                    since calcVerticalScrollbarPosition calls adjustHeadings wich could end up calling
  135. //                    triggerRedraw and put the form editor in an infinite loop (Addresses Mac Bug #8567).
  136. //  10/21/97    JYZ Add synchronized keyword to all registration methods
  137. //  10/21/97    JYZ Fixed bug in the set Foreground color for the list item. It should be set to
  138. //                  a system color instead of an absolute color such as black. Then this may
  139. //                  explain why we see different results on different systems. (A Customer bug report)
  140. //  12/19/97    DS  Added checks for null buffer and rectangle
  141. //  03/03/98    DS  Added DeprecatedException to stop use of "impossible to get right" method
  142.  
  143. /**
  144.  * This class implements a multi-column listbox component.
  145.  * Use to create a box that displays a matrix of items that the user can
  146.  * selectRow. The user cannot type or edit a selection in a list box.
  147.  * <p>
  148.  * The user can resize a column at run-time by dragging the column boundary
  149.  * to a new position.
  150.  * <p>
  151.  * @version 1.1, July 14, 1997
  152.  * @author Symantec
  153.  */
  154. public class MultiList extends Panel implements java.io.Serializable, java.awt.ItemSelectable
  155. {
  156.     /**
  157.      * Maximum time inverval to register a double click (in milliseconds).
  158.      */
  159.     public final static long CLICKTHRESHOLD = 250;
  160.  
  161.     /**
  162.      * Left-justify column alignment constant.
  163.      */
  164.     public final static int LEFT = 0;
  165.  
  166.     /**
  167.      * Center-justify column alignment constant.
  168.      */
  169.     public final static int CENTER = 1;
  170.  
  171.     /**
  172.      * Right-justify column alignment constant.
  173.      */
  174.     public final static int RIGHT = 2;
  175.  
  176.     /**
  177.      * Border amount. Only calc'd on the right and the bottom side.
  178.      */
  179.     protected final static int BORDER = 1;
  180.     /**
  181.      * Fudge factor for resizing of columns.
  182.      */
  183.     protected final static int RESIZE_FUDGE_FACTOR = 3;
  184.  
  185.     //
  186.     // Constructors
  187.     //
  188.  
  189.     /**
  190.      * Constructs a default MultiList.
  191.      */
  192.     public MultiList()
  193.     {
  194.         this(0, false, Color.white);
  195.     }
  196.  
  197.     /**
  198.      * Constructs a new MultiList with the specified number of columns.
  199.      * @param cols the number of columns
  200.      */
  201.     public MultiList(int cols)
  202.     {
  203.         this(cols, false, Color.white);
  204.     }
  205.  
  206.     /**
  207.      * Constructs a new MultiList with the spcified number of columns
  208.      * and whether multiple row selection allowed.
  209.      * @param cols the number of columns
  210.      * @param multi true for multiple row selection, false otherwise
  211.      */
  212.     public MultiList(int cols, boolean multi)
  213.     {
  214.         this(cols, multi, Color.white);
  215.     }
  216.  
  217.     /**
  218.      * Constructs a new MultiList with the specified number of columns,
  219.      * whether multiple row selection is allowed, and given background color.
  220.      * @param cols the number of columns
  221.      * @param multi true for multiple row selection, false otherwise
  222.      * @param bg the background color
  223.      */
  224.     public MultiList(int cols, boolean multi, Color bg)
  225.     {
  226.         internalCreateColumns(cols);
  227.  
  228.         multiSelect = multi;
  229.  
  230.         super.setLayout(null);
  231.  
  232.         Font defaultFont = new Font("SansSerif", Font.PLAIN, 12);
  233.         try
  234.         {
  235.             setHeadingFont(defaultFont);
  236.             setCellFont(defaultFont);
  237.         }
  238.         catch(PropertyVetoException e){}
  239.  
  240.         setBackground(colorBg = bg);
  241.  
  242.         verticalScrollbar = new Scrollbar(Scrollbar.VERTICAL);
  243.         verticalScrollbar.hide();
  244.         add(verticalScrollbar);
  245.  
  246.         horizontalScrollbar = new Scrollbar(Scrollbar.HORIZONTAL);
  247.         horizontalScrollbar.hide();
  248.         add(horizontalScrollbar);
  249.     }
  250.  
  251.     //
  252.     // Properties
  253.     //
  254.  
  255.     /**
  256.      * Sets whether this MultiList should allow multiple selections or not.
  257.      * @param b the boolean to allow multiple selections
  258.      * @see #isMultipleMode
  259.      * @exception PropertyVetoException
  260.      * if the specified property value is unacceptable
  261.      */
  262.     public void setMultipleMode(boolean b)
  263.         throws PropertyVetoException
  264.     {
  265.         if (multiSelect != b)
  266.         {
  267.             Boolean oldValue = new Boolean(multiSelect);
  268.             Boolean newValue = new Boolean(b);
  269.  
  270.             vetos.fireVetoableChange("multipleMode", oldValue, newValue);
  271.  
  272.             multiSelect = b;
  273.  
  274.             //If turning multimode off, only the first row in the selection should stay selected
  275.             if (!multiSelect)
  276.             {
  277.                 int[] selectedRows = getSelectedRows();
  278.                 for (int i = 1;i < selectedRows.length;i++)
  279.                     deselectRow(selectedRows[i]);
  280.             }
  281.  
  282.             changes.firePropertyChange("multipleMode", oldValue, newValue);
  283.         }
  284.     }
  285.  
  286.     /**
  287.      * Returns true if this MultiList allows multiple selections.
  288.      * @see #setMultipleMode
  289.      */
  290.     public boolean isMultipleMode()
  291.     {
  292.         return multiSelect;
  293.     }
  294.  
  295.     /**
  296.      * Sets whether this MultiList should visually indicate focus.
  297.      * @param b the boolean to visually indicate focus
  298.      * @see #isFocusIndicatedVisually
  299.      * @exception PropertyVetoException
  300.      * if the specified property value is unacceptable
  301.      */
  302.     public void setFocusIndicatedVisually(boolean b)
  303.         throws PropertyVetoException
  304.     {
  305.         if (focusIndicatedVisually != b)
  306.         {
  307.             Boolean oldValue = new Boolean(focusIndicatedVisually);
  308.             Boolean newValue = new Boolean(b);
  309.  
  310.             vetos.fireVetoableChange("focusIndicatedVisually", oldValue, newValue);
  311.  
  312.             focusIndicatedVisually = b;
  313.  
  314.             changes.firePropertyChange("focusIndicatedVisually", oldValue, newValue);
  315.  
  316.             repaintFocus();
  317.         }
  318.     }
  319.  
  320.     /**
  321.      * Returns true if this MultiList indicates it has focus visually.
  322.      * @see #setFocusIndicatedVisually
  323.      */
  324.     public boolean isFocusIndicatedVisually()
  325.     {
  326.         return focusIndicatedVisually;
  327.     }
  328.  
  329.     /**
  330.      * Sets the number of columns.
  331.      * @param i the new number of columns
  332.      * @exception PropertyVetoException
  333.      * if the specified property value is unacceptable
  334.      * @see #getNumberOfCols
  335.      */
  336.     public void setNumberOfCols(int i)
  337.         throws PropertyVetoException
  338.     {
  339.         Integer oldValue = new Integer(getNumberOfCols());
  340.         Integer newValue = new Integer(i);
  341.  
  342.         if (!oldValue.equals(newValue))
  343.         {
  344.             vetos.fireVetoableChange("numberOfCols", oldValue, newValue);
  345.  
  346.             internalCreateColumns(0);
  347.             resizeHeadings(i);
  348.             forceColumnSizeRecalc = true;
  349.  
  350.             changes.firePropertyChange("numberOfCols", oldValue, newValue);
  351.  
  352.             triggerRedraw();
  353.         }
  354.     }
  355.  
  356.     /**
  357.      * @deprecated
  358.      * @see #setNumberOfCols(int)
  359.      * @exception PropertyVetoException
  360.      * if the specified property value is unacceptable
  361.      */
  362.     public void setColumns(int i) throws PropertyVetoException
  363.     {
  364.         setNumberOfCols(i);
  365.     }
  366.  
  367.     /**
  368.      * Returns the current number of columns.
  369.      * @returns the current number of columns.
  370.      * @see #setNumberOfCols
  371.      */
  372.     public int getNumberOfCols()
  373.     {
  374.         return headings.length;
  375.     }
  376.  
  377.     /**
  378.      * Gets the current number of rows.
  379.      * @return the current number of rows
  380.      */
  381.     public int getNumberOfRows()
  382.     {
  383.         return cells.rows();
  384.     }
  385.  
  386.  
  387.     /**
  388.      * Sets whether this MultiList should draw the column headings or not.
  389.      * @param b the boolean to draw column headings
  390.      * @see #isHeadingVisible
  391.      * @exception PropertyVetoException
  392.      * if the specified property value is unacceptable
  393.      */
  394.     public void setHeadingVisible(boolean b)
  395.         throws PropertyVetoException
  396.     {
  397.         if (headingVisible != b)
  398.         {
  399.             Boolean oldValue = new Boolean(headingVisible);
  400.             Boolean newValue = new Boolean(b);
  401.  
  402.             vetos.fireVetoableChange("headingVisible", oldValue, newValue);
  403.  
  404.             headingVisible = b;
  405.  
  406.             changes.firePropertyChange("headingVisible", oldValue, newValue);
  407.  
  408.             if (headingVisible)
  409.                 calculateHeadingHeight();
  410.             else
  411.                 headingHeight = 0;
  412.  
  413.             //!!!RKM!!! Had to do this to handle mystery column not going away
  414.             //???RKM??? If I remove the mystery column on resize, I can probably remove this
  415.             forceFullRedraw = true;
  416.             triggerRedraw();
  417.         }
  418.     }
  419.  
  420.     /**
  421.      * Returns true if heading of the MultiList is visible.
  422.      * @see #setHeadingVisible
  423.      */
  424.     public boolean isHeadingVisible()
  425.     {
  426.         return headingVisible;
  427.     }
  428.  
  429.     /**
  430.      * Returns the heading text of the specified column.
  431.      * @param i the zero-relative index of the column
  432.      * @see #setHeading
  433.      */
  434.     public String getHeading(int i)
  435.     {
  436.         return headings[i];
  437.     }
  438.  
  439.     /**
  440.      * Sets a column's heading text.
  441.      * @param h the new column heading text
  442.      * @param i the zero-relative index of the column
  443.      * @see #setHeading(java.lang.String, int, int)
  444.      * @see #getHeading
  445.      * @exception PropertyVetoException
  446.      * if the specified property value is unacceptable
  447.      */
  448.     public void setHeading(String h, int i)
  449.         throws PropertyVetoException
  450.     {
  451.         String oldValue = headings[i];
  452.         if (!symantec.itools.util.GeneralUtils.objectsEqual(oldValue,h))
  453.         {
  454.             String newValue = h;
  455.  
  456.             vetos.fireVetoableChange("heading", oldValue, newValue);
  457.  
  458.             headings[i] = h;
  459.  
  460.             changes.firePropertyChange("heading", oldValue, newValue);
  461.  
  462.             triggerRedraw();
  463.         }
  464.     }
  465.  
  466.     /**
  467.      * Sets a column's heading text and width.
  468.      * @param h the new column heading text
  469.      * @param i the zero-relative index of the column
  470.      * @param pixels the new width of the column, in pixels
  471.      * @see #setHeading(java.lang.String, int)
  472.      * @see #getHeading
  473.      * @exception PropertyVetoException
  474.      * if the specified property value is unacceptable
  475.      */
  476.     public void setHeading(String h, int i, int pixels) throws PropertyVetoException
  477.     {
  478.         throw new DeprecatedException("symantec.itools.awt.Multilist.setHeadings(String, int, int)\n" +
  479.                                       "cannot be used.  Please change your code to\n" +
  480.                                       "setColumnSizes(String[]) instead");
  481.  
  482.  
  483.  
  484. /*
  485.         String oldValue = headings[i];
  486.         if (!symantec.itools.util.GeneralUtils.objectsEqual(oldValue,h))
  487.         {
  488.             String newValue = h;
  489.  
  490.             vetos.fireVetoableChange("heading", oldValue, newValue);
  491.  
  492.             headings[i] = h;
  493.             splitters[i+1] = pixels;
  494.  
  495.             {
  496.                 //???RKM??? Shouldn't this be done in such a way as to allow vetoing
  497.  
  498.                 if (columnSizes == null)
  499.                     columnSizes = new int[headings.length];
  500.  
  501.                 // Update all; the user might not set headings in sequence 0 to n
  502.  
  503.                 for (int j = 0;j < headings.length;j++)
  504.                     columnSizes[j] = splitters[j + 1] - splitters[j];
  505.             }
  506.  
  507.             changes.firePropertyChange("heading", oldValue, newValue);
  508.  
  509.             triggerRedraw();
  510.         }
  511.         */
  512.     }
  513.  
  514.     /**
  515.      * Gets a String array of all of the column headings.
  516.      * @return an array of all of the current headings
  517.      * @see #setHeadings
  518.      */
  519.     public String[] getHeadings()
  520.     {
  521.         return headings;
  522.     }
  523.  
  524.     /**
  525.      * Initializes all the column headings with the given list.
  526.      * For each string in the given list, create a column, use the string as
  527.      * its heading, and give it a default width.
  528.      * @param list array of heading title strings
  529.      * @see #getHeadings
  530.      * @exception PropertyVetoException
  531.      * if the specified property value is unacceptable
  532.      */
  533.     public void setHeadings(String[] list) throws PropertyVetoException
  534.     {
  535.         //Supress redraws
  536.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  537.  
  538.         try
  539.         {
  540.             //Handle String Arrays as well as array of one string with ; separators
  541.             list = tokenizeStringArrayIfNeeded(list);
  542.  
  543.             String[] oldValue = getHeadings();
  544.  
  545.             vetos.fireVetoableChange("headings", oldValue, list);
  546.  
  547.             if (list.length == 0)
  548.                 internalCreateColumns(0);
  549.             else
  550.                 calcHeadings(list);
  551.  
  552.             changes.firePropertyChange("headings", oldValue, list);
  553.         }
  554.         finally
  555.         {
  556.             forceColumnSizeRecalc = true;
  557.             triggerRedraw();
  558.             setSupressRedraw(wasSuppressingRedraw);
  559.         }
  560.     }
  561.  
  562.     /**
  563.      * Returns the font for column heading text.
  564.      * @see #setHeadingFont
  565.      */
  566.     public Font getHeadingFont()
  567.     {
  568.         return headingFont;
  569.     }
  570.  
  571.     /**
  572.      * Sets the font for column heading text.
  573.      * @param newFont the font for heading text
  574.      * @see #getHeadingFont
  575.      * @exception PropertyVetoException
  576.      * if the specified property value is unacceptable
  577.      */
  578.     public void setHeadingFont(Font newFont) throws PropertyVetoException
  579.     {
  580.         if (!symantec.itools.util.GeneralUtils.objectsEqual(headingFont,newFont))
  581.         {
  582.             Font oldValue = getHeadingFont();
  583.  
  584.             vetos.fireVetoableChange("headingFont", oldValue, newFont);
  585.  
  586.             headingFont = newFont;
  587.  
  588.             changes.firePropertyChange("headingFont", oldValue, newFont);
  589.  
  590.             if (headingVisible)
  591.             {
  592.                 calculateHeadingHeight();
  593.                 triggerRedraw();
  594.             }
  595.         }
  596.     }
  597.  
  598.     /**
  599.      * Returns the font currently used for text in all the cells.
  600.      * @see #setCellFont
  601.      */
  602.     public Font getCellFont()
  603.     {
  604.         return cellFont;
  605.     }
  606.  
  607.     /**
  608.      * Sets the font used for text in all the cells.
  609.      * @param f the font for cell text
  610.      * @see #getCellFont
  611.      * @exception PropertyVetoException
  612.      * if the specified property value is unacceptable
  613.      */
  614.     public void setCellFont(Font f) throws PropertyVetoException
  615.     {
  616.         if (!symantec.itools.util.GeneralUtils.objectsEqual(cellFont,f))
  617.         {
  618.             Font oldValue = cellFont;
  619.  
  620.             vetos.fireVetoableChange("cellFont", oldValue, f);
  621.  
  622.             cellFont = f;
  623.             FontMetrics fontMetrics = getFontMetrics(f);
  624.             cellAscent = fontMetrics.getAscent();
  625.             cellDescent = fontMetrics.getDescent();
  626.             cellHeight = fontMetrics.getHeight();
  627.  
  628.             changes.firePropertyChange("cellFont", oldValue, f);
  629.  
  630.             triggerRedraw();
  631.         }
  632.     }
  633.  
  634.     /**
  635.      * Sets the column heading foreground and background colors.
  636.      * @param fg foreground Color for heading text
  637.      * @param bg background Color for heading text
  638.      * @see #setHeadingFg
  639.      * @see #setHeadingBg
  640.      * @see #getHeadingFg
  641.      * @see #getHeadingBg
  642.      * @exception PropertyVetoException
  643.      * if the specified property value is unacceptable
  644.      */
  645.     public void setHeadingColors(Color fg, Color bg) throws PropertyVetoException
  646.     {
  647.         boolean wasSupressingRedraw = setSupressRedraw(true);
  648.  
  649.         try
  650.         {
  651.             setHeadingFg(fg);
  652.             setHeadingBg(bg);
  653.         }
  654.         finally
  655.         {
  656.             triggerRedraw();
  657.             setSupressRedraw(wasSupressingRedraw);
  658.         }
  659.     }
  660.  
  661.     /**
  662.      * Returns the color of the column heading text foreground.
  663.      * @return the current column heading foreground color
  664.      * @see #setHeadingFg
  665.      */
  666.     public Color getHeadingFg()
  667.     {
  668.         return headingFg;
  669.     }
  670.  
  671.     /**
  672.      * Sets the column heading text foreground color.
  673.      * @param c foreground color for heading text
  674.      * @see #getHeadingFg
  675.      * @see #setHeadingBg
  676.      * @see #setHeadingColors
  677.      * @exception PropertyVetoException
  678.      * if the specified property value is unacceptable
  679.      */
  680.     public void setHeadingFg(Color c) throws PropertyVetoException
  681.     {
  682.         if (!symantec.itools.util.GeneralUtils.objectsEqual(headingFg,c))
  683.         {
  684.             Color oldValue = headingFg;
  685.  
  686.             vetos.fireVetoableChange("headingFg", oldValue, c);
  687.  
  688.             headingFg = c;
  689.  
  690.             changes.firePropertyChange("headingFg", oldValue, c);
  691.  
  692.             triggerRedraw();
  693.         }
  694.     }
  695.  
  696.     /**
  697.      * Returns the color of the column heading text background.
  698.      * @return the current column heading background color
  699.      * @see #setHeadingBg
  700.      */
  701.     public Color getHeadingBg()
  702.     {
  703.         return headingBg;
  704.     }
  705.  
  706.     /**
  707.      * Sets the column heading text background color.
  708.      * @param c background color for heading text
  709.      * @see #getHeadingBg
  710.      * @see #setHeadingFg
  711.      * @see #setHeadingColors
  712.      * @exception PropertyVetoException
  713.      * if the specified property value is unacceptable
  714.      */
  715.     public void setHeadingBg(Color c) throws PropertyVetoException
  716.     {
  717.         if (!symantec.itools.util.GeneralUtils.objectsEqual(headingBg,c))
  718.         {
  719.             Color oldValue = headingBg;
  720.  
  721.             vetos.fireVetoableChange("headingBg", oldValue, c);
  722.  
  723.             headingBg = c;
  724.  
  725.             changes.firePropertyChange("headingBg", oldValue, c);
  726.  
  727.             triggerRedraw();
  728.         }
  729.     }
  730.  
  731.     /**
  732.      * Sets foreground and background colors of cell text.
  733.      * @param fg the foreground color of cell text
  734.      * @param bg the background color of cell text
  735.      * @see #setCellFg
  736.      * @see #setCellBg
  737.      * @see #getCellFg
  738.      * @see #getCellBg
  739.      * @exception PropertyVetoException
  740.      * if the specified property value is unacceptable
  741.      */
  742.     public void setCellColors(Color fg, Color bg) throws PropertyVetoException
  743.     {
  744.         boolean wasSupressingRedraw = setSupressRedraw(true);
  745.         try
  746.         {
  747.             setCellFg(fg);
  748.             setCellBg(bg);
  749.         }
  750.         finally
  751.         {
  752.             triggerRedraw();
  753.             setSupressRedraw(wasSupressingRedraw);
  754.         }
  755.     }
  756.  
  757.     /**
  758.      * Gets the current foreground color of cell text.
  759.      * @return the current cell foreground color
  760.      * @see #setCellFg
  761.      */
  762.     public Color getCellFg()
  763.     {
  764.         return colorFg;
  765.     }
  766.  
  767.     /**
  768.      * Sets the foreground color of cell text.
  769.      * @param c the foreground color of cell text
  770.      * @see #getCellFg
  771.      * @see #setCellBg
  772.      * @see #setCellColors
  773.      * @exception PropertyVetoException
  774.      * if the specified property value is unacceptable
  775.      */
  776.     public void setCellFg(Color c) throws PropertyVetoException
  777.     {
  778.         if (!symantec.itools.util.GeneralUtils.objectsEqual(colorFg,c))
  779.         {
  780.             Color oldValue = colorFg;
  781.  
  782.             vetos.fireVetoableChange("cellFg", oldValue, c);
  783.  
  784.             colorFg = c;
  785.  
  786.             changes.firePropertyChange("cellFg", oldValue, c);
  787.  
  788.             triggerRedraw();
  789.         }
  790.     }
  791.  
  792.     /**
  793.      * Sets the background color of cell text.
  794.      * @param c the background color of cell text
  795.      * @see #getCellBg
  796.      * @see #setCellFg
  797.      * @see #setCellColors
  798.      * @exception PropertyVetoException
  799.      * if the specified property value is unacceptable
  800.      */
  801.     public void setCellBg(Color c) throws PropertyVetoException
  802.     {
  803.         if (!symantec.itools.util.GeneralUtils.objectsEqual(colorBg,c))
  804.         {
  805.             Color oldValue = colorBg;
  806.  
  807.             vetos.fireVetoableChange("cellBg", oldValue, c);
  808.  
  809.             colorBg = c;
  810.  
  811.             changes.firePropertyChange("cellBg", oldValue, c);
  812.  
  813.             triggerRedraw();
  814.         }
  815.     }
  816.  
  817.     /**
  818.      * Gets the current background color of cell text.
  819.      * @return the current cell background color
  820.      * @see #setCellBg
  821.      */
  822.     public Color getCellBg()
  823.     {
  824.         return colorBg;
  825.     }
  826.  
  827.     /**
  828.      * Sets the alignment of each column.
  829.      * If null is passed, then the default alignment for all columns is used.
  830.      * @param list an array of strings that determines column alignment.
  831.      * Each item should contain one of the following: "Left", "Center", or "Right"
  832.      * @see #getColumnAlignments
  833.      * @exception PropertyVetoException
  834.      * if the specified property value is unacceptable
  835.      */
  836.     public void setColumnAlignments(String[] list)
  837.         throws PropertyVetoException
  838.     {
  839.         //Preprocess list (zero elements == null)
  840.         if (list != null && list.length == 0)
  841.             list = null;
  842.  
  843.         //More preprocessing of lists (users could pass array or single string with ; as separators)
  844.         list = tokenizeStringArrayIfNeeded(list);
  845.  
  846.         //???RKM??? What if the size passed in is not the same as the headings array size
  847.  
  848.         String[] oldValue = getColumnAlignments();
  849.  
  850.         vetos.fireVetoableChange("columnAlignments", oldValue, list);
  851.  
  852.         if (list != null)
  853.         {
  854.             // Initialize the columnSizes array again
  855.             columnAlignments = new int[list.length];
  856.  
  857.             for (int i = 0; i < list.length; i++)
  858.             {
  859.                 String listElem = list[i];
  860.  
  861.                 int alignment = defaultColumnAlignment;
  862.                 if (listElem != null)
  863.                 {
  864.                     if (listElem.equalsIgnoreCase("Left"))
  865.                         alignment = LEFT;
  866.                     else if (listElem.equalsIgnoreCase("Center"))
  867.                         alignment = CENTER;
  868.                     else if (listElem.equalsIgnoreCase("Right"))
  869.                         alignment = RIGHT;
  870.                 }
  871.  
  872.                 columnAlignments[i] = alignment;
  873.             }
  874.         }
  875.         else columnAlignments = null;
  876.  
  877.         changes.firePropertyChange("columnAlignments", oldValue, list);
  878.  
  879.         triggerRedraw();
  880.     }
  881.  
  882.     /**
  883.      * Gets a string array containing the alignment of each column.
  884.      * The possible values for each element are "Left", "Center", or "Right".
  885.      * @return an array of strings containing the alignment for each column
  886.      * @see #setColumnAlignments
  887.      */
  888.     public String[] getColumnAlignments()
  889.     {
  890.         //If using default alignment for all columns, then return null
  891.         if (columnAlignments == null)
  892.             return null;
  893.  
  894.         String[] list = new String[columnAlignments.length];
  895.  
  896.         for (int i = 0; i < list.length; i++)
  897.         {
  898.             String alignString;
  899.             switch (getColumnAlignment(i))
  900.             {
  901.                 case LEFT:
  902.                 default:
  903.                     alignString = "Left";
  904.                     break;
  905.                 case CENTER:
  906.                     alignString = "Center";
  907.                     break;
  908.                 case RIGHT:
  909.                     alignString = "Right";
  910.                     break;
  911.             }//switch
  912.  
  913.             list[i] = alignString;
  914.         }
  915.  
  916.         return list;
  917.     }
  918.  
  919.     /**
  920.      * Sets the alignment of the text in the specified column.
  921.      * @param column the zero-relative column index
  922.      * @param alignment one of the values: LEFT, CENTER, or RIGHT
  923.      * @see #LEFT
  924.      * @see #CENTER
  925.      * @see #RIGHT
  926.      * @see #getColumnAlignment
  927.      * @exception PropertyVetoException
  928.      * if the specified property value is unacceptable
  929.      */
  930.     public void setColumnAlignment(int column, int alignment)
  931.         throws PropertyVetoException
  932.     {
  933.         //Range check
  934.         rangeCheckAlignment(alignment);
  935.         rangeCheckColumn(column);
  936.  
  937.         Integer oldValue = new Integer(getColumnAlignment(column));
  938.         Integer newValue = new Integer(alignment);
  939.  
  940.         vetos.fireVetoableChange("columnAlignment", oldValue, newValue);
  941.  
  942.         //Handle switching from default alignment
  943.         if (columnAlignments == null)
  944.         {
  945.             columnAlignments = new int[headings.length];
  946.             for (int i = 0;i < headings.length;i++)
  947.                 columnAlignments[i] = -1;
  948.         }
  949.  
  950.         this.columnAlignments[column] = alignment;
  951.  
  952.         changes.firePropertyChange("columnAlignment", oldValue, newValue);
  953.  
  954.         triggerRedraw();
  955.     }
  956.  
  957.     /**
  958.      * Gets the justification of the text for the specified column.
  959.      * If the column is allocated, it returns the justification.
  960.      * Otherwise it returns the default justification.
  961.      * @param column the zero-relative column index
  962.      * @return the alignment of the column as one of the values LEFT, CENTER, or RIGHT
  963.      * @see #setColumnAlignment(int, int)
  964.      */
  965.     public int getColumnAlignment(int column)
  966.     {
  967.         if (columnAlignments != null && column < columnAlignments.length)
  968.         {
  969.             int columnAlignment = columnAlignments[column];
  970.             if (columnAlignment != -1)
  971.                 return columnAlignments[column];
  972.         }
  973.  
  974.         return getDefaultColumnAlignment();
  975.     }
  976.  
  977.     /**
  978.      * Gets the default column alignment.
  979.      * This value is used when a column alignment is not specified.
  980.      * @return the default column alignment, one of: LEFT, CENTER, or RIGHT
  981.      * @see #setDefaultColumnAlignment
  982.      * @see #LEFT
  983.      * @see #CENTER
  984.      * @see #RIGHT
  985.      */
  986.     public int getDefaultColumnAlignment()
  987.     {
  988.         return defaultColumnAlignment;
  989.     }
  990.  
  991.     /**
  992.      * Sets the default column alignment.
  993.      * This value is used when an alignment is not specified for a column.
  994.      * @param newDefaultColumnAlignment the new default column alignment, one of: LEFT, CENTER, or RIGHT
  995.      * @see #getDefaultColumnAlignment
  996.      * @see #LEFT
  997.      * @see #CENTER
  998.      * @see #RIGHT
  999.      * @exception PropertyVetoException
  1000.      * if the specified property value is unacceptable
  1001.      */
  1002.     public void setDefaultColumnAlignment(int newDefaultColumnAlignment)
  1003.         throws PropertyVetoException
  1004.     {
  1005.         rangeCheckAlignment(newDefaultColumnAlignment);
  1006.  
  1007.         if (defaultColumnAlignment != newDefaultColumnAlignment)
  1008.         {
  1009.             Integer oldValue = new Integer(defaultColumnAlignment);
  1010.             Integer newValue = new Integer(newDefaultColumnAlignment);
  1011.  
  1012.             vetos.fireVetoableChange("defaultColumnAlignment",oldValue,newValue);
  1013.  
  1014.             defaultColumnAlignment = newDefaultColumnAlignment;
  1015.  
  1016.             changes.firePropertyChange("defaultColumnAlignment",oldValue,newValue);
  1017.  
  1018.             triggerRedraw();
  1019.         }
  1020.     }
  1021.  
  1022.     /**
  1023.      * Sets the the width of each column in pixels.
  1024.      * Setting the column sizes to null will turn auto resizing on.
  1025.      * @param list a string array containing column sizes in pixels
  1026.      * @see #getColumnSizes
  1027.      * @exception PropertyVetoException
  1028.      * if the specified property value is unacceptable
  1029.      */
  1030.     public void setColumnSizes(String[] list)
  1031.         throws PropertyVetoException
  1032.     {
  1033.         //Supress redraws
  1034.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  1035.  
  1036.         try
  1037.         {
  1038.             //Preprocess list (zero elements == null)
  1039.             if (list != null && list.length == 0)
  1040.                 list = null;
  1041.  
  1042.             //More preprocessing of lists (users could pass array or single string with ; as separators)
  1043.             list = tokenizeStringArrayIfNeeded(list);
  1044.  
  1045.             String[] oldValue = getColumnSizes();
  1046.  
  1047.             vetos.fireVetoableChange("columnSizes", oldValue, list);
  1048.  
  1049.             if (list != null)
  1050.             {
  1051.                 //???RKM??? What do we do if the size passed in is different than the num columns ???
  1052.  
  1053.                 // Initialize the columnSizes array again
  1054.                 columnSizes = new int[list.length];
  1055.  
  1056.                 int total = 0;
  1057.  
  1058.                 for (int i = 0; i < list.length; i++)
  1059.                 {
  1060.                     int stringValue = 10;
  1061.                     try
  1062.                     {
  1063.                         String listElem = list[i];
  1064.                         if (listElem != null)
  1065.                             stringValue = Integer.parseInt(listElem);
  1066.                     }
  1067.                     catch (Exception e)
  1068.                     {
  1069.                     }
  1070.                     columnSizes[i] = stringValue;
  1071.  
  1072.                     if (splitters.length > i + 1)
  1073.                         splitters[i+1] = total += columnSizes[i];
  1074.                 }
  1075.  
  1076.                 if (splitters.length > 1)
  1077.                     splitters[0] = 0;
  1078.             }
  1079.             else
  1080.             {
  1081.                 columnSizes = null;
  1082.                 adjustHeadings();
  1083.             }
  1084.  
  1085.             changes.firePropertyChange("columnSizes", oldValue, list);
  1086.         }
  1087.         finally
  1088.         {
  1089.             triggerRedraw();
  1090.             setSupressRedraw(wasSuppressingRedraw);
  1091.         }
  1092.     }
  1093.  
  1094.     /**
  1095.      * Returns a string array containing the width of each column in pixels.
  1096.      * If columns are being automatically sized, it will return null.
  1097.      * @see #setColumnSizes
  1098.      */
  1099.     public String[] getColumnSizes()
  1100.     {
  1101.         return intArrayToStringArray(columnSizes);
  1102.     }
  1103.  
  1104.     /**
  1105.      * Get the column size in pixels for the specified column.
  1106.      * @param i the zero-relative column index
  1107.      */
  1108.     public int getColumnSize(int i)
  1109.     {
  1110.         return splitters[i + 1];
  1111.     }
  1112.  
  1113.     /**
  1114.      * Sets the specified row as the selected row.
  1115.      * @param row the zero-relative index of the row to selectRow
  1116.      * @exception IllegalArgumentException if the row parameter is out of range
  1117.      * @see getSelectedRow
  1118.      */
  1119.     public void setSelectedRow(int row) throws IllegalArgumentException
  1120.     {
  1121.         if (row < 0 || row > cells.rows() - 1){
  1122.             Object[] args = { new Integer(row) };
  1123.             throw new IllegalArgumentException(MessageFormat.format(errors.getString("InvalidRowNumber"), args));
  1124.         }
  1125.  
  1126.         selectRow(row);
  1127.     }
  1128.  
  1129.     /**
  1130.      * Returns the zero-relative index of the currently selected row.
  1131.      * Returns -1 if nothing is selected, or if more than one item is selected (in multi-mode)
  1132.      * @see #setSelectedRow
  1133.      * @see #isMultipleMode
  1134.      * @see #setMultipleMode
  1135.      */
  1136.     public int getSelectedRow()
  1137.     {
  1138.         int[] selected = getSelectedRows();
  1139.  
  1140.         if (selected.length == 1)
  1141.             return selected[0];
  1142.         return -1;
  1143.     }
  1144.  
  1145.     /**
  1146.      * Selects all rows in a MultiList. Does nothing if multiMode is off.
  1147.      * @see #deselectAll
  1148.      * @see #isMultipleMode
  1149.      * @see #setMultipleMode
  1150.      */
  1151.     public void selectAll()
  1152.     {
  1153.         //Only lists in multi-mode can do this
  1154.         if (!multiSelect)
  1155.             return;
  1156.  
  1157.         //Select all rows
  1158.         int numRows = getNumberOfRows();
  1159.         for (int i = 0;i < numRows;i++)
  1160.             selectRow(i);
  1161.     }
  1162.  
  1163.     /**
  1164.      * Deselects all rows in a MultiList.
  1165.      * @see #selectAll
  1166.      */
  1167.     public void deselectAll()
  1168.     {
  1169.         int[] selectedRows = getSelectedRows();
  1170.         for (int i = 0;i < selectedRows.length;i++)
  1171.             deselectRow(selectedRows[i]);
  1172.     }
  1173.  
  1174.     /**
  1175.      * Returns an integer index for each selected row.
  1176.      * @return the zero-relative indexes of the selected rows
  1177.      * @see #getSelectedRow
  1178.      * @see #setSelectedRow
  1179.      */
  1180.     public int[] getSelectedRows()
  1181.     {
  1182.         int size = highlightedRows.size();
  1183.         int count = 0;
  1184.  
  1185.         for (int i = 0; i < size; i++)
  1186.             if (highlightedRows.get(i))
  1187.                 count++;
  1188.  
  1189.         int[] selections = new int[count];
  1190.  
  1191.         count = 0;
  1192.         for (int i = 0; i < size; i++)
  1193.         {
  1194.             if (highlightedRows.get(i))
  1195.             {
  1196.                 selections[count++] = i;
  1197.             }
  1198.         }
  1199.  
  1200.         return selections;
  1201.     }
  1202.  
  1203.     /**
  1204.      * Returns an array of Objects (Integers), one for each selected row index.
  1205.      * This is a standard method of the java.awt.ItemSelectable interface.
  1206.      * @return the zero-relative indexes of the selected rows
  1207.      * May return null (no rows selected)
  1208.      * @see #getSelectedRows
  1209.      * @see #setSelectedRow
  1210.      * @see java.awt.ItemSelectable
  1211.      */
  1212.     public Object[] getSelectedObjects()
  1213.     {
  1214.         int[] temp = getSelectedRows();
  1215.         int length = temp.length;
  1216.  
  1217.         Integer[] newArray = new Integer[length];
  1218.  
  1219.         for (int i = 0; i < length; i++)
  1220.         {
  1221.             newArray[i] = new Integer(temp[i]);
  1222.         }
  1223.  
  1224.         return newArray;
  1225.     }
  1226.  
  1227.     /**
  1228.      * Sets the minimum allowable column width.
  1229.      * @param size the minimum column width in pixels
  1230.      * @see #getMinColumnWidth
  1231.      * @exception PropertyVetoException
  1232.      * if the specified property value is unacceptable
  1233.      */
  1234.     public void setMinColumnWidth(int size) throws PropertyVetoException
  1235.     {
  1236.         if (minColumnWidth != size && size > 0)
  1237.         {
  1238.             Integer oldValue = new Integer(minColumnWidth);
  1239.             Integer newValue = new Integer(size);
  1240.  
  1241.             vetos.fireVetoableChange("minColumnWidth", oldValue, newValue);
  1242.  
  1243.             minColumnWidth = size;
  1244.  
  1245.             changes.firePropertyChange("minColumnWidth", oldValue, newValue);
  1246.         }
  1247.     }
  1248.  
  1249.     /**
  1250.      * Sets the internal flag to supress redraw.
  1251.      * Call this to supress redraw, saving the returned boolean. After you've done
  1252.      * what you wanted to do restore the flag.
  1253.      * @param supressRedraw the new value for the supressing of redraw flags
  1254.      */
  1255.     public boolean setSupressRedraw(boolean supressRedraw)
  1256.     {
  1257.         //Save the old value, so we can return it
  1258.         boolean wasSupressingRedraw = isSuppressRedraw;
  1259.  
  1260.         //Set the new flag
  1261.         isSuppressRedraw = supressRedraw;
  1262.  
  1263.         //If the caller turned on redraw
  1264.         if (isSuppressRedraw != wasSupressingRedraw && !isSuppressRedraw)
  1265.         {
  1266.             if (redrawWasSupressed)
  1267.             {
  1268.                 redrawWasSupressed = false;
  1269.                 triggerRedraw();
  1270.             }
  1271.         }
  1272.  
  1273.         //Return the old value
  1274.         return wasSupressingRedraw;
  1275.     }
  1276.  
  1277.     /**
  1278.      * Internal helper routine.
  1279.      * Resizes the heading to match the number of columns.
  1280.      * @param column the number of columns
  1281.      */
  1282.     protected void resizeHeadings(int column)
  1283.     {
  1284.         if (column == headings.length)
  1285.             return;
  1286.  
  1287.         int columnsToAdd = column - headings.length;
  1288.  
  1289.         String[] newHeadings = new String[headings.length + columnsToAdd];
  1290.         int[] newSplitters = new int[newHeadings.length + 1];
  1291.  
  1292.         //Transfer over old columns
  1293.         {
  1294.             for (int i = 0;i < newHeadings.length;i++)
  1295.             {
  1296.                 if (i < headings.length)
  1297.                 {
  1298.                     newHeadings[i] = headings[i];
  1299.                     newSplitters[i + 1] = splitters[i + 1];
  1300.                 }
  1301.                 else
  1302.                 {
  1303.                     newHeadings[i] = ("Column " + (i + 1));
  1304.                     newSplitters[i + 1] = newSplitters[i] + getMinColumnWidth();
  1305.                 }
  1306.             }
  1307.         }
  1308.  
  1309.         headings = newHeadings;
  1310.         splitters = newSplitters;
  1311.  
  1312.         if (columnSizes == null)
  1313.             forceColumnSizeRecalc = true;
  1314.     }
  1315.  
  1316.     /**
  1317.      * Initializes all the MultiList cells with text. Each string in the provided
  1318.      * array initializes one row. The contents of each column are separated by
  1319.      * ";". For example, the string "col0;col1;col2" would result in "col0"
  1320.      * being placed in column 0, "col1" in column 1, and "col2" in column 2.
  1321.      * Any remaining columns will get cleared.
  1322.      * @param items the string array used to initialize all cell contents
  1323.      * @see #getListItems
  1324.      * @exception PropertyVetoException
  1325.      * if the specified property value is unacceptable
  1326.      */
  1327.     public void setListItems(String[] items) throws PropertyVetoException
  1328.     {
  1329.         //Supress redraws
  1330.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  1331.  
  1332.         try
  1333.         {
  1334.             String[] oldValue = getListItems();
  1335.  
  1336.             vetos.fireVetoableChange("listItems", oldValue, items);
  1337.  
  1338.             clear();
  1339.  
  1340.             for (int row = 0; row < items.length; row++)
  1341.             {
  1342.                 String s = items[row];
  1343.  
  1344.                 // Make sure that we handle null strings by creating an empty string in it's place
  1345.                 if (s == null)
  1346.                     s = "";
  1347.  
  1348.                 int len = s.length();
  1349.  
  1350.                 int start, end, col;
  1351.                 for (end = col = start = 0; end <= len /*&& col < headings.length*/; ++end)
  1352.                 {
  1353.                     if (end == len || s.charAt(end) == ';')
  1354.                     {
  1355.                         addCell(row, col++, s.substring(start, end), null);
  1356.                         start = end + 1;
  1357.  
  1358.                         //Handle adding of headings (hey, if the user puts in the data he/she probably wants to see it)
  1359.                         if (col > headings.length)
  1360.                             resizeHeadings(col);
  1361.                     }
  1362.                 }
  1363.  
  1364.                 while (col < headings.length)
  1365.                     addCell(row, col++, "", null);
  1366.             }
  1367.  
  1368.             changes.firePropertyChange("listItems", oldValue, getListItems());
  1369.         }
  1370.         finally
  1371.         {
  1372.             triggerRedraw();
  1373.             setSupressRedraw(wasSuppressingRedraw);
  1374.         }
  1375.     }
  1376.  
  1377.     /**
  1378.      * Returns the text contents of all the cells as a string array. A new
  1379.      * string is used for each row. The contents of each row's column are
  1380.      * separated by ";". For example, the string "col0;;col2" would result
  1381.      * from a "col0" in column 0, an empty column 1, and  "col2" in column 2.
  1382.      * @return the string array containing the text of all the cells
  1383.      * @see #setListItems
  1384.      */
  1385.     public String[] getListItems()
  1386.     {
  1387.         Vector v = new Vector();
  1388.         String s;
  1389.         boolean lastNull = false;
  1390.         int row, col;
  1391.         String rowString;
  1392.         String[] items = new String[cells.rows()];
  1393.  
  1394.         for (row = 0; row < cells.rows(); row++) {
  1395.             rowString = "";
  1396.  
  1397.             for (col = 0; col < headings.length; col++) {
  1398.                 s = getCellText(row, col);
  1399.                 if (col != 0) rowString += ";";
  1400.                 rowString += (s != null) ? s : "";
  1401.             }
  1402.  
  1403.             items[row] = rowString;
  1404.         }
  1405.  
  1406.         return items;
  1407.     }
  1408.  
  1409.     /**
  1410.      * Sets the flag to allow sorting of columns when a heading is selected.
  1411.      * @param b if true allow columns to be sorted when column heading is clicked;
  1412.      * if false, no sorting will occur
  1413.      * @see #isAllowSorting
  1414.      * @exception PropertyVetoException
  1415.      * if the specified property value is unacceptable
  1416.      */
  1417.     public void setAllowSorting(boolean b) throws PropertyVetoException
  1418.     {
  1419.         if (allowSorting != b)
  1420.         {
  1421.             Boolean oldValue = new Boolean(allowSorting);
  1422.             Boolean newValue = new Boolean(b);
  1423.  
  1424.             vetos.fireVetoableChange("allowSorting", oldValue, newValue);
  1425.  
  1426.             allowSorting = b;
  1427.  
  1428.             changes.firePropertyChange("allowSorting", oldValue, newValue);
  1429.         }
  1430.     }
  1431.  
  1432.     /**
  1433.      * Gets the flag controlling sorting of columns when a heading is selected.
  1434.      * @return true if columns will be sorted when column heading is clicked;
  1435.      * if false, no sorting will occur
  1436.      * @see #setAllowSorting
  1437.      */
  1438.     public boolean isAllowSorting()
  1439.     {
  1440.         return allowSorting;
  1441.     }
  1442.  
  1443.     /**
  1444.      * Sets the flag to allow resizing of columns via the user interface.
  1445.      * @param allowResizing if true the user may resize columns via the UI;
  1446.      * if false, columns cannot be resized via the UI
  1447.      * @see #isAllowResizingOfColumns
  1448.      * @exception PropertyVetoException
  1449.      * if the specified property value is unacceptable
  1450.      */
  1451.     public void setAllowResizingOfColumns(boolean allowResizing)
  1452.         throws PropertyVetoException
  1453.     {
  1454.         if (allowResizingOfColumns != allowResizing)
  1455.         {
  1456.             Boolean oldValue = new Boolean(allowResizingOfColumns);
  1457.             Boolean newValue = new Boolean(allowResizing);
  1458.  
  1459.             vetos.fireVetoableChange("allowResizingOfColumns", oldValue, newValue);
  1460.  
  1461.             allowResizingOfColumns = allowResizing;
  1462.  
  1463.             changes.firePropertyChange("allowResizingOfColumns", oldValue, newValue);
  1464.         }
  1465.     }
  1466.  
  1467.     /**
  1468.      * Gets whether columns may currently be resized by the user via the user interface.
  1469.      * @return true if resizing of columns via the UI is allowed;
  1470.      * if false, columns cannot be resized via the UI
  1471.      * @see #setAllowResizingOfColumns
  1472.      */
  1473.     public boolean isAllowResizingOfColumns()
  1474.     {
  1475.         return allowResizingOfColumns;
  1476.     }
  1477.  
  1478.     /**
  1479.      * Returns the minimum allowable column width in pixels.
  1480.      * @see #setMinColumnWidth
  1481.      */
  1482.     public int getMinColumnWidth()
  1483.     {
  1484.         return minColumnWidth;
  1485.     }
  1486.  
  1487.     /**
  1488.      * Adjusts the columns to default width.
  1489.      * The width of the columns is adjusted so that all columns fit into the
  1490.      * overall width of the MultiList.
  1491.      */
  1492.     public void adjustHeadings()
  1493.     {
  1494.         if (headings.length == 0)
  1495.         {
  1496.             internalCreateColumns(0);
  1497.             triggerRedraw();
  1498.         }
  1499.         else
  1500.         {
  1501.             Dimension d = size();
  1502.             int w = 0, width = (d.width - verticalScrollbarWidth) / headings.length, r = (d.width - verticalScrollbarWidth) - headings.length * width;
  1503.             splitters[0] = 0;
  1504.             for (int i = 0; i < headings.length; ++i)
  1505.                 splitters[i+1] = w += width+(i<r?1:0);
  1506.         }
  1507.     }
  1508.  
  1509.     /**
  1510.      * Initializes the MultiList with the specified total numer of columns.
  1511.      * All old column data is lost. Row data is preserved. Note that this
  1512.      * method is identical to the method setNumberOfCols(), but doesn't
  1513.      * allow the change to be vetoed.
  1514.      * @param i the new number of columns
  1515.      * @see #setNumberOfCols
  1516.      * @see #getNumberOfCols
  1517.      */
  1518.     public void createColumns(int i)
  1519.     {
  1520.         internalCreateColumns(i);
  1521.  
  1522.         triggerRedraw();
  1523.     }
  1524.  
  1525.     /**
  1526.      * Internal helper routine.
  1527.      * Allocates room for the specified number of columns.
  1528.      * @param i the new number of columns
  1529.      */
  1530.     protected void internalCreateColumns(int i)
  1531.     {
  1532.         headings = new String[i];
  1533.         splitters = new int[i + 1];
  1534.  
  1535.         //???RKM??? I do not think this is wanted in all cases (setNumberOfCols)
  1536.         columnAlignments = null;
  1537.         columnSizes = null;
  1538.     }
  1539.  
  1540.     /**
  1541.      * Removes all rows from the MultiList.
  1542.      * @see #removeRow
  1543.      */
  1544.     public void clear()
  1545.     {
  1546.         cells.removeAllElements();
  1547.  
  1548.         //Stop dragging
  1549.         xDragLast = -1;
  1550.         isDragging = false;
  1551.  
  1552.         selectedRow = -1;
  1553.         highlightedRows = new BitSet();
  1554.  
  1555.         topRow = 0;
  1556.         sbVPosition = 0;
  1557.  
  1558.         triggerRedraw();
  1559.     }
  1560.  
  1561.     /**
  1562.      * Removes a single row from the MultiList.
  1563.      * @param row the zero-relative index of the row to remove
  1564.      * @see #clear
  1565.      */
  1566.     public void removeRow(int row)
  1567.     {
  1568.         int numRows = cells.rows();
  1569.  
  1570.         //Make certain row is in range
  1571.         if (row < 0 || row > numRows - 1)
  1572.             return;
  1573.  
  1574.         //Remove the row from the matrix
  1575.         cells.removeRow(row);
  1576.  
  1577.         //If the row is selected, remove it from the selection
  1578.         if (highlightedRows.get(row))
  1579.             highlightedRows.clear(row);
  1580.  
  1581.         //Move selections down
  1582.         for (int i = row + 1;i < numRows;i++)
  1583.         {
  1584.             if (highlightedRows.get(i))
  1585.             {
  1586.                 highlightedRows.clear(i);
  1587.                 highlightedRows.set(i - 1);
  1588.             }
  1589.         }
  1590.  
  1591.         triggerRedraw();
  1592.     }
  1593.  
  1594.     /**
  1595.      * Gets the object used to compare cells for sorting the specified column.
  1596.      * If no sorter has been explicitly specified, the default column sorter
  1597.      * will be returned.
  1598.      * @param column the zero-relative column index
  1599.      * @return the object used to compare column cells
  1600.      * @see #setColumnSorter
  1601.      */
  1602.     public CompareCells getColumnSorter(int column)
  1603.     {
  1604.         CompareCells compareCellsFunc = (CompareCells)columnCompareCellsRoutines.get(new Integer(column));
  1605.         if (compareCellsFunc == null)
  1606.             compareCellsFunc = getDefaultColumnSorter();
  1607.         return compareCellsFunc;
  1608.     }
  1609.  
  1610.     /**
  1611.      * Sets the object used to compare cells for sorting the specified column.
  1612.      * @param column the zero-relative column index
  1613.      * @param compare the object used to compare column cells
  1614.      * @see #getColumnSorter
  1615.      */
  1616.     public void setColumnSorter(int column, CompareCells compare)
  1617.     {
  1618.         columnCompareCellsRoutines.put(new Integer(column),compare);
  1619.     }
  1620.  
  1621.     /**
  1622.      * Gets the default object used to compare column cells for sorting.
  1623.      * @return the object used to compare column cells by default
  1624.      * @see #setDefaultColumnSorter
  1625.      * @see #getColumnSorter
  1626.      */
  1627.     public CompareCells getDefaultColumnSorter()
  1628.     {
  1629.         return defaultColumnSorter;
  1630.     }
  1631.  
  1632.     /**
  1633.      * Sets the default object used to compare column cells for sorting.
  1634.      * @param newDefaultSorter the object used to compare column cells by default
  1635.      * @see #getDefaultColumnSorter
  1636.      * @see #setColumnSorter
  1637.      */
  1638.     public void setDefaultColumnSorter(CompareCells newDefaultSorter)
  1639.     {
  1640.         defaultColumnSorter = newDefaultSorter;
  1641.     }
  1642.  
  1643.     /**
  1644.      * Sets the text of a cell at the given row and column position.
  1645.      * @param r the zero-relative row index
  1646.      * @param c the zero-relative column index
  1647.      * @param s the new cell text
  1648.      * @see #addImageCell
  1649.      * @see #addCell
  1650.      */
  1651.     public void addTextCell(int r, int c, String s)
  1652.     {
  1653.         if (s == null)
  1654.             s = new String("");
  1655.         TextAndImageCell cell = new TextAndImageCell(this, s);
  1656.         addCellImpl(r,c,cell);
  1657.     }
  1658.  
  1659.     /**
  1660.      * Sets the image of a cell at the given row and column position.
  1661.      * @param r the zero-relative row index
  1662.      * @param c the zero-relative column index
  1663.      * @param i the new cell image
  1664.      * @see #addTextCell
  1665.      * @see #addCell
  1666.      */
  1667.     public void addImageCell(int r, int c, Image i)
  1668.     {
  1669.         TextAndImageCell cell = new TextAndImageCell(this, i);
  1670.         addCellImpl(r,c,cell);
  1671.     }
  1672.  
  1673.     /**
  1674.      * Sets the contents of a cell, both text and image.
  1675.      * @param r the zero-relative row index
  1676.      * @param c the zero-relative column index
  1677.      * @param s the new cell text
  1678.      * @param i the new cell image
  1679.      * @see #addTextCell
  1680.      * @see #addImageCell
  1681.      */
  1682.     public void addCell(int r, int c, String s, Image i)
  1683.     {
  1684.         TextAndImageCell cell = new TextAndImageCell(this, s, i);
  1685.         addCellImpl(r,c,cell);
  1686.     }
  1687.  
  1688.     /**
  1689.      * Sets the contents of a cell to the given cell.
  1690.      * @param row the zero-relative row index
  1691.      * @param column the zero-relative column index
  1692.      * @param cell the new cell
  1693.      * @see #addCell
  1694.      * @see #addTextCell
  1695.      * @see #addImageCell
  1696.      */
  1697.     public void addCell(int row, int column, Cell cell)
  1698.     {
  1699.         addCellImpl(row,column,cell);
  1700.     }
  1701.  
  1702.     /**
  1703.      * Adds a cell to the MultiList in the row and column specified.
  1704.      * All addCell forms call this method.
  1705.      * @param row the zero-relative row index
  1706.      * @param column the zero-relative column index
  1707.      * @param cell the cell that is being added to the MultiList
  1708.      */
  1709.     protected void addCellImpl(int row, int column, Cell cell)
  1710.     {
  1711.         cells.updateElement(row, column, cell);
  1712.  
  1713.         triggerRedraw();
  1714.     }
  1715.  
  1716.     /**
  1717.      * Returns the text of the specified cell.
  1718.      * @param r the zero-relative row index
  1719.      * @param c the zero-relative column index
  1720.      * @return the cell text or the empty string ("") if the cell is not allocated
  1721.      * @see #getCellImage
  1722.      * @see #addCell
  1723.      * @see #addTextCell
  1724.      */
  1725.     public String getCellText(int r, int c)
  1726.     {
  1727.         try
  1728.         {
  1729.             //Calling elementAt will throw if the element is not allocated
  1730.             TextAndImageCell cell = (TextAndImageCell)cells.elementAt(r,c);
  1731.  
  1732.             //The element may be allocated, but that doesn't mean there's an object there
  1733.             if (cell != null)
  1734.                 return cell.getText();
  1735.         }
  1736.         catch(ArrayIndexOutOfBoundsException e)
  1737.         {
  1738.             //Fall through, so an empty string is returned
  1739.         }
  1740.         catch(ClassCastException e)
  1741.         {
  1742.             //Fall through, so an empty string is returned
  1743.         }
  1744.  
  1745.         return "";
  1746.     }
  1747.  
  1748.     /**
  1749.      * Returns the image of the specified cell, if any.
  1750.      * @param r the zero-relative row index
  1751.      * @param c the zero-relative column index
  1752.      * @return the cell image, or null if the cell is not allocated
  1753.      * @see #getCellText
  1754.      * @see #addCell
  1755.      * @see #addImageCell
  1756.      */
  1757.     public Image getCellImage(int r, int c)
  1758.     {
  1759.         try
  1760.         {
  1761.             //Calling elementAt will throw if the element is not allocated
  1762.             TextAndImageCell cell = (TextAndImageCell)cells.elementAt(r,c);
  1763.  
  1764.             //The element may be allocated, but that doesn't mean there's an object there
  1765.             if (cell != null)
  1766.                 return cell.getImage();
  1767.         }
  1768.         catch(ArrayIndexOutOfBoundsException e)
  1769.         {
  1770.             //Fall through, so null is returned
  1771.         }
  1772.         catch(ClassCastException e)
  1773.         {
  1774.             //Fall through, so null is returned
  1775.         }
  1776.  
  1777.         return null;
  1778.     }
  1779.  
  1780.     /**
  1781.      * Handles redrawing of this component on the screen.
  1782.      * This is a standard Java AWT method which gets called by the Java
  1783.      * AWT (repaint()) to handle repainting this component on the screen.
  1784.      * The graphics context clipping region is set to the bounding rectangle
  1785.      * of this component and its [0,0] coordinate is this component's
  1786.      * top-left corner.
  1787.      * Typically this method paints the background color to clear the
  1788.      * component's drawing space, sets graphics context to be the foreground
  1789.      * color, and then calls paint() to draw the component.
  1790.      *
  1791.      * It is overridden here to eliminate the unneeded repainting of the background.
  1792.      *
  1793.      * @param g the graphics context
  1794.      * @see java.awt.Component#repaint
  1795.      * @see #paint
  1796.      */
  1797.     public void update(Graphics g)
  1798.     {
  1799.         paint(g);
  1800.     }
  1801.  
  1802.     /**
  1803.      * Paints this component using the given graphics context.
  1804.      * This is a standard Java AWT method which typically gets called
  1805.      * by the AWT to handle painting this component. It paints this component
  1806.      * using the given graphics context. The graphics context clipping region
  1807.      * is set to the bounding rectangle of this component and its [0,0]
  1808.      * coordinate is this component's top-left corner.
  1809.      *
  1810.      * @param g the graphics context used for painting
  1811.      * @see java.awt.Component#repaint
  1812.      * @see #update
  1813.      */
  1814.     public void paint(Graphics g)
  1815.     {
  1816.         // we can enter here without an image if the multilist is created on
  1817.         // a panel without a peer (i.e., a tab panel).  Just to make sure, create
  1818.         // it if we need one...
  1819.  
  1820.         if (offscreenImage == null)
  1821.             redraw();
  1822.  
  1823.         if (offscreenImage != null)
  1824.         {
  1825.             Font f = g.getFont();
  1826.             if (f == null)
  1827.             {
  1828.                 f = headingFont;
  1829.                 setFont(f);
  1830.                 return;
  1831.             }
  1832.  
  1833.             Dimension s = size();
  1834.  
  1835.             //???RKM??? Why would it always redraw at design time???
  1836.             //???RKM??? DO NOT JUST TURN THIS BACK ON - PLEASE COME TALK TO ME (YOU HAVE FOUND A BUG)
  1837.             if (cachedWidth != s.width || cachedHeight != s.height || forceRedraw /*|| java.beans.Beans.isDesignTime()*/)
  1838.                 redraw();
  1839.  
  1840.             int heightMinusScrollbar = s.height - scrollbarHeight;
  1841.             int widthMinusScrollbar = s.width - verticalScrollbarWidth;
  1842.  
  1843.             g.translate(-sbHPosition, 0);
  1844.             if (sbVShow && sbHShow)
  1845.             {
  1846.                 g.setColor(Color.lightGray);
  1847.                 g.fillRect(sbHPosition + widthMinusScrollbar, heightMinusScrollbar, verticalScrollbarWidth, scrollbarHeight);
  1848.                 g.setColor(Color.black);
  1849.                 g.drawRect(sbHPosition + widthMinusScrollbar - 1, heightMinusScrollbar - 1, verticalScrollbarWidth, scrollbarHeight);
  1850.             }
  1851.  
  1852.             g.clipRect(sbHPosition,0,widthMinusScrollbar,heightMinusScrollbar);
  1853.             g.drawImage(offscreenImage, 0, 0, this);
  1854.  
  1855.             //Draw frame
  1856.             //???RKM??? This will flicker - fix
  1857.             g.setColor(Color.black);
  1858.             g.drawRect(sbHPosition,0, widthMinusScrollbar - 1, heightMinusScrollbar - 1);
  1859.         }
  1860.     }
  1861.  
  1862.     /**
  1863.      * Ensures the given column index is valid.
  1864.      * An IllegalArgumentException is thrown if it is out of range.
  1865.      * @param column the zero-relative column index to validate
  1866.      */
  1867.     protected void rangeCheckColumn(int column)
  1868.     {
  1869.         if (column < 0 || column > headings.length - 1)
  1870.             throw new IllegalArgumentException(errors.getString("InvalidColumnIndex") + column);
  1871.     }
  1872.  
  1873.     /**
  1874.      * Ensures the given column alignment value is valid.
  1875.      * An IllegalArgumentException is thrown if the value is invalid.
  1876.      * @param alignment the column alignment value to validate, should
  1877.      * be one of: LEFT, CENTER, or RIGHT
  1878.      * @see #LEFT
  1879.      * @see #CENTER
  1880.      * @see #RIGHT
  1881.      */
  1882.     protected void rangeCheckAlignment(int alignment)
  1883.     {
  1884.         if (alignment != LEFT && alignment != CENTER && alignment != RIGHT){
  1885.             Object[] args = { new Integer(alignment), "LEFT", "CENTER", "RIGHT" };
  1886.             throw new IllegalArgumentException(MessageFormat.format(errors.getString("InvalidAlignment"), args));
  1887.         }
  1888.     }
  1889.  
  1890.     /**
  1891.      * Determines if the vertical scrollbar needs to be shown.
  1892.      * If column sizes are being automatically determined,
  1893.      * forces a column size recalc as needed.
  1894.      */
  1895.     protected void calcVerticalScrollbarPosition(boolean minusHorizontalScrollbar)
  1896.     {
  1897.         Dimension s = size();
  1898.  
  1899.         //Calc if vertical scrollbar is needed
  1900.         if (cells.rows() * cellHeight > s.height - headingHeight - BORDER - (headingHeight > 0 ? 0 : BORDER) - (minusHorizontalScrollbar ? scrollbarHeight : 0))
  1901.         {
  1902.             sbVShow = true;
  1903.             verticalScrollbarWidth = verticalScrollbar.preferredSize().width - 1;
  1904.         }
  1905.         else
  1906.         {
  1907.             sbVShow = false;
  1908.             verticalScrollbarWidth = 0;
  1909.         }
  1910.  
  1911.         //Adjust column widths, if we are auto calculating
  1912.         if (columnSizes == null && (forceColumnSizeRecalc || (cachedWidth != s.width - verticalScrollbarWidth)))
  1913.             adjustHeadings();
  1914.         forceColumnSizeRecalc = false;
  1915.     }
  1916.  
  1917.     /**
  1918.      * Paints this component into an offscreen image for cleaner screen repaints.
  1919.      * It is not typically called directly.
  1920.      */
  1921.     public void redraw()
  1922.     {
  1923.         boolean wasSuppressingRedraw;
  1924.  
  1925.         Dimension s = size();
  1926.  
  1927.         //Set redraw to false
  1928.         forceRedraw = false;
  1929.  
  1930.         if (s.width == 0 || s.height == 0)
  1931.             return;
  1932.  
  1933.         //Suppress redraws.  I had to avoid using setSuppressRedraw to avoid looping. LAB.
  1934.         wasSuppressingRedraw = isSuppressRedraw;
  1935.         isSuppressRedraw = true;
  1936.         try
  1937.         {
  1938.             calcVerticalScrollbarPosition(false);
  1939.         }
  1940.         finally
  1941.         {
  1942.             isSuppressRedraw = wasSuppressingRedraw;
  1943.         }
  1944.  
  1945.         int lastSplitter = splitters[splitters.length-1];
  1946.  
  1947.         if (lastSplitter > s.width - verticalScrollbarWidth)
  1948.         {
  1949.             sbHShow = true;
  1950.             scrollbarHeight = horizontalScrollbar.preferredSize().height - 1;
  1951.  
  1952.             //Suppress redraws.  I had to avoid using setSuppressRedraw to avoid looping. LAB.
  1953.             wasSuppressingRedraw = isSuppressRedraw;
  1954.             isSuppressRedraw = true;
  1955.             try
  1956.             {
  1957.                 calcVerticalScrollbarPosition(true);
  1958.             }
  1959.             finally
  1960.             {
  1961.                 isSuppressRedraw = wasSuppressingRedraw;
  1962.             }
  1963.         }
  1964.         else
  1965.         {
  1966.             sbHShow = false;
  1967.             scrollbarHeight = 0;
  1968.         }
  1969.  
  1970.         //Post calc, set scrollbar positions
  1971.         if (!sbVShow)
  1972.             sbVPosition = 0;
  1973.         if (!sbHShow)
  1974.             sbHPosition = 0;
  1975.  
  1976.         {
  1977.             //Calc needed image width
  1978.             int imageWidth = Math.max(lastSplitter, s.width + sbHPosition);
  1979.  
  1980.             //Create a new offscreen image, if we need to
  1981.             if (offscreenImage == null ||
  1982.                 offscreenImageGraphics == null ||
  1983.                 cachedWidth != imageWidth ||
  1984.                 cachedHeight != s.height ||
  1985.                 cachedLastSplitter != lastSplitter ||
  1986.                 offscreenImage.getWidth(this) != imageWidth ||
  1987.                 forceFullRedraw)
  1988.             {
  1989.                 forceFullRedraw = false;
  1990.  
  1991.                 //If old offscreenImage, flush it
  1992.                 if (offscreenImage != null)
  1993.                 {
  1994.                     offscreenImage.flush();
  1995.                     offscreenImage = null;
  1996.                 }
  1997.  
  1998.                 //Create a new offscreen image
  1999.                 offscreenImage = createImage(imageWidth, s.height);
  2000.  
  2001.                 //Cache the height and width of the MultiList
  2002.                 cachedWidth = s.width;
  2003.                 cachedHeight = s.height;
  2004.  
  2005.                 //!!!RKM!!! Note: lastSplitter has to be cached due to the clipRect below
  2006.                 //???RKM??? This could be removed if we get a working setClip
  2007.                 //Cache last splitter
  2008.                 cachedLastSplitter = lastSplitter;
  2009.  
  2010.                 //If there previously was a graphics, dispose it
  2011.                 if (offscreenImageGraphics != null)
  2012.                 {
  2013.                     offscreenImageGraphics.dispose();
  2014.                     offscreenImageGraphics = null;
  2015.                 }
  2016.  
  2017.                 //Hook up to the new offscreenImage
  2018.                 if (offscreenImage != null)
  2019.                     offscreenImageGraphics = offscreenImage.getGraphics();
  2020.             }
  2021.         }
  2022.  
  2023.         if (offscreenImageGraphics != null)
  2024.         {
  2025.             offscreenImageGraphics.setColor(colorBg);
  2026.             offscreenImageGraphics.fillRect(0,0,offscreenImage.getWidth(this), s.height);
  2027.         }
  2028.  
  2029.         //Cache numRows, since cells.rows() is expensive
  2030.         int numRows = cells.rows();
  2031.  
  2032.         if (sbVShow)
  2033.         {
  2034.             int vis = getNumVisibleRows();
  2035.  
  2036.             verticalScrollbar.reshape(s.width - verticalScrollbarWidth,0,verticalScrollbarWidth,s.height - scrollbarHeight);
  2037.             verticalScrollbar.setValues(sbVPosition, vis, 0, numRows - (isSun1_1 ? 0 : vis));
  2038.             verticalScrollbar.setPageIncrement(vis-1);
  2039.             verticalScrollbar.show();
  2040.         }
  2041.         else
  2042.         {
  2043.             topRow = 0;
  2044.             verticalScrollbar.hide();
  2045.         }
  2046.  
  2047.         if (sbHShow)
  2048.         {
  2049.             horizontalScrollbar.reshape(0,s.height-scrollbarHeight,s.width-verticalScrollbarWidth,scrollbarHeight);
  2050.             horizontalScrollbar.setValues(sbHPosition, s.width-verticalScrollbarWidth, 0, lastSplitter-(isSun1_1?0:(s.width-verticalScrollbarWidth)));
  2051.             horizontalScrollbar.setPageIncrement(s.width-verticalScrollbarWidth);
  2052.             horizontalScrollbar.setLineIncrement(hScrollbarLineIncrement);
  2053.             horizontalScrollbar.show();
  2054.         }
  2055.         else
  2056.         {
  2057.             horizontalScrollbar.hide();
  2058.         }
  2059.  
  2060.         if (offscreenImageGraphics != null)
  2061.         {
  2062.             if (headings.length > 0)
  2063.             {
  2064.                 drawHeading(false);
  2065.  
  2066.                 if (lastSplitter > 0)
  2067.                     offscreenImageGraphics.clipRect(0,0,lastSplitter,s.height);
  2068.  
  2069.                 int count = cellHeight > 0 ? getNumVisibleRows() : 0;
  2070.                 if (count > numRows)
  2071.                     count = numRows;
  2072.  
  2073.                 offscreenImageGraphics.setFont(cellFont);
  2074.                 drawRows(topRow, count, false);
  2075.             }
  2076.         }
  2077.     }
  2078.  
  2079.     /**
  2080.      * This routine changes the selected row, handling multiple selections
  2081.      * as needed.
  2082.      * @param newSelection the zero-relative index of the newly selected row
  2083.      * @param meta the state of the modifier keys from Event.modifiers
  2084.      * @see setSelectedRow
  2085.      * @see Event#CTRL_MASK
  2086.      * @see Event#SHIFT_MASK
  2087.      */
  2088.     public void changeSelection(int newSelection, int meta)
  2089.     {
  2090.         //Supress redraws
  2091.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  2092.  
  2093.         try
  2094.         {
  2095.             int numberOfRows = getNumberOfRows();
  2096.  
  2097.             if (!((newSelection < numberOfRows) && (newSelection > -1)))
  2098.             {
  2099.                 int[] selRows = getSelectedRows();
  2100.                 if (multiSelect)
  2101.                 {
  2102.                     if (((meta & Event.SHIFT_MASK) == Event.SHIFT_MASK) || ((meta & Event.CTRL_MASK) == Event.CTRL_MASK))
  2103.                         return;
  2104.                     deselectEvent(newSelection, selRows);
  2105.                 }
  2106.                 highlightedRows = new BitSet();
  2107.                 paintSelection(selRows);
  2108.                 repaint();
  2109.                 return;
  2110.             }
  2111.  
  2112.             if (multiSelect)
  2113.                 multiSel(newSelection, meta);
  2114.             else
  2115.                 singleSel(newSelection, meta);
  2116.  
  2117.             // scroll a page UPWARD
  2118.             if (newSelection < topRow)
  2119.             {
  2120.                 topRow = newSelection;
  2121.  
  2122.                 if (topRow < 0)
  2123.                     topRow = 0;
  2124.  
  2125.                 sbVPosition = topRow;
  2126.                 verticalScrollbar.setValue(sbVPosition);
  2127.  
  2128.                 triggerRedraw();
  2129.             }
  2130.  
  2131.             // scroll a page DOWNWARD
  2132.             if ((newSelection-topRow+1) > getNumVisibleRows())
  2133.             {
  2134.                 topRow = topRow + 1;
  2135.  
  2136.                 if (topRow > numberOfRows-1)
  2137.                     topRow = newSelection;
  2138.  
  2139.                 sbVPosition = topRow;
  2140.                 verticalScrollbar.setValue(sbVPosition);
  2141.  
  2142.                 triggerRedraw();
  2143.             }
  2144.  
  2145.             selectedRow = newSelection;
  2146.             drawRows(selectedRow, 1, true);
  2147.         }
  2148.         finally
  2149.         {
  2150.             triggerRedraw();
  2151.             setSupressRedraw(wasSuppressingRedraw);
  2152.         }
  2153.     }
  2154.  
  2155.     /**
  2156.      * Tells this component that it has been added to a container.
  2157.      * This is a standard Java AWT method which gets called by the AWT when
  2158.      * this component is added to a container. Typically, it is used to
  2159.      * create this component's peer.
  2160.      *
  2161.      * It has been overridden here to add local event listeners.
  2162.      *
  2163.      * @see #removeNotify
  2164.      */
  2165.     public synchronized void addNotify()
  2166.     {
  2167.         super.addNotify();
  2168.         errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  2169.  
  2170.         //Hook up listeners
  2171.         if (mouse == null)
  2172.         {
  2173.             mouse = new Mouse();
  2174.             addMouseListener(mouse);
  2175.         }
  2176.         if (mouseMotion == null)
  2177.         {
  2178.             mouseMotion = new MouseMotion();
  2179.             addMouseMotionListener(mouseMotion);
  2180.         }
  2181.         if (key == null)
  2182.         {
  2183.             key = new Key();
  2184.             addKeyListener(key);
  2185.         }
  2186.         if (adjustment == null)
  2187.         {
  2188.             adjustment = new Adjustment();
  2189.             verticalScrollbar.addAdjustmentListener(adjustment);
  2190.             horizontalScrollbar.addAdjustmentListener(adjustment);
  2191.         }
  2192.         if (focus == null)
  2193.         {
  2194.             focus = new Focus();
  2195.             addFocusListener(focus);
  2196.         }
  2197.     }
  2198.  
  2199.     /**
  2200.      * Tells this component that it is being removed from a container.
  2201.      * This is a standard Java AWT method which gets called by the AWT when
  2202.      * this component is removed from a container. Typically, it is used to
  2203.      * destroy the peers of this component and all its subcomponents.
  2204.      *
  2205.      * It has been overridden here to remove local event listeners.
  2206.      *
  2207.      * @see #addNotify
  2208.      */
  2209.     public synchronized void removeNotify()
  2210.     {
  2211.         if (mouse != null)
  2212.         {
  2213.             removeMouseListener(mouse);
  2214.             mouse = null;
  2215.         }
  2216.         if (mouseMotion != null)
  2217.         {
  2218.             removeMouseMotionListener(mouseMotion);
  2219.             mouseMotion = null;
  2220.         }
  2221.         if (key != null)
  2222.         {
  2223.             removeKeyListener(key);
  2224.             key = null;
  2225.         }
  2226.         if (adjustment != null)
  2227.         {
  2228.             verticalScrollbar.removeAdjustmentListener(adjustment);
  2229.             horizontalScrollbar.removeAdjustmentListener(adjustment);
  2230.             adjustment = null;
  2231.         }
  2232.         if (focus != null)
  2233.         {
  2234.             removeFocusListener(focus);
  2235.             focus = null;
  2236.         }
  2237.  
  2238.         super.removeNotify();
  2239.     }
  2240.  
  2241.     private void multiSel(int newSelection, int meta)
  2242.     {
  2243.             if ((meta & Event.CTRL_MASK) == Event.CTRL_MASK)
  2244.             {
  2245.                 if (highlightedRows.get(newSelection))
  2246.                     deselectRow(newSelection);
  2247.                 else
  2248.                 {
  2249.                     removeSelectionBorder();
  2250.                     selectRow(newSelection);
  2251.                 }
  2252.             }
  2253.             else if ((meta & Event.SHIFT_MASK) == Event.SHIFT_MASK)
  2254.             {
  2255.                 if (selectedRow == -1)
  2256.                     selectedRow = newSelection;
  2257.  
  2258.                 for (int i=Math.min(selectedRow, newSelection);
  2259.                      i<=Math.max(selectedRow, newSelection);
  2260.                      i++)
  2261.                 {
  2262.                     if (i>=0)
  2263.                     {
  2264.                         highlightedRows.set(i);
  2265.                     }
  2266.                 }
  2267.                 sourceItemEvent(ItemEvent.SELECTED);
  2268.                 selectedRow = -1;
  2269.                 paintSelection(getSelectedRows());
  2270.             }
  2271.             else
  2272.             {
  2273.                 int[] selRows = getSelectedRows();
  2274.                 deselectEvent(newSelection, selRows);
  2275.                 highlightedRows = new BitSet();
  2276.                 removeSelectionBorder();
  2277.                 selectedRow = -1;
  2278.                 paintSelection(selRows);
  2279.                 selectRow(newSelection);
  2280.             }
  2281.     }
  2282.  
  2283.     private void singleSel(int newSelection, int meta)
  2284.     {
  2285.         if (selectedRow >= 0 && highlightedRows.get(selectedRow))
  2286.         {
  2287.             if (selectedRow != newSelection)
  2288.             {
  2289.                 deselectRow(selectedRow);
  2290.                 selectRow(newSelection);
  2291.             }
  2292.             else if ((meta & Event.CTRL_MASK) == Event.CTRL_MASK)
  2293.             {
  2294.                 deselectRow(selectedRow);
  2295.             }
  2296.         }
  2297.         else
  2298.         {
  2299.             removeSelectionBorder();
  2300.             selectRow(newSelection);
  2301.         }
  2302.     }
  2303.  
  2304.     private void deselectEvent(int newSelection, int[] selRows)
  2305.     {
  2306.         int numberSelected = selRows.length;
  2307.  
  2308.         if ((numberSelected>1) ||
  2309.             ((numberSelected==1) &&
  2310.              (selectedRow!=newSelection)))
  2311.         {
  2312.             sourceItemEvent(ItemEvent.DESELECTED);
  2313.         }
  2314.     }
  2315.  
  2316.     /**
  2317.      * Paints the specified rows as selected.
  2318.      * @param selRows an array of the zero-relative indexes of selected rows
  2319.      */
  2320.     protected void paintSelection(int[] selRows)
  2321.     {
  2322.         for (int i = 0; i < selRows.length ;i++)
  2323.         {
  2324.             int selectedRow = selRows[i];
  2325.             if (selectedRow - sbVPosition >= 0 && selectedRow - sbVPosition <= getNumVisibleRows()+1)
  2326.                 drawRows(selectedRow, 1, true);
  2327.         }
  2328.     }
  2329.  
  2330.     /**
  2331.      * Selects the row, if it is not already selected.
  2332.      * @param row the zero-relative row index
  2333.      * @see #deselectRow
  2334.      */
  2335.     public void selectRow(int row)
  2336.     {
  2337.         if (!highlightedRows.get(row))
  2338.         {
  2339.             //If in single select mode, deselect previously selected row
  2340.             if (!multiSelect)
  2341.             {
  2342.                 int selectedRow = getSelectedRow();
  2343.                 if (selectedRow != -1 && selectedRow != row)
  2344.                     deselectRow(selectedRow);
  2345.             }
  2346.  
  2347.             highlightedRows.set(row);
  2348.             sourceItemEvent(ItemEvent.SELECTED);
  2349.  
  2350.             drawRows(row, 1, true);
  2351.         }
  2352.     }
  2353.  
  2354.     /**
  2355.      * Deselects the row, if it is not already selected.
  2356.      * @param row the zero-relative row index
  2357.      * @see #selectRow
  2358.      */
  2359.     public void deselectRow(int row)
  2360.     {
  2361.         //If the row is hilited, unhilite it
  2362.         if (highlightedRows.get(row))
  2363.         {
  2364.             highlightedRows.clear(row);
  2365.             sourceItemEvent(ItemEvent.DESELECTED);
  2366.  
  2367.             //If the row has the selection border, remove it
  2368.             if (row == selectedRow)
  2369.                 removeSelectionBorder();
  2370.             else
  2371.                 drawRows(row, 1, true);
  2372.         }
  2373.     }
  2374.  
  2375.     /**
  2376.      * If there is a selected row, it removes the selection border from it.
  2377.      */
  2378.     protected void removeSelectionBorder()
  2379.     {
  2380.         if (selectedRow != -1)
  2381.         {
  2382.             int saveSelectedRow = selectedRow;
  2383.             selectedRow = -1;
  2384.             drawRows(saveSelectedRow, 1, true);
  2385.             selectedRow = saveSelectedRow;
  2386.         }
  2387.     }
  2388.  
  2389.     /**
  2390.      * Returns the recommended dimensions to properly display this component.
  2391.      * This is a standard Java AWT method which gets called to determine
  2392.      * the recommended size of this component.
  2393.      *
  2394.      * @see #minimumSize
  2395.      */
  2396.     //???RKM??? Seems wrong - hard coded
  2397.     public synchronized Dimension preferredSize()
  2398.     {
  2399.         return new Dimension(175, 125);
  2400.     }
  2401.  
  2402.     /**
  2403.      * Returns the minimum dimensions to properly display this component.
  2404.      * This is a standard Java AWT method which gets called to determine
  2405.      * the minimum size of this component.
  2406.      *
  2407.      * @see #preferredSize
  2408.      */
  2409.     //???RKM??? Seems wrong - hard coded
  2410.     public synchronized Dimension minimumSize()
  2411.     {
  2412.         return new Dimension(50, 50);
  2413.     }
  2414.  
  2415.     /**
  2416.      * Adds the specified action listener to receive action events
  2417.      * from this object.
  2418.      * @param l the action listener
  2419.      * @see #removeActionListener
  2420.      */
  2421.     public synchronized void addActionListener(ActionListener l)
  2422.     {
  2423.         actionListener = AWTEventMulticaster.add(actionListener, l);
  2424.     }
  2425.  
  2426.     /**
  2427.      * Removes the specified action listener so it no longer receives
  2428.      * action events from this object.
  2429.      * @param l the action listener
  2430.      * @see #addActionListener
  2431.      */
  2432.     public synchronized void removeActionListener(ActionListener l)
  2433.     {
  2434.         actionListener = AWTEventMulticaster.remove(actionListener, l);
  2435.     }
  2436.  
  2437.     /**
  2438.      * Adds the specified item listener to receive item events from this object.
  2439.      * @param l the item listener
  2440.      * @see #removeItemListener
  2441.      * @see #sourceItemEvent
  2442.      */
  2443.     public synchronized void addItemListener(ItemListener l)
  2444.     {
  2445.         itemListener = AWTEventMulticaster.add(itemListener, l);
  2446.     }
  2447.  
  2448.     /**
  2449.      * Removes the specified item listener so it no longer receives
  2450.      * item events from this object.
  2451.      * @param l the action listener
  2452.      * @see #addItemListener
  2453.      * @see #sourceItemEvent
  2454.      */
  2455.     public synchronized void removeItemListener(ItemListener l)
  2456.     {
  2457.         itemListener = AWTEventMulticaster.remove(itemListener, l);
  2458.     }
  2459.  
  2460.     /**
  2461.      * This is the Mouse Event handling innerclass.
  2462.      */
  2463.     class Mouse extends java.awt.event.MouseAdapter implements java.io.Serializable
  2464.     {
  2465.         /**
  2466.          * Handles Mouse Pressed events
  2467.          * @param e the MouseEvent
  2468.          */
  2469.         public void mousePressed(MouseEvent e)
  2470.         {
  2471.             int x = e.getX();
  2472.             int y = e.getY();
  2473.  
  2474.             requestFocus();
  2475.  
  2476.             //Reset column click code
  2477.             clickedInHeadings = false;
  2478.  
  2479.             //Is the click in the header
  2480.             if (y < headingHeight)
  2481.             {
  2482.                 if (allowResizingOfColumns)
  2483.                 {
  2484.                     // It's a click on the heading area:
  2485.                     // is it close enough to be a column size drag?
  2486.                     for (int i = 1;i< splitters.length - 1;i++)
  2487.                     {
  2488.                         int currSplitter = splitters[i];
  2489.                         if ((x < Math.min(currSplitter - sbHPosition + RESIZE_FUDGE_FACTOR, size().width - verticalScrollbarWidth)) &&
  2490.                             (x > currSplitter - sbHPosition - RESIZE_FUDGE_FACTOR))
  2491.                         {
  2492.                             dragColumn = i;
  2493.                             isDragging = true;
  2494.                             offscreenImageGraphics.setClip(0, 0, Math.max(splitters[splitters.length-1], size().width+sbHPosition), size().height);
  2495.                             mouseMotion.mouseDragged(e); //draw drag line immediately
  2496.                             return;
  2497.                         }
  2498.                     }
  2499.                 }
  2500.  
  2501.                 if (allowSorting)
  2502.                 {
  2503.                     // check for sort button push
  2504.                     for (int i = 0; i < headings.length; i++)
  2505.                     {
  2506.                         //int max = i == headings.length - 1 ? size().width : splitters[i+1]-sbHPosition;
  2507.  
  2508.                         if ((x > splitters[i] - sbHPosition) && (x < splitters[i+1] - sbHPosition))
  2509.                         {
  2510.                            clickedInHeadings = true;
  2511.                            columnClicked = i;
  2512.                            drawHeading(true);
  2513.                            repaint();
  2514.                            return;
  2515.                         }
  2516.                     }
  2517.                 }
  2518.  
  2519.                 return;
  2520.             }
  2521.  
  2522.             //Check if the user is clicking in the corner box
  2523.             {
  2524.                 Dimension size = getSize();
  2525.                 if (x >= size.width - verticalScrollbarWidth || y >= size.height - scrollbarHeight)
  2526.                     return;
  2527.             }
  2528.  
  2529.             //Calc row clicked in
  2530.             int clickedInRow = ((int) (y - headingHeight) / cellHeight) + topRow;
  2531.  
  2532.             //Range check the row that was clicked in
  2533.             if (clickedInRow < 0 || clickedInRow > cells.rows() - 1)
  2534.                 return;
  2535.  
  2536.             //Save off the old selection
  2537.             int oldRowSelect = selectedRow;
  2538.  
  2539.             // set new row selection
  2540.             changeSelection(clickedInRow, e.getModifiers());
  2541.  
  2542.             // detect a double click
  2543.             long when = e.getWhen();
  2544.             if ((selectedRow == oldRowSelect) || (oldRowSelect < 0))
  2545.             {
  2546.                 // if clicked on same row track time elapsed since last mouseDown
  2547.                 long clickSpeed = when - clickTime;
  2548.  
  2549.                 if (clickSpeed < CLICKTHRESHOLD)
  2550.                     sourceActionEvent(selectedRow);
  2551.             }
  2552.  
  2553.             clickTime = when;
  2554.         }
  2555.  
  2556.         /**
  2557.          * Handles Mouse Released events
  2558.          * @param e the MouseEvent
  2559.          */
  2560.         public void mouseReleased(MouseEvent e)
  2561.         {
  2562.             int x = e.getX();
  2563.  
  2564.             if (isDragging)
  2565.             {
  2566.                 //fix any drag that went off the left side
  2567.                 if (x < 0)
  2568.                     x = 0;
  2569.  
  2570.                 //Adjust splitters to delta
  2571.                 boolean splittersChanged = false;
  2572.                 {
  2573.                     xDragLast = Math.max(splitters[dragColumn - 1] + minColumnWidth,x + sbHPosition);
  2574.  
  2575.                     int splitterDelta = xDragLast - splitters[dragColumn];
  2576.  
  2577.                     for (int i = dragColumn + 1;i < splitters.length;i++)
  2578.                     {
  2579.                         int splitterWas = splitters[i];
  2580.                         splitters[i] += splitterDelta;
  2581.                         if (splitterWas != splitters[i])
  2582.                             splittersChanged = true;
  2583.                     }
  2584.  
  2585.                     if (splitters[dragColumn] != xDragLast)
  2586.                     {
  2587.                         splitters[dragColumn] = xDragLast;
  2588.                         splittersChanged = true;
  2589.                     }
  2590.                 }
  2591.  
  2592.                 //Stop dragging
  2593.                 xDragLast = -1;
  2594.                 isDragging = false;
  2595.  
  2596.                 //???RKM??? Cursor should not be assumed as default
  2597.                 if (e.getY() > headingHeight)
  2598.                     setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  2599.  
  2600.                 //If the columnSizes were being calculated, switch to set column sizes
  2601.                 if (splittersChanged && columnSizes == null)
  2602.                 {
  2603.                     //Switch the column sizes - done this way so they could be vetoed
  2604.                     try
  2605.                     {
  2606.                         setColumnSizes(intArrayToStringArray(getColumnSizesFromSplitters()));
  2607.                     }
  2608.                     catch(PropertyVetoException veto)
  2609.                     {
  2610.                     }
  2611.                 }
  2612.  
  2613.                 triggerRedraw();
  2614.  
  2615.                 return;
  2616.             }
  2617.  
  2618.             if (columnClicked != -1)
  2619.             {
  2620.                 // Find sort for that column
  2621.                 CompareCells compareCellsFunc = getColumnSorter(columnClicked);
  2622.  
  2623.                 // it was a sorting request
  2624.                 compareCellsFunc.setCurrentBitSet(highlightedRows);
  2625.                 compareCellsFunc.setSelectedRow(selectedRow);
  2626.  
  2627.                 if (columnClicked == lastColumnClicked)
  2628.                     compareCellsFunc.reverse();
  2629.                 cells.sort(compareCellsFunc, columnClicked);
  2630.  
  2631.                 selectedRow = compareCellsFunc.getSelectedRow();
  2632.  
  2633.                 //Rember the last column click, so we can detect when to swap the sort order
  2634.                 lastColumnClicked = columnClicked;
  2635.  
  2636.                 columnClicked = -1;
  2637.  
  2638.                 triggerRedraw();
  2639.             }
  2640.         }
  2641.  
  2642.         public void mouseExited(MouseEvent e)
  2643.         {
  2644.             if ((getCursor().getType() != Cursor.DEFAULT_CURSOR) && (!isDragging))
  2645.                 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  2646.         }
  2647.     }
  2648.  
  2649.     /**
  2650.      * This is the MouseMotion Event handling innerclass.
  2651.      */
  2652.     class MouseMotion implements java.awt.event.MouseMotionListener, java.io.Serializable
  2653.     {
  2654.         /**
  2655.          * Handles Mouse Moved events
  2656.          * @param e the MouseEvent
  2657.          */
  2658.         public void mouseMoved(MouseEvent e)
  2659.         {
  2660.             if (allowResizingOfColumns)
  2661.             {
  2662.                 boolean isCloseEnough = false;
  2663.  
  2664.                 // Use resize cursor ?
  2665.                 if (e.getY() < headingHeight)
  2666.                 {
  2667.                     int x = e.getX();
  2668.  
  2669.                     // Moving mouse around in header area
  2670.                     // is it close enough to be a column size drag?
  2671.                     for (int i = 1;i < splitters.length - 1;i++)
  2672.                     {
  2673.                         int currSplitter = splitters[i];
  2674.                         if ((x < Math.min(currSplitter - sbHPosition + RESIZE_FUDGE_FACTOR, size().width - verticalScrollbarWidth)) &&
  2675.                             (x > currSplitter - sbHPosition - RESIZE_FUDGE_FACTOR))
  2676.                         {
  2677.                             isCloseEnough = true;
  2678.                             break;
  2679.                         }
  2680.                     }
  2681.                 }
  2682.  
  2683.                 int newCursor = (isCloseEnough ? Cursor.W_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR);
  2684.  
  2685.                 if (newCursor != getCursor().getType())
  2686.                     setCursor(new Cursor(newCursor));
  2687.             }
  2688.         }
  2689.  
  2690.         /**
  2691.          * Handles Mouse Dragged events
  2692.          * @param e the MouseEvent
  2693.          */
  2694.         public void mouseDragged(MouseEvent e)
  2695.         {
  2696.             int x = e.getX();
  2697.             int y = e.getY();
  2698.             Dimension s = size();
  2699.  
  2700.             if (!isDragging)
  2701.             {
  2702.                 if (clickedInHeadings)
  2703.                 {
  2704.                     // check for sort button push outside of button
  2705.                     int max;
  2706.                     int min;
  2707.  
  2708.                     if (columnClicked > -1)
  2709.                     {
  2710.                         if (columnClicked == 0)
  2711.                             min = 0;
  2712.                         else
  2713.                             min = splitters[columnClicked]-sbHPosition;
  2714.  
  2715.                         if (columnClicked == headings.length - 1)
  2716.                             max = s.width;
  2717.                         else
  2718.                             max = splitters[columnClicked+1]-sbHPosition;
  2719.  
  2720.                         if ((x < min) || (x > max) || (y > headingHeight) || (y < 0))
  2721.                         {
  2722.                             memoryClick = columnClicked;
  2723.                             columnClicked = -1;
  2724.                             drawHeading(false);
  2725.                             //???RKM??? Seems crazy to repaint entire component, just to redraw column heading rect
  2726.                             Graphics g = getGraphics();
  2727.                             paint(g);
  2728.                             //???RKM??? Get smarter about what is blit to the scene, this could be made nicer
  2729.                             //repaint();
  2730.                         }
  2731.                     }
  2732.                     else if (memoryClick > -1)
  2733.                     {
  2734.                         if (memoryClick == 0)
  2735.                             min = 0;
  2736.                         else
  2737.                             min = splitters[memoryClick]-sbHPosition;
  2738.  
  2739.                         if (memoryClick == headings.length - 1)
  2740.                             max = s.width;
  2741.                         else
  2742.                             max = splitters[memoryClick+1]-sbHPosition;
  2743.  
  2744.                         if ((x > min) && (x < max) && (y < headingHeight) && (y > 0))
  2745.                         {
  2746.                             columnClicked = memoryClick;
  2747.                             memoryClick = -1;
  2748.                             drawHeading(true);
  2749.                             //???RKM??? Seems crazy to repaint entire component, just to redraw column heading rect
  2750.                             Graphics g = getGraphics();
  2751.                             paint(g);
  2752.                             //???RKM??? Get smarter about what is blit to the scene, this could be made nicer
  2753.                             //repaint();
  2754.                         }
  2755.                     }
  2756.                 }
  2757.             }
  2758.             else
  2759.             {
  2760.                 //fix any drag that went off the left side
  2761.                 if (x < 0)
  2762.                     x = 0;
  2763.  
  2764.                 if (x + sbHPosition < splitters[dragColumn-1] + minColumnWidth)
  2765.                     return;
  2766.  
  2767.                 //erase prvious drag line and draw in new position
  2768.                 int x1 = Math.min(x, xDragLast-sbHPosition);
  2769.                 int x2 = Math.max(x, xDragLast-sbHPosition);
  2770.  
  2771.                 offscreenImageGraphics.setColor(colorBg);
  2772.                 offscreenImageGraphics.setXORMode(Color.gray);
  2773.                 offscreenImageGraphics.drawLine(xDragLast,0,xDragLast,s.height);
  2774.                 offscreenImageGraphics.drawLine(x+sbHPosition, 0, x+sbHPosition, s.height);
  2775.                 offscreenImageGraphics.setColor(getForeground());
  2776.                 offscreenImageGraphics.setPaintMode();
  2777.  
  2778.                 //save x position of drag line
  2779.                 xDragLast = x + sbHPosition;
  2780.                 repaint(x1, 0, x2-x1+1, size().height);
  2781.             }
  2782.         }
  2783.     }
  2784.  
  2785.     /**
  2786.      * This is the Key Event handling innerclass.
  2787.      */
  2788.     class Key extends java.awt.event.KeyAdapter implements java.io.Serializable
  2789.     {
  2790.         /**
  2791.          * Handles Key Pressed events
  2792.          * @param e the KeyEvent
  2793.          */
  2794.         public void keyPressed(KeyEvent e)
  2795.         {
  2796.             int modifiers = e.getModifiers();
  2797.  
  2798.             switch (e.getKeyCode())
  2799.             {
  2800.                 case KeyEvent.VK_DOWN:
  2801.                     if (selectedRow < cells.rows() - 1)
  2802.                         changeSelection(selectedRow + 1, modifiers);
  2803.                     break;
  2804.  
  2805.                 case KeyEvent.VK_PAGE_DOWN:
  2806.                     changeSelection(Math.min(selectedRow + getNumVisibleRows(), cells.rows()-1), modifiers);
  2807.                     break;
  2808.  
  2809.                 case KeyEvent.VK_UP:
  2810.                     if (selectedRow > 0)
  2811.                         changeSelection(selectedRow - 1, modifiers);
  2812.                     break;
  2813.  
  2814.                 case KeyEvent.VK_PAGE_UP:
  2815.                     changeSelection(Math.max(selectedRow - getNumVisibleRows(), 0), modifiers);
  2816.                     break;
  2817.  
  2818.                 case KeyEvent.VK_RIGHT:
  2819.                 {
  2820.                     int max = horizontalScrollbar.getMaximum()-(isSun1_1?size().width-verticalScrollbarWidth:0);
  2821.                     if (sbHShow && sbHPosition < max)
  2822.                     {
  2823.                         horizontalScrollbar.setValue(Math.min(sbHPosition += hScrollbarLineIncrement, max));
  2824.                         repaint();
  2825.                     }
  2826.                     break;
  2827.                 }
  2828.  
  2829.                 case KeyEvent.VK_LEFT:
  2830.                 {
  2831.                     if (sbHPosition > 0)
  2832.                     {
  2833.                         horizontalScrollbar.setValue(Math.max(sbHPosition-=hScrollbarLineIncrement, 0));
  2834.                         repaint();
  2835.                     }
  2836.                     break;
  2837.                 }
  2838.  
  2839.                 case KeyEvent.VK_ENTER:
  2840.                     if (selectedRow > -1)
  2841.                     {
  2842.                         sourceActionEvent(selectedRow);
  2843.                     }
  2844.                     break;
  2845.             }
  2846.         }
  2847.     }
  2848.  
  2849.  
  2850.     /**
  2851.      * This is the Adjustment Event handling innerclass.
  2852.      */
  2853.     class Adjustment implements java.awt.event.AdjustmentListener, java.io.Serializable
  2854.     {
  2855.         /**
  2856.          * Handles Scrollbar events
  2857.          * @param e the AdjustmentEvent
  2858.          */
  2859.         public void adjustmentValueChanged(AdjustmentEvent e)
  2860.         {
  2861.             if (e.getSource() == verticalScrollbar)
  2862.             {
  2863.                 if (topRow != verticalScrollbar.getValue())
  2864.                 {
  2865.                     topRow = verticalScrollbar.getValue();
  2866.                     sbVPosition = topRow;
  2867.  
  2868.                     triggerRedraw();
  2869.                 }
  2870.             }
  2871.             else if (e.getSource() == horizontalScrollbar)
  2872.             {
  2873.                 if (sbHPosition != horizontalScrollbar.getValue())
  2874.                 {
  2875.                     sbHPosition = horizontalScrollbar.getValue();
  2876.  
  2877.                     triggerRedraw();
  2878.                 }
  2879.             }
  2880.         }
  2881.     }
  2882.  
  2883.     /**
  2884.      * Fires an item event to the listeners.
  2885.      * @param eventType value indicating the kind of state change, one of:
  2886.      * ItemEvent.SELECTED or ItemEvent.DESELECTED
  2887.      * @see java.awt.event.ItemEvent#SELECTED
  2888.      * @see java.awt.event.ItemEvent#DESELECTED
  2889.      * @see #addItemListener
  2890.      * @see #removeItemListener
  2891.      */
  2892.     protected void sourceItemEvent(int eventType)
  2893.     {
  2894.         if (itemListener != null)
  2895.             itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this, eventType));
  2896.     }
  2897.  
  2898.     /**
  2899.      * Internal helper routine.
  2900.      * Repaints the user input focus, as needed.
  2901.      */
  2902.     protected void repaintFocus()
  2903.     {
  2904.         if (selectedRow != -1)
  2905.             drawRows(selectedRow, 1, true);
  2906.         paintSelection(getSelectedRows());
  2907.     }
  2908.  
  2909.     class Focus implements FocusListener, java.io.Serializable
  2910.     {
  2911.         public void focusGained(FocusEvent e)
  2912.         {
  2913.             hasFocus = true;
  2914.             repaintFocus();
  2915.         }
  2916.  
  2917.         public void focusLost(FocusEvent e)
  2918.         {
  2919.             hasFocus = false;
  2920.             repaintFocus();
  2921.         }
  2922.     }
  2923.  
  2924.     /**
  2925.      * Fires an action event to the listeners.
  2926.      * The action command sent is the value specified in <code>actionCommand</code> concatenated
  2927.      * with the selectedRow. By default the <code>actionCommand</code> is "RowSelected:", so a
  2928.      * typical action command sent might be "RowSelected:0".
  2929.      * @param selectedRow the zero-relative index of the row generating the event
  2930.      * @see #actionCommand
  2931.      */
  2932.     protected void sourceActionEvent(int selectedRow)
  2933.     {
  2934.         //Sending the selected row along with the command name, for convenience.
  2935.         if (actionListener != null)
  2936.             actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand + selectedRow));
  2937.     }
  2938.  
  2939.     /**
  2940.      * Returns the number of visible rows that can fit in the MulitList.
  2941.      * Note: This could be more than the number of rows in the MultiList.
  2942.      */
  2943.     protected int getNumVisibleRows()
  2944.     {
  2945.         return ((int)((size().height - headingHeight - scrollbarHeight - BORDER - (headingHeight > 0 ? 0 : BORDER)) / cellHeight));
  2946.     }
  2947.  
  2948.     /**
  2949.      * Draws the specifed rows into an offscreen image.
  2950.      * @param cellRow the zero-relative index of the first row to draw
  2951.      * @param the number of rows to draw
  2952.      * @param toRepaint if true, calls the <code>repaint</code> method to update the screen
  2953.      */
  2954.     protected void drawRows(int cellRow,int numRowsToDraw,boolean toRepaint)
  2955.     {
  2956.         //Calc index of row in visible area (ie first row on screen is zero - could be row twenty)
  2957.         int visibleRowIndex = cellRow - sbVPosition;
  2958.  
  2959.         //Range check visibleRowIndex
  2960.         if (visibleRowIndex < 0 || visibleRowIndex > getNumVisibleRows() - 1)
  2961.             return;
  2962.  
  2963.         MatrixEnumeration e = cells.elements();
  2964.         Cell c = null;
  2965.  
  2966.         if (cellRow > 0)
  2967.             c  = (Cell)e.advanceTo(cellRow);
  2968.  
  2969.         //iterate the rows and paint each one
  2970.         while (e.hasMoreElements() || c != null)
  2971.         {
  2972.             int x = 1;
  2973.  
  2974.             //Calc if rowIsSelected, once, outside the loop
  2975.             boolean rowIsSelected = highlightedRows.get(cellRow);
  2976.  
  2977.             //Calc topOfCell, onc, outside of loop
  2978.             int topOfCell = headingHeight + (headingHeight > 0 ? 0 : BORDER) + (visibleRowIndex * cellHeight);
  2979.  
  2980.             //Iterate the cols of the current row
  2981.                int cols = headings.length;
  2982.             for (int i = 0;i < cols;i++)
  2983.             {
  2984.                 int w = splitters[i+1] - splitters[i];
  2985.  
  2986.                 if(offscreenImage != null)
  2987.                 {
  2988.                     offscreenImageGraphics.setColor(rowIsSelected ? colorHBg : colorBg);
  2989.                     offscreenImageGraphics.fillRect(x, topOfCell, w + 4, cellHeight);
  2990.                     offscreenImageGraphics.setColor(rowIsSelected?colorHFg :colorFg);
  2991.                 }
  2992.  
  2993.                 if (c == null)
  2994.                     c = (Cell)e.nextElement();
  2995.  
  2996.                 if (offscreenImage != null && (c != null) && (e.currRow() == cellRow) && (e.currCol() == i))
  2997.                 {
  2998.                     c.drawCell(offscreenImageGraphics, getColumnAlignment(i), splitters[i]+3, topOfCell, w, cellHeight, cellAscent);
  2999.                     c = null;
  3000.                 }
  3001.  
  3002.                 if (c != null && e.currRow() < cellRow)
  3003.                     c = null;
  3004.  
  3005.                 x = splitters[i+1]-3;
  3006.             }
  3007.  
  3008.             if (hasFocus & focusIndicatedVisually)
  3009.             {
  3010.                 if (offscreenImage != null && cellRow == selectedRow)
  3011.                 {
  3012.                     Rectangle r1 = offscreenImageGraphics.getClipRect();
  3013.  
  3014.                     if(r1 != null)
  3015.                     {
  3016.                         offscreenImageGraphics.setColor(colorBg);
  3017.                         offscreenImageGraphics.setXORMode(Color.gray);
  3018.                         offscreenImageGraphics.drawRect(1, topOfCell, r1.width-3, cellHeight-1);
  3019.                         offscreenImageGraphics.setPaintMode();
  3020.                     }
  3021.                 }
  3022.             }
  3023.  
  3024.             if (offscreenImage != null && toRepaint)
  3025.             {
  3026.                 Rectangle r1 = offscreenImageGraphics.getClipRect();
  3027.  
  3028.                 if(r1 != null)
  3029.                 {
  3030.                     repaint(0, topOfCell, r1.width, cellHeight);
  3031.                 }
  3032.             }
  3033.  
  3034.             if (--numRowsToDraw == 0)
  3035.                 break;
  3036.  
  3037.             cellRow++;
  3038.  
  3039.             visibleRowIndex++;
  3040.         }
  3041.     }
  3042.  
  3043.     /**
  3044.      * Adds a listener for all property change events.
  3045.      * @param listener the listener to add
  3046.      * @see #removePropertyChangeListener
  3047.      */
  3048.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
  3049.     {
  3050.         changes.addPropertyChangeListener(listener);
  3051.     }
  3052.  
  3053.     /**
  3054.      * Removes a listener for all property change events.
  3055.      * @param listener the listener to remove
  3056.      * @see #addPropertyChangeListener
  3057.      */
  3058.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
  3059.     {
  3060.         changes.removePropertyChangeListener(listener);
  3061.     }
  3062.  
  3063.     /**
  3064.      * Adds a listener for all vetoable property change events.
  3065.      * @param listener the listener to add
  3066.      * @see #removeVetoableChangeListener
  3067.      */
  3068.     public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
  3069.     {
  3070.         vetos.addVetoableChangeListener(listener);
  3071.     }
  3072.  
  3073.     /**
  3074.      * Removes a listener for all vetoable property change events.
  3075.      * @param listener the listener to remove
  3076.      * @see #addVetoableChangeListener
  3077.      */
  3078.     public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
  3079.     {
  3080.         vetos.removeVetoableChangeListener(listener);
  3081.     }
  3082.  
  3083.     /**
  3084.      * Takes no action.
  3085.      * This is a standard Java AWT method which gets called to specify
  3086.      * which layout manager should be used to layout the components in
  3087.      * standard containers.
  3088.      *
  3089.      * Since layout managers CANNOT BE USED with this container the standard
  3090.      * setLayout has been OVERRIDDEN for this container and does nothing.
  3091.      *
  3092.      * @param l the layout manager to use to layout this container's components
  3093.      * (IGNORED)
  3094.      * @see java.awt.Container#getLayout
  3095.      **/
  3096.     public void setLayout(LayoutManager mgr) {
  3097.     }
  3098.  
  3099.     public boolean isFocusTraversable()
  3100.     {
  3101.         return true;
  3102.     }
  3103.  
  3104.     static Color thirtyThreePercentGray = new Color(0x212121);
  3105.     static Color sixtySixPercentGray = new Color(0x434343);
  3106.  
  3107.     /**
  3108.      * Draws the specified column heading into an offscreen image.
  3109.      * @param columnHeadingRect the heading bounds
  3110.      * @param isSelected true if the column heading is selected
  3111.      */
  3112.     protected void drawColumnHeading
  3113.             (Rectangle    columnHeadingRect,
  3114.              boolean    isSelected)
  3115.     {
  3116.         if (symantec.itools.lang.OS.isMacintosh())
  3117.         {
  3118.             //Frame the column heading
  3119.             offscreenImageGraphics.setColor(Color.black);
  3120.             offscreenImageGraphics.drawRect(columnHeadingRect.x, columnHeadingRect.y, columnHeadingRect.width - 1, columnHeadingRect.height - 1);
  3121.  
  3122.             //Draw the gray in it
  3123.             offscreenImageGraphics.setColor(isSelected ? Color.gray : headingBg);
  3124.             offscreenImageGraphics.fillRect
  3125.                 (columnHeadingRect.x + 2,
  3126.                  columnHeadingRect.y + 2,
  3127.                  columnHeadingRect.width - 4,
  3128.                  columnHeadingRect.height - 4);
  3129.  
  3130.             if (isSelected)
  3131.                  offscreenImageGraphics.setColor(Color.black);
  3132.  
  3133.             //Draw dot in lower left hand corner
  3134.             offscreenImageGraphics.drawLine
  3135.                 (columnHeadingRect.x + 1,
  3136.                  columnHeadingRect.y + columnHeadingRect.height - 2,
  3137.                  columnHeadingRect.x + 1,
  3138.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3139.  
  3140.             //Draw dot in upper right hand corner
  3141.             offscreenImageGraphics.drawLine
  3142.                 (columnHeadingRect.x + columnHeadingRect.width - 2,
  3143.                  columnHeadingRect.y + 1,
  3144.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3145.                  columnHeadingRect.y + 1);
  3146.  
  3147.             //Draw white left side vertical line (upper hilight)
  3148.             offscreenImageGraphics.setColor(isSelected ? thirtyThreePercentGray: Color.white);
  3149.             offscreenImageGraphics.drawLine
  3150.                 (columnHeadingRect.x + 1,
  3151.                  columnHeadingRect.y + 1,
  3152.                  columnHeadingRect.x + 1,
  3153.                  columnHeadingRect.y + columnHeadingRect.height - 3);
  3154.  
  3155.             //Draw white top side horrizontal line (upper hilight)
  3156.             offscreenImageGraphics.drawLine
  3157.                 (columnHeadingRect.x + 2,
  3158.                  columnHeadingRect.y + 1,
  3159.                  columnHeadingRect.x + columnHeadingRect.width - 3,
  3160.                  columnHeadingRect.y + 1);
  3161.  
  3162.             //Draw shadow
  3163.             offscreenImageGraphics.setColor(isSelected ? sixtySixPercentGray: Color.gray);
  3164.  
  3165.             //Draw horizontal shadow
  3166.             offscreenImageGraphics.drawLine
  3167.                 (columnHeadingRect.x + 2,
  3168.                  columnHeadingRect.y + columnHeadingRect.height - 2,
  3169.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3170.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3171.  
  3172.             //Draw vertical shadow
  3173.             offscreenImageGraphics.drawLine
  3174.                 (columnHeadingRect.x + columnHeadingRect.width - 2,
  3175.                  columnHeadingRect.y + 2,
  3176.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3177.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3178.         }
  3179.         else
  3180.         {
  3181.             columnHeadingRect.x += 1;
  3182.             columnHeadingRect.y += 1;
  3183.             columnHeadingRect.width -= 2;
  3184.             columnHeadingRect.height -= 2;
  3185.  
  3186.             draw3DBox(columnHeadingRect, !isSelected);
  3187.  
  3188.             offscreenImageGraphics.setColor(headingBg);
  3189.             offscreenImageGraphics.fillRect(columnHeadingRect.x+1, columnHeadingRect.y+1, columnHeadingRect.width-1, columnHeadingRect.height-1);
  3190.         }
  3191.     }
  3192.  
  3193.     //???RKM??? Can't we call draw3DRect here???
  3194.     /**
  3195.      * Draws a rectangle with 3-dimensional effect into the offscreen image.
  3196.      * @param r the rectangle bounds
  3197.      * @param up true to draw as if raised, false to draw as if lowered
  3198.      */
  3199.     protected void draw3DBox(Rectangle r, boolean up)
  3200.     {
  3201.         int x0 = r.x;
  3202.         int y0 = r.y;
  3203.         int x1 = r.x + r.width;
  3204.         int y1 = r.y + r.height;
  3205.  
  3206.         offscreenImageGraphics.setColor(up?Color.black: Color.white);
  3207.         offscreenImageGraphics.drawLine(x1,   y0, x1,   y1); //right
  3208.         offscreenImageGraphics.drawLine(x1+1, y0, x1+1, y1); //right
  3209.  
  3210.         offscreenImageGraphics.drawLine(x0,y1,x1,y1);        //bottom
  3211.         offscreenImageGraphics.drawLine(x0,y1+1, x1, y1+1);  //bottom
  3212.  
  3213.         offscreenImageGraphics.setColor(up?Color.white:Color.gray);
  3214.         offscreenImageGraphics.drawLine(x0,y0,x1-2,y0);     //top
  3215.         offscreenImageGraphics.drawLine(x0,y0,x0,y1-1);     //left
  3216.     }
  3217.  
  3218.     /**
  3219.      * Calculates the height of the column headings and stores that in
  3220.      * headingHeight.
  3221.      */
  3222.     protected void calculateHeadingHeight()
  3223.     {
  3224.         FontMetrics fontMetrics = getFontMetrics(headingFont);
  3225.         headingHeight = fontMetrics.getHeight() + fontMetrics.getLeading() + 7;
  3226.     }
  3227.  
  3228.     /**
  3229.      * Draws all column headings into an offscreen image.
  3230.      * @param down true if mouse is down in heading
  3231.      */
  3232.     protected void drawHeading(boolean down)
  3233.     {
  3234.         //If we aren't drawing headings, get out of here
  3235.         if (!headingVisible)
  3236.             return;
  3237.  
  3238.         //If we have any headers
  3239.         if (headings.length > 0)
  3240.         {
  3241.             //Save font we entered this routine with
  3242.             Font saveFont = offscreenImageGraphics.getFont();
  3243.  
  3244.             //Set font to heading font and get font metric for it (we'll need it later)
  3245.             offscreenImageGraphics.setFont(headingFont);
  3246.             FontMetrics fontMetrics = offscreenImageGraphics.getFontMetrics();
  3247.  
  3248.             //Init currSplitter to 0th elem, avoid two array indexes per loop
  3249.             int currSplitter = splitters[0];
  3250.  
  3251.             //Loop though all of the headings
  3252.             for (int i = 0; i < headings.length; i++)
  3253.             {
  3254.                 int nextSplitter = splitters[i+1];
  3255.  
  3256.                 int currHeadingWidth = nextSplitter - currSplitter;
  3257.  
  3258.                 //Draw column heading
  3259.                 Rectangle columnHeaderRect =
  3260.                     new Rectangle(currSplitter,0,currHeadingWidth,headingHeight);
  3261.  
  3262.                 boolean clickingInCurrColumn = (columnClicked == i) ? true : false;
  3263.  
  3264.                 drawColumnHeading(columnHeaderRect, clickingInCurrColumn);
  3265.  
  3266.                 String currHeading = headings[i];
  3267.                 if (currHeading == null)
  3268.                     continue;
  3269.  
  3270.                 offscreenImageGraphics.setColor(headingFg);
  3271.  
  3272.                 int stringWidth = fontMetrics.stringWidth(currHeading);
  3273.                 int w = currHeadingWidth - 3;
  3274.                 int shift = down && clickingInCurrColumn ? 1 : 0;
  3275.  
  3276.                 int stringBaseline = headingHeight - 6 + shift;
  3277.  
  3278.                 //Save clip and clip to heading rect
  3279.                 Shape saveClip = offscreenImageGraphics.getClip();
  3280.                 offscreenImageGraphics.setClip(columnHeaderRect.x,columnHeaderRect.y,columnHeaderRect.width,columnHeaderRect.height);
  3281.  
  3282.                 switch (getColumnAlignment(i))
  3283.                 {
  3284.                     case MultiList.LEFT:
  3285.                     {
  3286.                         offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3287.                         break;
  3288.                     }
  3289.  
  3290.                     case MultiList.CENTER:
  3291.                     {
  3292.                         if (stringWidth > w)
  3293.                             offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3294.                         else
  3295.                             offscreenImageGraphics.drawString(currHeading, currSplitter + (w-stringWidth)/2+shift, stringBaseline);
  3296.  
  3297.                         break;
  3298.                     }
  3299.  
  3300.                     case MultiList.RIGHT:
  3301.                     {
  3302.                         if (stringWidth > w)
  3303.                             offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3304.                         else
  3305.                             offscreenImageGraphics.drawString(currHeading, currSplitter+w-stringWidth-6+shift, stringBaseline);
  3306.  
  3307.                         break;
  3308.                     }
  3309.                 }//switch
  3310.  
  3311.                 //Restore clip
  3312.                 offscreenImageGraphics.setClip(saveClip);
  3313.  
  3314.                 if (clickingInCurrColumn)
  3315.                     offscreenImageGraphics.drawLine(columnHeaderRect.x+1, headingHeight - 1, nextSplitter - 3,  headingHeight - 1);
  3316.  
  3317.                 //Set currSplitter to nextSplitter (loop iterations - one array index per loop)
  3318.                 currSplitter = nextSplitter - 1;
  3319.             }
  3320.  
  3321.             //Restore font
  3322.             offscreenImageGraphics.setFont(saveFont);
  3323.         }
  3324.  
  3325.         //If the column headers do not fill up the width, add column
  3326.         {
  3327.             Dimension dim = size();
  3328.  
  3329.             int lastSplitter = splitters[splitters.length - 1];
  3330.  
  3331.             if (lastSplitter < sbHPosition + dim.width - verticalScrollbarWidth)
  3332.             {
  3333.                 Rectangle fillerHeaderRect =
  3334.                     new Rectangle(lastSplitter - 1,0,sbHPosition + dim.width - verticalScrollbarWidth - lastSplitter + 1, headingHeight);
  3335.                 drawColumnHeading(fillerHeaderRect, false);
  3336.             }
  3337.         }
  3338.     }
  3339.  
  3340.     /**
  3341.      * Converts the given array of int into a corresponding array of String.
  3342.      * @param intArray the array of ints
  3343.      * @return an array of String
  3344.      */
  3345.     protected String[] intArrayToStringArray(int[] intArray)
  3346.     {
  3347.         if (intArray == null)
  3348.             return null;
  3349.  
  3350.         String[] list = new String[intArray.length];
  3351.  
  3352.         for (int i = 0;i < intArray.length;i++)
  3353.         {
  3354.             String intString = "";
  3355.             try
  3356.             {
  3357.                 intString = String.valueOf(intArray[i]);
  3358.             }
  3359.             catch (Exception e)
  3360.             {
  3361.             }
  3362.             list[i] = intString;
  3363.         }
  3364.  
  3365.         return list;
  3366.     }
  3367.  
  3368.     /**
  3369.      * Returns an array of column sizes as determined from the current splitter information.
  3370.      */
  3371.     protected int[] getColumnSizesFromSplitters()
  3372.     {
  3373.         int[] splitterColumnSizes = new int[headings.length];
  3374.  
  3375.         for (int i = 0;i < headings.length;i++)
  3376.             splitterColumnSizes[i] = splitters[i + 1] - splitters[i];
  3377.  
  3378.         return splitterColumnSizes;
  3379.     }
  3380.  
  3381.     /**
  3382.      * Triggers an entire redraw of this component, unless redrawing is
  3383.      * currently suppressed.
  3384.      * @see #setSupressRedraw
  3385.      */
  3386.     protected void triggerRedraw()
  3387.     {
  3388.         //If we're not suppressing redraws
  3389.         if (!isSuppressRedraw)
  3390.         {
  3391.             //Set forceRedraw so contents of offscreen is updated
  3392.             forceRedraw = true;
  3393.             repaint();
  3394.         }
  3395.         else redrawWasSupressed = true;
  3396.     }
  3397.  
  3398.     /**
  3399.      * Handles cases where the user might pass either an array of items,
  3400.      * or a single string with itemds separated by the ';' character.
  3401.      * This may occur in the <code>setHeadings</code>,
  3402.      * <code>setColumnAlignments</code>, or <code>setColumnSizes</code>
  3403.      * methods.
  3404.      * @param list the input list
  3405.      * @param an array of items, one per String
  3406.      * @see #setHeadings
  3407.      * @see #setColumnAlignments
  3408.      * @see #setColumnSizes
  3409.      */
  3410.     protected String[] tokenizeStringArrayIfNeeded(String[] list)
  3411.     {
  3412.         if (list != null)
  3413.         {
  3414.             //Handle case where user thinks ; separates entries
  3415.             if (list.length == 1 && list[0].indexOf(";") != -1)
  3416.             {
  3417.                 //Convert the first element into an array of strings
  3418.                 java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(list[0],";");
  3419.  
  3420.                 int numColumns = tokenizer.countTokens();
  3421.  
  3422.                 String[] convertedList = new String[numColumns];
  3423.  
  3424.                 int currCol = 0;
  3425.                 while (tokenizer.hasMoreTokens())
  3426.                 {
  3427.                     String columnHeader = tokenizer.nextToken();
  3428.  
  3429.                     convertedList[currCol] = columnHeader;
  3430.  
  3431.                     currCol++;
  3432.                 }
  3433.  
  3434.                 list = convertedList;
  3435.             }
  3436.         }
  3437.  
  3438.         return list;
  3439.     }
  3440.  
  3441.     /**
  3442.      * Initializes new headings with the given list of heading titles.
  3443.      * @param list array of heading title strings
  3444.      */
  3445.     protected void calcHeadings(String[] list)
  3446.     {
  3447.         //    RKM    In order to reduce chance of a bug injection, I've allowed the calculation below to
  3448.         //        occur, even in the case where the result is bogus
  3449.  
  3450.         headings = new String[list.length];
  3451.         splitters = new int[list.length + 1];
  3452.  
  3453.         //???RKM??? So what do we do if columnAlignments is not the same width
  3454.  
  3455.         for (int i = 0; i < list.length; ++i)
  3456.         {
  3457.             try
  3458.             {
  3459.                 setHeading(list[i], i);
  3460.             }
  3461.             catch (PropertyVetoException e){}
  3462.         }
  3463.  
  3464.         //If there were column sizes, reapply them now
  3465.         if (columnSizes != null)
  3466.         {
  3467.             try
  3468.             {
  3469.                 setColumnSizes(intArrayToStringArray(columnSizes));
  3470.             }
  3471.             catch(PropertyVetoException veto)
  3472.             {
  3473.             }
  3474.         }
  3475.     }
  3476.  
  3477.     /**
  3478.      * The background color of cell text.
  3479.      * @see #getCellBg
  3480.      * @see #setCellBg
  3481.      */
  3482.     protected Color        colorBg = Color.white;
  3483.     /**
  3484.      * The foreground color of cell text.
  3485.      * @see #getCellFg
  3486.      * @see #setCellFg
  3487.      */
  3488.     protected Color        colorFg = Color.black;
  3489.     //???RKM??? Remove if when MRJ fixes it's bugs
  3490.     /**
  3491.      * The background color of hilighted cell text.
  3492.      * Automatically determined.
  3493.      */
  3494.     protected Color        colorHBg = symantec.itools.lang.OS.isMacintosh() ? new Color(0,0,128) : SystemColor.textHighlight;
  3495.     /**
  3496.      * The foreground color of hilighted cell text.
  3497.      * Automatically determined.
  3498.      */
  3499.     protected Color        colorHFg = symantec.itools.lang.OS.isMacintosh() ? Color.white : SystemColor.textHighlightText;
  3500.     /**
  3501.      * The background color of column heading text.
  3502.      * @see #getHeadingBg
  3503.      * @see #setHeadingBg
  3504.      */
  3505.     protected Color        headingBg = Color.lightGray;
  3506.     /**
  3507.      * The foreground color of column heading text.
  3508.      * @see #getHeadingFg
  3509.      * @see #setHeadingFg
  3510.      */
  3511.     protected Color        headingFg = Color.black;
  3512.     /**
  3513.      * Allow sorting. Default is true.
  3514.      */
  3515.     protected boolean        allowSorting = true;
  3516.     /**
  3517.      * Indicate focus when drawing. Default is true.
  3518.      */
  3519.     protected boolean        focusIndicatedVisually = true;
  3520.     /**
  3521.      * Allow multiple selections. Default is false.
  3522.      */
  3523.     protected boolean        multiSelect = false;
  3524.     /**
  3525.      * Allow columns to be resized. Default is true.
  3526.      */
  3527.     protected boolean        allowResizingOfColumns = true;
  3528.     /* *
  3529.      * Checks for scrollbars that have different max value Members used to
  3530.      * calculate headings, when size of component is not known (pre add).
  3531.      */
  3532.  
  3533.     /**
  3534.      * The heading titles.
  3535.      * @see #getHeadings
  3536.      * @see #setHeadings
  3537.      * @see #getHeading
  3538.      * @see #setHeading
  3539.      */
  3540.     protected String        headings[];
  3541.     /**
  3542.      * Is the heading visible. Default is true.
  3543.      */
  3544.     protected boolean        headingVisible = true;
  3545.     /**
  3546.      * The heading height in pixels.
  3547.      * If the heading is showing, it includes one pixel for the border (they overlap).
  3548.      */
  3549.     protected int            headingHeight = 0;
  3550.     /**
  3551.      * The font used to draw heading text.
  3552.      * @see #getHeadingFont
  3553.      * @see #setHeadingFont
  3554.      */
  3555.     protected Font            headingFont;
  3556.  
  3557.     /**
  3558.      * The location of the boundries between columns, in pixels.
  3559.      */
  3560.     protected int            splitters[];
  3561.  
  3562.     /**
  3563.      * The column alignments.
  3564.      * Values are one of: LEFT, CENTER, or RIGHT.
  3565.      * @see #LEFT
  3566.      * @see #CENTER
  3567.      * @see #RIGHT
  3568.      * @see #setColumnAlignments
  3569.      */
  3570.     protected int            columnAlignments[];
  3571.     /**
  3572.      * The column alignment to use by default.
  3573.      * The initial value is LEFT.
  3574.      * @see #LEFT
  3575.      * @see #CENTER
  3576.      * @see #RIGHT
  3577.      * @see #getDefaultColumnAlignment
  3578.      * @see #setDefaultColumnAlignment
  3579.      */
  3580.     protected int            defaultColumnAlignment = LEFT;
  3581.  
  3582.     /**
  3583.      * If non-null, the the width of each column in pixels.
  3584.      * If null, the columns are automatically sized.
  3585.      * @see #getColumnSizes
  3586.      * @see #setColumnSizes
  3587.      */
  3588.     protected int            columnSizes[] = null;
  3589.  
  3590.     /**
  3591.      * If sorting is allowed, the column clicked in.
  3592.      * @see #isAllowSorting
  3593.      * @see #setAllowSorting
  3594.      */
  3595.     protected int            columnClicked = -1;
  3596.     /**
  3597.      * If sorting is allowed, the column previously clicked in.
  3598.      * @see #isAllowSorting
  3599.      * @see #setAllowSorting
  3600.      */
  3601.     protected int            lastColumnClicked = -1;
  3602.     /**
  3603.      * Temp storage for columnClicked when repainting during a mouse drag.
  3604.      */
  3605.     protected int            memoryClick = -1;
  3606.  
  3607.     //???RKM??? What if multiSelect is true ???
  3608.     /**
  3609.      * The most recently selected row.
  3610.      */
  3611.     protected int            selectedRow = -1;
  3612.     /**
  3613.      * Flags indicating which rows are currently selected.
  3614.      */
  3615.     protected BitSet        highlightedRows = new BitSet();
  3616.  
  3617.     /**
  3618.      * The cells.
  3619.      */
  3620.     protected Matrix        cells = new Matrix();
  3621.     /**
  3622.      * The font used to display cell text.
  3623.      * @see #getCellFont
  3624.      * @see #setCellFont
  3625.      */
  3626.     protected Font            cellFont;
  3627.     /**
  3628.      * The height of cells, in pixels.
  3629.      */
  3630.     protected int            cellHeight = 0;
  3631.     /**
  3632.      * The ascent metric of the cell font.
  3633.      */
  3634.     protected int            cellAscent = 0;
  3635.     /**
  3636.      * The descent metric of the cell font.
  3637.      */
  3638.     protected int            cellDescent = 0;
  3639.  
  3640.     /**
  3641.      * The zero-relative index of the first visible row in the list.
  3642.      */
  3643.     protected int            topRow;
  3644.  
  3645.     /**
  3646.      * The latest vertical size of this component.
  3647.      */
  3648.     protected int            cachedHeight = -1;
  3649.     /**
  3650.      * The latest horizontal size of this component.
  3651.      */
  3652.     protected int            cachedWidth = -1;
  3653.     /**
  3654.      * The latest overall row width.
  3655.      */
  3656.     protected int            cachedLastSplitter = -1;
  3657.  
  3658.     /**
  3659.      * The zero-relative index of the column being resized via the UI.
  3660.      */
  3661.     protected int            dragColumn = -1;
  3662.     /**
  3663.      * The latest horizontal position the the splitter being dragged to resize a column.
  3664.      */
  3665.     protected int            xDragLast = -1;
  3666.     /**
  3667.      * The vertical scrollbar value.
  3668.      */
  3669.     protected int            sbVPosition = 0;
  3670.     /**
  3671.      * The horizontal scrollbar value.
  3672.      */
  3673.     protected int            sbHPosition = 0;
  3674.     /**
  3675.      * The width of the vertical scrollbar. 0 if it is not displayed.
  3676.      */
  3677.     protected int            verticalScrollbarWidth = 0;
  3678.     /**
  3679.      * The height of the horizontal scrollbar. 0 if it is not displayed.
  3680.      */
  3681.     protected int            scrollbarHeight = 0;
  3682.     /**
  3683.      * The unit increment of the horizontal scrollbar.
  3684.      * The default value is 4.
  3685.      */
  3686.     protected int            hScrollbarLineIncrement = 4;
  3687.     /**
  3688.      * The minimum allowed column width, in pixels.
  3689.      */
  3690.     protected int            minColumnWidth = 10;
  3691.     /**
  3692.      * The time of the previous click.
  3693.      * Used to detect when a row is double-clicked.
  3694.      */
  3695.     transient protected long        clickTime = 0;
  3696.     /**
  3697.      * The first part of the action command.
  3698.      * It is sent when a row is double-clicked, or enter is pressed.
  3699.      * @see #sourceActionEvent
  3700.      */
  3701.     protected String        actionCommand = "RowSelected:";
  3702.     /**
  3703.      * The vertical scrollbar.
  3704.      * Always exists. Shown as needed.
  3705.      */
  3706.     protected Scrollbar    verticalScrollbar;
  3707.     /**
  3708.      * The horizontal scrollbar.
  3709.      * Always exists. Shown as needed.
  3710.      */
  3711.     protected Scrollbar    horizontalScrollbar;
  3712.  
  3713.     //Sorting variables
  3714.     /**
  3715.      * The default cell comparer.
  3716.      * @see #getDefaultColumnSorter
  3717.      * @see #setDefaultColumnSorter
  3718.      */
  3719.     protected CompareCells defaultColumnSorter            = new CompareTextAndImageCells();
  3720.     /**
  3721.      * The cell compare routines to use for each column.
  3722.      * The routines are indexed using an Integer of the column index.
  3723.      * @see #getColumnSorter
  3724.      * @see #setColumnSorter
  3725.      */
  3726.     protected Hashtable columnCompareCellsRoutines        = new Hashtable();
  3727.  
  3728.     //Event sourcers
  3729.     /**
  3730.      * The action listener to keep track of listeners for our action event.
  3731.      */
  3732.     protected ActionListener    actionListener    = null;
  3733.     /**
  3734.      * The action listener to keep track of listeners for our item event.
  3735.      */
  3736.     protected ItemListener        itemListener    = null;
  3737.     /**
  3738.      * The action listener to keep track of listeners for our focus event.
  3739.      */
  3740.     protected FocusListener    focusListener    = null;
  3741.  
  3742.     //Event listeners
  3743.     private Mouse                mouse        = null;
  3744.     private MouseMotion        mouseMotion    = null;
  3745.     private Key                key            = null;
  3746.     private Focus                focus        = null;
  3747.     private Adjustment            adjustment    = null;
  3748.  
  3749.     private VetoableChangeSupport vetos    = new VetoableChangeSupport(this);
  3750.     private PropertyChangeSupport changes    = new PropertyChangeSupport(this);
  3751.  
  3752.     /**
  3753.      * Column splitter is being dragged to resize a column.
  3754.      */
  3755.     transient protected boolean        isDragging = false;
  3756.     /**
  3757.      * Mouse button has been pressed in a column heading.
  3758.      */
  3759.     transient protected boolean        clickedInHeadings = false;
  3760.     /**
  3761.      * Vertical scrollbar is visible.
  3762.      */
  3763.     transient protected boolean        sbVShow = false;
  3764.     /**
  3765.      * Horizontal scrollbar is visible.
  3766.      */
  3767.     transient protected boolean        sbHShow = false;
  3768.     /**
  3769.      * True if next paint will redraw the entire component even if
  3770.      * its size has not changed.
  3771.      */
  3772.     transient protected boolean        forceRedraw = false;
  3773.     /**
  3774.      * Column sizes need to be recalculated.
  3775.      */
  3776.     transient protected boolean        forceColumnSizeRecalc = false;
  3777.     /**
  3778.      * Offscreen image needs to be flushed.
  3779.      */
  3780.     transient protected boolean        forceFullRedraw = false;
  3781.     /**
  3782.      * Redrawing of component is currently suppressed.
  3783.      * @see #setSupressRedraw
  3784.      */
  3785.     transient protected boolean        isSuppressRedraw = false;
  3786.     /**
  3787.      * Redraw would have occurred if it hadn't been suppressed.
  3788.      */
  3789.     transient protected boolean        redrawWasSupressed = false;
  3790.     /**
  3791.      * This component has the focus.
  3792.      */
  3793.     transient protected boolean        hasFocus = false;
  3794.  
  3795.     //
  3796.     //Offscreen image graphics
  3797.     //
  3798.  
  3799.     /**
  3800.      * The offscreen image. Used to draw the component before painting it to the screen.
  3801.      */
  3802.     transient protected Image offscreenImage = null;
  3803.     /**
  3804.      * The graphics context used to draw into the offscreen image.
  3805.      */
  3806.     transient protected Graphics offscreenImageGraphics = null;
  3807.  
  3808.     /**
  3809.      * Error strings.
  3810.      */
  3811.     transient protected ResourceBundle errors;
  3812.  
  3813.     /**
  3814.      * True if running under Java 1.1.
  3815.      */
  3816.     static protected boolean isSun1_1;
  3817.  
  3818.     static
  3819.     {
  3820.         //Calc it once
  3821.  
  3822.         String vendor = System.getProperty("java.vendor");
  3823.         String version = System.getProperty("java.version");
  3824.         isSun1_1 = ((vendor.startsWith("Sun Microsystems Inc.") ||
  3825.                     (vendor.startsWith("Apple"))                ||
  3826.                     (vendor.startsWith("Symantec Corporation"))  ||
  3827.                     (vendor.startsWith("Netscape"))) &&
  3828.                    ((version.startsWith("11"))
  3829.                     || (version.startsWith("1.1"))));
  3830.     }
  3831. }
  3832.  
  3833.  
  3834. class DeprecatedException
  3835.     extends RuntimeException
  3836. {
  3837.     DeprecatedException(String msg)
  3838.     {
  3839.         super(msg);
  3840.     }
  3841. }
  3842.  
  3843.