home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / VCafe / Source.bin / MultiList.java < prev    next >
Encoding:
Java Source  |  1998-09-14  |  109.0 KB  |  3,832 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.     protected void resizeHeadings(int column)
  1278.     {
  1279.         if (column == headings.length)
  1280.             return;
  1281.  
  1282.         int columnsToAdd = column - headings.length;
  1283.  
  1284.         String[] newHeadings = new String[headings.length + columnsToAdd];
  1285.         int[] newSplitters = new int[newHeadings.length + 1];
  1286.  
  1287.         //Transfer over old columns
  1288.         {
  1289.             for (int i = 0;i < newHeadings.length;i++)
  1290.             {
  1291.                 if (i < headings.length)
  1292.                 {
  1293.                     newHeadings[i] = headings[i];
  1294.                     newSplitters[i + 1] = splitters[i + 1];
  1295.                 }
  1296.                 else
  1297.                 {
  1298.                     newHeadings[i] = ("Column " + (i + 1));
  1299.                     newSplitters[i + 1] = newSplitters[i] + getMinColumnWidth();
  1300.                 }
  1301.             }
  1302.         }
  1303.  
  1304.         headings = newHeadings;
  1305.         splitters = newSplitters;
  1306.  
  1307.         if (columnSizes == null)
  1308.             forceColumnSizeRecalc = true;
  1309.     }
  1310.  
  1311.     /**
  1312.      * Initializes all the MultiList cells with text. Each string in the provided
  1313.      * array initializes one row. The contents of each column are separated by
  1314.      * ";". For example, the string "col0;col1;col2" would result in "col0"
  1315.      * being placed in column 0, "col1" in column 1, and "col2" in column 2.
  1316.      * Any remaining columns will get cleared.
  1317.      * @param items the string array used to initialize all cell contents
  1318.      * @see #getListItems
  1319.      * @exception PropertyVetoException
  1320.      * if the specified property value is unacceptable
  1321.      */
  1322.     public void setListItems(String[] items) throws PropertyVetoException
  1323.     {
  1324.         //Supress redraws
  1325.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  1326.  
  1327.         try
  1328.         {
  1329.             String[] oldValue = getListItems();
  1330.  
  1331.             vetos.fireVetoableChange("listItems", oldValue, items);
  1332.  
  1333.             clear();
  1334.  
  1335.             for (int row = 0; row < items.length; row++)
  1336.             {
  1337.                 String s = items[row];
  1338.  
  1339.                 // Make sure that we handle null strings by creating an empty string in it's place
  1340.                 if (s == null)
  1341.                     s = "";
  1342.  
  1343.                 int len = s.length();
  1344.  
  1345.                 int start, end, col;
  1346.                 for (end = col = start = 0; end <= len /*&& col < headings.length*/; ++end)
  1347.                 {
  1348.                     if (end == len || s.charAt(end) == ';')
  1349.                     {
  1350.                         addCell(row, col++, s.substring(start, end), null);
  1351.                         start = end + 1;
  1352.  
  1353.                         //Handle adding of headings (hey, if the user puts in the data he/she probably wants to see it)
  1354.                         if (col > headings.length)
  1355.                             resizeHeadings(col);
  1356.                     }
  1357.                 }
  1358.  
  1359.                 while (col < headings.length)
  1360.                     addCell(row, col++, "", null);
  1361.             }
  1362.  
  1363.             changes.firePropertyChange("listItems", oldValue, getListItems());
  1364.         }
  1365.         finally
  1366.         {
  1367.             triggerRedraw();
  1368.             setSupressRedraw(wasSuppressingRedraw);
  1369.         }
  1370.     }
  1371.  
  1372.     /**
  1373.      * Returns the text contents of all the cells as a string array. A new
  1374.      * string is used for each row. The contents of each row's column are
  1375.      * separated by ";". For example, the string "col0;;col2" would result
  1376.      * from a "col0" in column 0, an empty column 1, and  "col2" in column 2.
  1377.      * @return the string array containing the text of all the cells
  1378.      * @see #setListItems
  1379.      */
  1380.     public String[] getListItems()
  1381.     {
  1382.         Vector v = new Vector();
  1383.         String s;
  1384.         boolean lastNull = false;
  1385.         int row, col;
  1386.         String rowString;
  1387.         String[] items = new String[cells.rows()];
  1388.  
  1389.         for (row = 0; row < cells.rows(); row++) {
  1390.             rowString = "";
  1391.  
  1392.             for (col = 0; col < headings.length; col++) {
  1393.                 s = getCellText(row, col);
  1394.                 if (col != 0) rowString += ";";
  1395.                 rowString += (s != null) ? s : "";
  1396.             }
  1397.  
  1398.             items[row] = rowString;
  1399.         }
  1400.  
  1401.         return items;
  1402.     }
  1403.  
  1404.     /**
  1405.      * Sets the flag to allow sorting of columns when a heading is selected.
  1406.      * @param b if true allow columns to be sorted when column heading is clicked;
  1407.      * if false, no sorting will occur
  1408.      * @see #isAllowSorting
  1409.      * @exception PropertyVetoException
  1410.      * if the specified property value is unacceptable
  1411.      */
  1412.     public void setAllowSorting(boolean b) throws PropertyVetoException
  1413.     {
  1414.         if (allowSorting != b)
  1415.         {
  1416.             Boolean oldValue = new Boolean(allowSorting);
  1417.             Boolean newValue = new Boolean(b);
  1418.  
  1419.             vetos.fireVetoableChange("allowSorting", oldValue, newValue);
  1420.  
  1421.             allowSorting = b;
  1422.  
  1423.             changes.firePropertyChange("allowSorting", oldValue, newValue);
  1424.         }
  1425.     }
  1426.  
  1427.     /**
  1428.      * Gets the flag controlling sorting of columns when a heading is selected.
  1429.      * @return true if columns will be sorted when column heading is clicked;
  1430.      * if false, no sorting will occur
  1431.      * @see #setAllowSorting
  1432.      */
  1433.     public boolean isAllowSorting()
  1434.     {
  1435.         return allowSorting;
  1436.     }
  1437.  
  1438.     /**
  1439.      * Sets the flag to allow resizing of columns via the user interface.
  1440.      * @param allowResizing if true the user may resize columns via the UI;
  1441.      * if false, columns cannot be resized via the UI
  1442.      * @see #isAllowResizingOfColumns
  1443.      * @exception PropertyVetoException
  1444.      * if the specified property value is unacceptable
  1445.      */
  1446.     public void setAllowResizingOfColumns(boolean allowResizing)
  1447.         throws PropertyVetoException
  1448.     {
  1449.         if (allowResizingOfColumns != allowResizing)
  1450.         {
  1451.             Boolean oldValue = new Boolean(allowResizingOfColumns);
  1452.             Boolean newValue = new Boolean(allowResizing);
  1453.  
  1454.             vetos.fireVetoableChange("allowResizingOfColumns", oldValue, newValue);
  1455.  
  1456.             allowResizingOfColumns = allowResizing;
  1457.  
  1458.             changes.firePropertyChange("allowResizingOfColumns", oldValue, newValue);
  1459.         }
  1460.     }
  1461.  
  1462.     /**
  1463.      * Gets whether columns may currently be resized by the user via the user interface.
  1464.      * @return true if resizing of columns via the UI is allowed;
  1465.      * if false, columns cannot be resized via the UI
  1466.      * @see #setAllowResizingOfColumns
  1467.      */
  1468.     public boolean isAllowResizingOfColumns()
  1469.     {
  1470.         return allowResizingOfColumns;
  1471.     }
  1472.  
  1473.     /**
  1474.      * Returns the minimum allowable column width in pixels.
  1475.      * @see #setMinColumnWidth
  1476.      */
  1477.     public int getMinColumnWidth()
  1478.     {
  1479.         return minColumnWidth;
  1480.     }
  1481.  
  1482.     /**
  1483.      * Adjusts the columns to default width.
  1484.      * The width of the columns is adjusted so that all columns fit into the
  1485.      * overall width of the MultiList.
  1486.      */
  1487.     public void adjustHeadings()
  1488.     {
  1489.         if (headings.length == 0)
  1490.         {
  1491.             internalCreateColumns(0);
  1492.             triggerRedraw();
  1493.         }
  1494.         else
  1495.         {
  1496.             Dimension d = size();
  1497.             int w = 0, width = (d.width - verticalScrollbarWidth) / headings.length, r = (d.width - verticalScrollbarWidth) - headings.length * width;
  1498.             splitters[0] = 0;
  1499.             for (int i = 0; i < headings.length; ++i)
  1500.                 splitters[i+1] = w += width+(i<r?1:0);
  1501.         }
  1502.     }
  1503.  
  1504.     /**
  1505.      * Initializes the MultiList with the specified total numer of columns.
  1506.      * All old column data is lost. Row data is preserved. Note that this
  1507.      * method is identical to the method setNumberOfCols(), but doesn't
  1508.      * allow the change to be vetoed.
  1509.      * @param i the new number of columns
  1510.      * @see #setNumberOfCols
  1511.      * @see #getNumberOfCols
  1512.      */
  1513.     public void createColumns(int i)
  1514.     {
  1515.         internalCreateColumns(i);
  1516.  
  1517.         triggerRedraw();
  1518.     }
  1519.  
  1520.     protected void internalCreateColumns(int i)
  1521.     {
  1522.         headings = new String[i];
  1523.         splitters = new int[i + 1];
  1524.  
  1525.         //???RKM??? I do not think this is wanted in all cases (setNumberOfCols)
  1526.         columnAlignments = null;
  1527.         columnSizes = null;
  1528.     }
  1529.  
  1530.     /**
  1531.      * Removes all rows from the MultiList.
  1532.      * @see #removeRow
  1533.      */
  1534.     public void clear()
  1535.     {
  1536.         cells.removeAllElements();
  1537.  
  1538.         //Stop dragging
  1539.         xDragLast = -1;
  1540.         isDragging = false;
  1541.  
  1542.         selectedRow = -1;
  1543.         highlightedRows = new BitSet();
  1544.  
  1545.         topRow = 0;
  1546.         sbVPosition = 0;
  1547.  
  1548.         triggerRedraw();
  1549.     }
  1550.  
  1551.     /**
  1552.      * Removes a single row from the MultiList.
  1553.      * @param row the zero-relative index of the row to remove
  1554.      * @see #clear
  1555.      */
  1556.     public void removeRow(int row)
  1557.     {
  1558.         int numRows = cells.rows();
  1559.  
  1560.         //Make certain row is in range
  1561.         if (row < 0 || row > numRows - 1)
  1562.             return;
  1563.  
  1564.         //Remove the row from the matrix
  1565.         cells.removeRow(row);
  1566.  
  1567.         //If the row is selected, remove it from the selection
  1568.         if (highlightedRows.get(row))
  1569.             highlightedRows.clear(row);
  1570.  
  1571.         //Move selections down
  1572.         for (int i = row + 1;i < numRows;i++)
  1573.         {
  1574.             if (highlightedRows.get(i))
  1575.             {
  1576.                 highlightedRows.clear(i);
  1577.                 highlightedRows.set(i - 1);
  1578.             }
  1579.         }
  1580.  
  1581.         triggerRedraw();
  1582.     }
  1583.  
  1584.     /**
  1585.      * Gets the object used to compare cells for sorting the specified column.
  1586.      * If no sorter has been explicitly specified, the default column sorter
  1587.      * will be returned.
  1588.      * @param column the zero-relative column index
  1589.      * @return the object used to compare column cells
  1590.      * @see #setColumnSorter
  1591.      */
  1592.     public CompareCells getColumnSorter(int column)
  1593.     {
  1594.         CompareCells compareCellsFunc = (CompareCells)columnCompareCellsRoutines.get(new Integer(column));
  1595.         if (compareCellsFunc == null)
  1596.             compareCellsFunc = getDefaultColumnSorter();
  1597.         return compareCellsFunc;
  1598.     }
  1599.  
  1600.     /**
  1601.      * Sets the object used to compare cells for sorting the specified column.
  1602.      * @param column the zero-relative column index
  1603.      * @param compare the object used to compare column cells
  1604.      * @see #getColumnSorter
  1605.      */
  1606.     public void setColumnSorter(int column, CompareCells compare)
  1607.     {
  1608.         columnCompareCellsRoutines.put(new Integer(column),compare);
  1609.     }
  1610.  
  1611.     /**
  1612.      * Gets the default object used to compare column cells for sorting.
  1613.      * @return the object used to compare column cells by default
  1614.      * @see #setDefaultColumnSorter
  1615.      * @see #getColumnSorter
  1616.      */
  1617.     public CompareCells getDefaultColumnSorter()
  1618.     {
  1619.         return defaultColumnSorter;
  1620.     }
  1621.  
  1622.     /**
  1623.      * Sets the default object used to compare column cells for sorting.
  1624.      * @param newDefaultSorter the object used to compare column cells by default
  1625.      * @see #getDefaultColumnSorter
  1626.      * @see #setColumnSorter
  1627.      */
  1628.     public void setDefaultColumnSorter(CompareCells newDefaultSorter)
  1629.     {
  1630.         defaultColumnSorter = newDefaultSorter;
  1631.     }
  1632.  
  1633.     /**
  1634.      * Sets the text of a cell at the given row and column position.
  1635.      * @param r the zero-relative row index
  1636.      * @param c the zero-relative column index
  1637.      * @param s the new cell text
  1638.      * @see #addImageCell
  1639.      * @see #addCell
  1640.      */
  1641.     public void addTextCell(int r, int c, String s)
  1642.     {
  1643.         if (s == null)
  1644.             s = new String("");
  1645.         TextAndImageCell cell = new TextAndImageCell(this, s);
  1646.         addCellImpl(r,c,cell);
  1647.     }
  1648.  
  1649.     /**
  1650.      * Sets the image of a cell at the given row and column position.
  1651.      * @param r the zero-relative row index
  1652.      * @param c the zero-relative column index
  1653.      * @param i the new cell image
  1654.      * @see #addTextCell
  1655.      * @see #addCell
  1656.      */
  1657.     public void addImageCell(int r, int c, Image i)
  1658.     {
  1659.         TextAndImageCell cell = new TextAndImageCell(this, i);
  1660.         addCellImpl(r,c,cell);
  1661.     }
  1662.  
  1663.     /**
  1664.      * Sets the contents of a cell, both text and image.
  1665.      * @param r the zero-relative row index
  1666.      * @param c the zero-relative column index
  1667.      * @param s the new cell text
  1668.      * @param i the new cell image
  1669.      * @see #addTextCell
  1670.      * @see #addImageCell
  1671.      */
  1672.     public void addCell(int r, int c, String s, Image i)
  1673.     {
  1674.         TextAndImageCell cell = new TextAndImageCell(this, s, i);
  1675.         addCellImpl(r,c,cell);
  1676.     }
  1677.  
  1678.     /**
  1679.      * Sets the contents of a cell to the given cell.
  1680.      * @param row the zero-relative row index
  1681.      * @param column the zero-relative column index
  1682.      * @param cell the new cell
  1683.      * @see #addCell
  1684.      * @see #addTextCell
  1685.      * @see #addImageCell
  1686.      */
  1687.     public void addCell(int row, int column, Cell cell)
  1688.     {
  1689.         addCellImpl(row,column,cell);
  1690.     }
  1691.  
  1692.     /**
  1693.      * Adds a cell to the MultiList in the row and column specified.
  1694.      * All addCell forms call this method.
  1695.      * @param row the zero-relative row index
  1696.      * @param column the zero-relative column index
  1697.      * @param cell the cell that is being added to the MultiList
  1698.      */
  1699.     protected void addCellImpl(int row, int column, Cell cell)
  1700.     {
  1701.         cells.updateElement(row, column, cell);
  1702.  
  1703.         triggerRedraw();
  1704.     }
  1705.  
  1706.     /**
  1707.      * Returns the text of the specified cell.
  1708.      * @param r the zero-relative row index
  1709.      * @param c the zero-relative column index
  1710.      * @return the cell text or the empty string ("") if the cell is not allocated
  1711.      * @see #getCellImage
  1712.      * @see #addCell
  1713.      * @see #addTextCell
  1714.      */
  1715.     public String getCellText(int r, int c)
  1716.     {
  1717.         try
  1718.         {
  1719.             //Calling elementAt will throw if the element is not allocated
  1720.             TextAndImageCell cell = (TextAndImageCell)cells.elementAt(r,c);
  1721.  
  1722.             //The element may be allocated, but that doesn't mean there's an object there
  1723.             if (cell != null)
  1724.                 return cell.getText();
  1725.         }
  1726.         catch(ArrayIndexOutOfBoundsException e)
  1727.         {
  1728.             //Fall through, so an empty string is returned
  1729.         }
  1730.         catch(ClassCastException e)
  1731.         {
  1732.             //Fall through, so an empty string is returned
  1733.         }
  1734.  
  1735.         return "";
  1736.     }
  1737.  
  1738.     /**
  1739.      * Returns the image of the specified cell, if any.
  1740.      * @param r the zero-relative row index
  1741.      * @param c the zero-relative column index
  1742.      * @return the cell image, or null if the cell is not allocated
  1743.      * @see #getCellText
  1744.      * @see #addCell
  1745.      * @see #addImageCell
  1746.      */
  1747.     public Image getCellImage(int r, int c)
  1748.     {
  1749.         try
  1750.         {
  1751.             //Calling elementAt will throw if the element is not allocated
  1752.             TextAndImageCell cell = (TextAndImageCell)cells.elementAt(r,c);
  1753.  
  1754.             //The element may be allocated, but that doesn't mean there's an object there
  1755.             if (cell != null)
  1756.                 return cell.getImage();
  1757.         }
  1758.         catch(ArrayIndexOutOfBoundsException e)
  1759.         {
  1760.             //Fall through, so null is returned
  1761.         }
  1762.         catch(ClassCastException e)
  1763.         {
  1764.             //Fall through, so null is returned
  1765.         }
  1766.  
  1767.         return null;
  1768.     }
  1769.  
  1770.     /**
  1771.      * Handles redrawing of this component on the screen.
  1772.      * This is a standard Java AWT method which gets called by the Java
  1773.      * AWT (repaint()) to handle repainting this component on the screen.
  1774.      * The graphics context clipping region is set to the bounding rectangle
  1775.      * of this component and its [0,0] coordinate is this component's
  1776.      * top-left corner.
  1777.      * Typically this method paints the background color to clear the
  1778.      * component's drawing space, sets graphics context to be the foreground
  1779.      * color, and then calls paint() to draw the component.
  1780.      *
  1781.      * It is overridden here to eliminate the unneeded repainting of the background.
  1782.      *
  1783.      * @param g the graphics context
  1784.      * @see java.awt.Component#repaint
  1785.      * @see #paint
  1786.      */
  1787.     public void update(Graphics g)
  1788.     {
  1789.         paint(g);
  1790.     }
  1791.  
  1792.     /**
  1793.      * Paints this component using the given graphics context.
  1794.      * This is a standard Java AWT method which typically gets called
  1795.      * by the AWT to handle painting this component. It paints this component
  1796.      * using the given graphics context. The graphics context clipping region
  1797.      * is set to the bounding rectangle of this component and its [0,0]
  1798.      * coordinate is this component's top-left corner.
  1799.      *
  1800.      * @param g the graphics context used for painting
  1801.      * @see java.awt.Component#repaint
  1802.      * @see #update
  1803.      */
  1804.     public void paint(Graphics g)
  1805.     {
  1806.         // we can enter here without an image if the multilist is created on
  1807.         // a panel without a peer (i.e., a tab panel).  Just to make sure, create
  1808.         // it if we need one...
  1809.  
  1810.         if (offscreenImage == null)
  1811.             redraw();
  1812.  
  1813.         if (offscreenImage != null)
  1814.         {
  1815.             Font f = g.getFont();
  1816.             if (f == null)
  1817.             {
  1818.                 f = headingFont;
  1819.                 setFont(f);
  1820.                 return;
  1821.             }
  1822.  
  1823.             Dimension s = size();
  1824.  
  1825.             //???RKM??? Why would it always redraw at design time???
  1826.             //???RKM??? DO NOT JUST TURN THIS BACK ON - PLEASE COME TALK TO ME (YOU HAVE FOUND A BUG)
  1827.             if (cachedWidth != s.width || cachedHeight != s.height || forceRedraw /*|| java.beans.Beans.isDesignTime()*/)
  1828.                 redraw();
  1829.  
  1830.             int heightMinusScrollbar = s.height - scrollbarHeight;
  1831.             int widthMinusScrollbar = s.width - verticalScrollbarWidth;
  1832.  
  1833.             g.translate(-sbHPosition, 0);
  1834.             if (sbVShow && sbHShow)
  1835.             {
  1836.                 g.setColor(Color.lightGray);
  1837.                 g.fillRect(sbHPosition + widthMinusScrollbar, heightMinusScrollbar, verticalScrollbarWidth, scrollbarHeight);
  1838.                 g.setColor(Color.black);
  1839.                 g.drawRect(sbHPosition + widthMinusScrollbar - 1, heightMinusScrollbar - 1, verticalScrollbarWidth, scrollbarHeight);
  1840.             }
  1841.  
  1842.             g.clipRect(sbHPosition,0,widthMinusScrollbar,heightMinusScrollbar);
  1843.             g.drawImage(offscreenImage, 0, 0, this);
  1844.  
  1845.             //Draw frame
  1846.             //???RKM??? This will flicker - fix
  1847.             g.setColor(Color.black);
  1848.             g.drawRect(sbHPosition,0, widthMinusScrollbar - 1, heightMinusScrollbar - 1);
  1849.         }
  1850.     }
  1851.  
  1852.     /**
  1853.      * Ensures the given column index is valid.
  1854.      * An IllegalArgumentException is thrown if it is out of range.
  1855.      * @param column the zero-relative column index to validate
  1856.      */
  1857.     protected void rangeCheckColumn(int column)
  1858.     {
  1859.         if (column < 0 || column > headings.length - 1)
  1860.             throw new IllegalArgumentException(errors.getString("InvalidColumnIndex") + column);
  1861.     }
  1862.  
  1863.     /**
  1864.      * Ensures the given column alignment value is valid.
  1865.      * An IllegalArgumentException is thrown if the value is invalid.
  1866.      * @param alignment the column alignment value to validate, should
  1867.      * be one of: LEFT, CENTER, or RIGHT
  1868.      * @see #LEFT
  1869.      * @see #CENTER
  1870.      * @see #RIGHT
  1871.      */
  1872.     protected void rangeCheckAlignment(int alignment)
  1873.     {
  1874.         if (alignment != LEFT && alignment != CENTER && alignment != RIGHT){
  1875.             Object[] args = { new Integer(alignment), "LEFT", "CENTER", "RIGHT" };
  1876.             throw new IllegalArgumentException(MessageFormat.format(errors.getString("InvalidAlignment"), args));
  1877.         }
  1878.     }
  1879.  
  1880.     /**
  1881.      * Determines if the vertical scrollbar needs to be shown.
  1882.      * If column sizes are being automatically determined,
  1883.      * forces a column size recalc as needed.
  1884.      */
  1885.     protected void calcVerticalScrollbarPosition(boolean minusHorizontalScrollbar)
  1886.     {
  1887.         Dimension s = size();
  1888.  
  1889.         //Calc if vertical scrollbar is needed
  1890.         if (cells.rows() * cellHeight > s.height - headingHeight - BORDER - (headingHeight > 0 ? 0 : BORDER) - (minusHorizontalScrollbar ? scrollbarHeight : 0))
  1891.         {
  1892.             sbVShow = true;
  1893.             verticalScrollbarWidth = verticalScrollbar.preferredSize().width - 1;
  1894.         }
  1895.         else
  1896.         {
  1897.             sbVShow = false;
  1898.             verticalScrollbarWidth = 0;
  1899.         }
  1900.  
  1901.         //Adjust column widths, if we are auto calculating
  1902.         if (columnSizes == null && (forceColumnSizeRecalc || (cachedWidth != s.width - verticalScrollbarWidth)))
  1903.             adjustHeadings();
  1904.         forceColumnSizeRecalc = false;
  1905.     }
  1906.  
  1907.     /**
  1908.      * Paints this component into an offscreen image for cleaner screen repaints.
  1909.      * It is not typically called directly.
  1910.      */
  1911.     public void redraw()
  1912.     {
  1913.         boolean wasSuppressingRedraw;
  1914.  
  1915.         Dimension s = size();
  1916.  
  1917.         //Set redraw to false
  1918.         forceRedraw = false;
  1919.  
  1920.         if (s.width == 0 || s.height == 0)
  1921.             return;
  1922.  
  1923.         //Suppress redraws.  I had to avoid using setSuppressRedraw to avoid looping. LAB.
  1924.         wasSuppressingRedraw = isSuppressRedraw;
  1925.         isSuppressRedraw = true;
  1926.         try
  1927.         {
  1928.             calcVerticalScrollbarPosition(false);
  1929.         }
  1930.         finally
  1931.         {
  1932.             isSuppressRedraw = wasSuppressingRedraw;
  1933.         }
  1934.  
  1935.         int lastSplitter = splitters[splitters.length-1];
  1936.  
  1937.         if (lastSplitter > s.width - verticalScrollbarWidth)
  1938.         {
  1939.             sbHShow = true;
  1940.             scrollbarHeight = horizontalScrollbar.preferredSize().height - 1;
  1941.  
  1942.             //Suppress redraws.  I had to avoid using setSuppressRedraw to avoid looping. LAB.
  1943.             wasSuppressingRedraw = isSuppressRedraw;
  1944.             isSuppressRedraw = true;
  1945.             try
  1946.             {
  1947.                 calcVerticalScrollbarPosition(true);
  1948.             }
  1949.             finally
  1950.             {
  1951.                 isSuppressRedraw = wasSuppressingRedraw;
  1952.             }
  1953.         }
  1954.         else
  1955.         {
  1956.             sbHShow = false;
  1957.             scrollbarHeight = 0;
  1958.         }
  1959.  
  1960.         //Post calc, set scrollbar positions
  1961.         if (!sbVShow)
  1962.             sbVPosition = 0;
  1963.         if (!sbHShow)
  1964.             sbHPosition = 0;
  1965.  
  1966.         {
  1967.             //Calc needed image width
  1968.             int imageWidth = Math.max(lastSplitter, s.width + sbHPosition);
  1969.  
  1970.             //Create a new offscreen image, if we need to
  1971.             if (offscreenImage == null ||
  1972.                 offscreenImageGraphics == null ||
  1973.                 cachedWidth != imageWidth ||
  1974.                 cachedHeight != s.height ||
  1975.                 cachedLastSplitter != lastSplitter ||
  1976.                 offscreenImage.getWidth(this) != imageWidth ||
  1977.                 forceFullRedraw)
  1978.             {
  1979.                 forceFullRedraw = false;
  1980.  
  1981.                 //If old offscreenImage, flush it
  1982.                 if (offscreenImage != null)
  1983.                 {
  1984.                     offscreenImage.flush();
  1985.                     offscreenImage = null;
  1986.                 }
  1987.  
  1988.                 //Create a new offscreen image
  1989.                 offscreenImage = createImage(imageWidth, s.height);
  1990.  
  1991.                 //Cache the height and width of the MultiList
  1992.                 cachedWidth = s.width;
  1993.                 cachedHeight = s.height;
  1994.  
  1995.                 //!!!RKM!!! Note: lastSplitter has to be cached due to the clipRect below
  1996.                 //???RKM??? This could be removed if we get a working setClip
  1997.                 //Cache last splitter
  1998.                 cachedLastSplitter = lastSplitter;
  1999.  
  2000.                 //If there previously was a graphics, dispose it
  2001.                 if (offscreenImageGraphics != null)
  2002.                 {
  2003.                     offscreenImageGraphics.dispose();
  2004.                     offscreenImageGraphics = null;
  2005.                 }
  2006.  
  2007.                 //Hook up to the new offscreenImage
  2008.                 if (offscreenImage != null)
  2009.                     offscreenImageGraphics = offscreenImage.getGraphics();
  2010.             }
  2011.         }
  2012.  
  2013.         if (offscreenImageGraphics != null)
  2014.         {
  2015.             offscreenImageGraphics.setColor(colorBg);
  2016.             offscreenImageGraphics.fillRect(0,0,offscreenImage.getWidth(this), s.height);
  2017.         }
  2018.  
  2019.         //Cache numRows, since cells.rows() is expensive
  2020.         int numRows = cells.rows();
  2021.  
  2022.         if (sbVShow)
  2023.         {
  2024.             int vis = getNumVisibleRows();
  2025.  
  2026.             verticalScrollbar.reshape(s.width - verticalScrollbarWidth,0,verticalScrollbarWidth,s.height - scrollbarHeight);
  2027.             verticalScrollbar.setValues(sbVPosition, vis, 0, numRows - (isSun1_1 ? 0 : vis));
  2028.             verticalScrollbar.setPageIncrement(vis-1);
  2029.             verticalScrollbar.show();
  2030.         }
  2031.         else
  2032.         {
  2033.             topRow = 0;
  2034.             verticalScrollbar.hide();
  2035.         }
  2036.  
  2037.         if (sbHShow)
  2038.         {
  2039.             horizontalScrollbar.reshape(0,s.height-scrollbarHeight,s.width-verticalScrollbarWidth,scrollbarHeight);
  2040.             horizontalScrollbar.setValues(sbHPosition, s.width-verticalScrollbarWidth, 0, lastSplitter-(isSun1_1?0:(s.width-verticalScrollbarWidth)));
  2041.             horizontalScrollbar.setPageIncrement(s.width-verticalScrollbarWidth);
  2042.             horizontalScrollbar.setLineIncrement(hScrollbarLineIncrement);
  2043.             horizontalScrollbar.show();
  2044.         }
  2045.         else
  2046.         {
  2047.             horizontalScrollbar.hide();
  2048.         }
  2049.  
  2050.         if (offscreenImageGraphics != null)
  2051.         {
  2052.             if (headings.length > 0)
  2053.             {
  2054.                 drawHeading(false);
  2055.  
  2056.                 if (lastSplitter > 0)
  2057.                     offscreenImageGraphics.clipRect(0,0,lastSplitter,s.height);
  2058.  
  2059.                 int count = cellHeight > 0 ? getNumVisibleRows() : 0;
  2060.                 if (count > numRows)
  2061.                     count = numRows;
  2062.  
  2063.                 offscreenImageGraphics.setFont(cellFont);
  2064.                 drawRows(topRow, count, false);
  2065.             }
  2066.         }
  2067.     }
  2068.  
  2069.     /**
  2070.      * This routine changes the selected row, handling multiple selections
  2071.      * as needed.
  2072.      * @param newSelection the zero-relative index of the newly selected row
  2073.      * @param meta the state of the modifier keys from Event.modifiers
  2074.      * @see setSelectedRow
  2075.      * @see Event#CTRL_MASK
  2076.      * @see Event#SHIFT_MASK
  2077.      */
  2078.     public void changeSelection(int newSelection, int meta)
  2079.     {
  2080.         //Supress redraws
  2081.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  2082.  
  2083.         try
  2084.         {
  2085.             int numberOfRows = getNumberOfRows();
  2086.  
  2087.             if (!((newSelection < numberOfRows) && (newSelection > -1)))
  2088.             {
  2089.                 int[] selRows = getSelectedRows();
  2090.                 if (multiSelect)
  2091.                 {
  2092.                     if (((meta & Event.SHIFT_MASK) == Event.SHIFT_MASK) || ((meta & Event.CTRL_MASK) == Event.CTRL_MASK))
  2093.                         return;
  2094.                     deselectEvent(newSelection, selRows);
  2095.                 }
  2096.                 highlightedRows = new BitSet();
  2097.                 paintSelection(selRows);
  2098.                 repaint();
  2099.                 return;
  2100.             }
  2101.  
  2102.             if (multiSelect)
  2103.                 multiSel(newSelection, meta);
  2104.             else
  2105.                 singleSel(newSelection, meta);
  2106.  
  2107.             // scroll a page UPWARD
  2108.             if (newSelection < topRow)
  2109.             {
  2110.                 topRow = newSelection;
  2111.  
  2112.                 if (topRow < 0)
  2113.                     topRow = 0;
  2114.  
  2115.                 sbVPosition = topRow;
  2116.                 verticalScrollbar.setValue(sbVPosition);
  2117.  
  2118.                 triggerRedraw();
  2119.             }
  2120.  
  2121.             // scroll a page DOWNWARD
  2122.             if ((newSelection-topRow+1) > getNumVisibleRows())
  2123.             {
  2124.                 topRow = topRow + 1;
  2125.  
  2126.                 if (topRow > numberOfRows-1)
  2127.                     topRow = newSelection;
  2128.  
  2129.                 sbVPosition = topRow;
  2130.                 verticalScrollbar.setValue(sbVPosition);
  2131.  
  2132.                 triggerRedraw();
  2133.             }
  2134.  
  2135.             selectedRow = newSelection;
  2136.             drawRows(selectedRow, 1, true);
  2137.         }
  2138.         finally
  2139.         {
  2140.             triggerRedraw();
  2141.             setSupressRedraw(wasSuppressingRedraw);
  2142.         }
  2143.     }
  2144.  
  2145.     /**
  2146.      * Tells this component that it has been added to a container.
  2147.      * This is a standard Java AWT method which gets called by the AWT when
  2148.      * this component is added to a container. Typically, it is used to
  2149.      * create this component's peer.
  2150.      *
  2151.      * It has been overridden here to add local event listeners.
  2152.      *
  2153.      * @see #removeNotify
  2154.      */
  2155.     public synchronized void addNotify()
  2156.     {
  2157.         super.addNotify();
  2158.         
  2159.         try
  2160.         {
  2161.             errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  2162.         }
  2163.         catch(Throwable ex)
  2164.         {
  2165.             errors = new symantec.itools.resources.ErrorsBundle();
  2166.         }
  2167.  
  2168.         //Hook up listeners
  2169.         if (mouse == null)
  2170.         {
  2171.             mouse = new Mouse();
  2172.             addMouseListener(mouse);
  2173.         }
  2174.         if (mouseMotion == null)
  2175.         {
  2176.             mouseMotion = new MouseMotion();
  2177.             addMouseMotionListener(mouseMotion);
  2178.         }
  2179.         if (key == null)
  2180.         {
  2181.             key = new Key();
  2182.             addKeyListener(key);
  2183.         }
  2184.         if (adjustment == null)
  2185.         {
  2186.             adjustment = new Adjustment();
  2187.             verticalScrollbar.addAdjustmentListener(adjustment);
  2188.             horizontalScrollbar.addAdjustmentListener(adjustment);
  2189.         }
  2190.         if (focus == null)
  2191.         {
  2192.             focus = new Focus();
  2193.             addFocusListener(focus);
  2194.         }
  2195.     }
  2196.  
  2197.     /**
  2198.      * Tells this component that it is being removed from a container.
  2199.      * This is a standard Java AWT method which gets called by the AWT when
  2200.      * this component is removed from a container. Typically, it is used to
  2201.      * destroy the peers of this component and all its subcomponents.
  2202.      *
  2203.      * It has been overridden here to remove local event listeners.
  2204.      *
  2205.      * @see #addNotify
  2206.      */
  2207.     public synchronized void removeNotify()
  2208.     {
  2209.         if (mouse != null)
  2210.         {
  2211.             removeMouseListener(mouse);
  2212.             mouse = null;
  2213.         }
  2214.         if (mouseMotion != null)
  2215.         {
  2216.             removeMouseMotionListener(mouseMotion);
  2217.             mouseMotion = null;
  2218.         }
  2219.         if (key != null)
  2220.         {
  2221.             removeKeyListener(key);
  2222.             key = null;
  2223.         }
  2224.         if (adjustment != null)
  2225.         {
  2226.             verticalScrollbar.removeAdjustmentListener(adjustment);
  2227.             horizontalScrollbar.removeAdjustmentListener(adjustment);
  2228.             adjustment = null;
  2229.         }
  2230.         if (focus != null)
  2231.         {
  2232.             removeFocusListener(focus);
  2233.             focus = null;
  2234.         }
  2235.  
  2236.         super.removeNotify();
  2237.     }
  2238.  
  2239.     private void multiSel(int newSelection, int meta)
  2240.     {
  2241.             if ((meta & Event.CTRL_MASK) == Event.CTRL_MASK)
  2242.             {
  2243.                 if (highlightedRows.get(newSelection))
  2244.                     deselectRow(newSelection);
  2245.                 else
  2246.                 {
  2247.                     removeSelectionBorder();
  2248.                     selectRow(newSelection);
  2249.                 }
  2250.             }
  2251.             else if ((meta & Event.SHIFT_MASK) == Event.SHIFT_MASK)
  2252.             {
  2253.                 if (selectedRow == -1)
  2254.                     selectedRow = newSelection;
  2255.  
  2256.                 for (int i=Math.min(selectedRow, newSelection);
  2257.                      i<=Math.max(selectedRow, newSelection);
  2258.                      i++)
  2259.                 {
  2260.                     if (i>=0)
  2261.                     {
  2262.                         highlightedRows.set(i);
  2263.                     }
  2264.                 }
  2265.                 sourceItemEvent(ItemEvent.SELECTED);
  2266.                 selectedRow = -1;
  2267.                 paintSelection(getSelectedRows());
  2268.             }
  2269.             else
  2270.             {
  2271.                 int[] selRows = getSelectedRows();
  2272.                 deselectEvent(newSelection, selRows);
  2273.                 highlightedRows = new BitSet();
  2274.                 removeSelectionBorder();
  2275.                 selectedRow = -1;
  2276.                 paintSelection(selRows);
  2277.                 selectRow(newSelection);
  2278.             }
  2279.     }
  2280.  
  2281.     private void singleSel(int newSelection, int meta)
  2282.     {
  2283.         if (selectedRow >= 0 && highlightedRows.get(selectedRow))
  2284.         {
  2285.             if (selectedRow != newSelection)
  2286.             {
  2287.                 deselectRow(selectedRow);
  2288.                 selectRow(newSelection);
  2289.             }
  2290.             else if ((meta & Event.CTRL_MASK) == Event.CTRL_MASK)
  2291.             {
  2292.                 deselectRow(selectedRow);
  2293.             }
  2294.         }
  2295.         else
  2296.         {
  2297.             removeSelectionBorder();
  2298.             selectRow(newSelection);
  2299.         }
  2300.     }
  2301.  
  2302.     private void deselectEvent(int newSelection, int[] selRows)
  2303.     {
  2304.         int numberSelected = selRows.length;
  2305.  
  2306.         if ((numberSelected>1) ||
  2307.             ((numberSelected==1) &&
  2308.              (selectedRow!=newSelection)))
  2309.         {
  2310.             sourceItemEvent(ItemEvent.DESELECTED);
  2311.         }
  2312.     }
  2313.  
  2314.     /**
  2315.      * Paints the specified rows as selected.
  2316.      * @param selRows an array of the zero-relative indexes of selected rows
  2317.      */
  2318.     protected void paintSelection(int[] selRows)
  2319.     {
  2320.         for (int i = 0; i < selRows.length ;i++)
  2321.         {
  2322.             int selectedRow = selRows[i];
  2323.             if (selectedRow - sbVPosition >= 0 && selectedRow - sbVPosition <= getNumVisibleRows()+1)
  2324.                 drawRows(selectedRow, 1, true);
  2325.         }
  2326.     }
  2327.  
  2328.     /**
  2329.      * Selects the row, if it is not already selected.
  2330.      * @param row the zero-relative row index
  2331.      * @see #deselectRow
  2332.      */
  2333.     public void selectRow(int row)
  2334.     {
  2335.         if (!highlightedRows.get(row))
  2336.         {
  2337.             //If in single select mode, deselect previously selected row
  2338.             if (!multiSelect)
  2339.             {
  2340.                 int selectedRow = getSelectedRow();
  2341.                 if (selectedRow != -1 && selectedRow != row)
  2342.                     deselectRow(selectedRow);
  2343.             }
  2344.  
  2345.             highlightedRows.set(row);
  2346.             sourceItemEvent(ItemEvent.SELECTED);
  2347.  
  2348.             drawRows(row, 1, true);
  2349.         }
  2350.     }
  2351.  
  2352.     /**
  2353.      * Deselects the row, if it is not already selected.
  2354.      * @param row the zero-relative row index
  2355.      * @see #selectRow
  2356.      */
  2357.     public void deselectRow(int row)
  2358.     {
  2359.         //If the row is hilited, unhilite it
  2360.         if (highlightedRows.get(row))
  2361.         {
  2362.             highlightedRows.clear(row);
  2363.             sourceItemEvent(ItemEvent.DESELECTED);
  2364.  
  2365.             //If the row has the selection border, remove it
  2366.             if (row == selectedRow)
  2367.                 removeSelectionBorder();
  2368.             else
  2369.                 drawRows(row, 1, true);
  2370.         }
  2371.     }
  2372.  
  2373.     /**
  2374.      * If there is a selected row, it removes the selection border from it.
  2375.      */
  2376.     protected void removeSelectionBorder()
  2377.     {
  2378.         if (selectedRow != -1)
  2379.         {
  2380.             int saveSelectedRow = selectedRow;
  2381.             selectedRow = -1;
  2382.             drawRows(saveSelectedRow, 1, true);
  2383.             selectedRow = saveSelectedRow;
  2384.         }
  2385.     }
  2386.  
  2387.     /**
  2388.      * Returns the recommended dimensions to properly display this component.
  2389.      * This is a standard Java AWT method which gets called to determine
  2390.      * the recommended size of this component.
  2391.      *
  2392.      * @see #minimumSize
  2393.      */
  2394.     //???RKM??? Seems wrong - hard coded
  2395.     public synchronized Dimension preferredSize()
  2396.     {
  2397.         return new Dimension(175, 125);
  2398.     }
  2399.  
  2400.     /**
  2401.      * Returns the minimum dimensions to properly display this component.
  2402.      * This is a standard Java AWT method which gets called to determine
  2403.      * the minimum size of this component.
  2404.      *
  2405.      * @see #preferredSize
  2406.      */
  2407.     //???RKM??? Seems wrong - hard coded
  2408.     public synchronized Dimension minimumSize()
  2409.     {
  2410.         return new Dimension(50, 50);
  2411.     }
  2412.  
  2413.     /**
  2414.      * Adds the specified action listener to receive action events
  2415.      * from this object.
  2416.      * @param l the action listener
  2417.      * @see #removeActionListener
  2418.      */
  2419.     public synchronized void addActionListener(ActionListener l)
  2420.     {
  2421.         actionListener = AWTEventMulticaster.add(actionListener, l);
  2422.     }
  2423.  
  2424.     /**
  2425.      * Removes the specified action listener so it no longer receives
  2426.      * action events from this object.
  2427.      * @param l the action listener
  2428.      * @see #addActionListener
  2429.      */
  2430.     public synchronized void removeActionListener(ActionListener l)
  2431.     {
  2432.         actionListener = AWTEventMulticaster.remove(actionListener, l);
  2433.     }
  2434.  
  2435.     /**
  2436.      * Adds the specified item listener to receive item events from this object.
  2437.      * @param l the item listener
  2438.      * @see #removeItemListener
  2439.      * @see #sourceItemEvent
  2440.      */
  2441.     public synchronized void addItemListener(ItemListener l)
  2442.     {
  2443.         itemListener = AWTEventMulticaster.add(itemListener, l);
  2444.     }
  2445.  
  2446.     /**
  2447.      * Removes the specified item listener so it no longer receives
  2448.      * item events from this object.
  2449.      * @param l the action listener
  2450.      * @see #addItemListener
  2451.      * @see #sourceItemEvent
  2452.      */
  2453.     public synchronized void removeItemListener(ItemListener l)
  2454.     {
  2455.         itemListener = AWTEventMulticaster.remove(itemListener, l);
  2456.     }
  2457.  
  2458.     /**
  2459.      * This is the Mouse Event handling innerclass.
  2460.      */
  2461.     class Mouse extends java.awt.event.MouseAdapter implements java.io.Serializable
  2462.     {
  2463.         /**
  2464.          * Handles Mouse Pressed events
  2465.          * @param e the MouseEvent
  2466.          */
  2467.         public void mousePressed(MouseEvent e)
  2468.         {
  2469.             int x = e.getX();
  2470.             int y = e.getY();
  2471.  
  2472.             requestFocus();
  2473.  
  2474.             //Reset column click code
  2475.             clickedInHeadings = false;
  2476.  
  2477.             //Is the click in the header
  2478.             if (y < headingHeight)
  2479.             {
  2480.                 if (allowResizingOfColumns)
  2481.                 {
  2482.                     // It's a click on the heading area:
  2483.                     // is it close enough to be a column size drag?
  2484.                     for (int i = 1;i< splitters.length - 1;i++)
  2485.                     {
  2486.                         int currSplitter = splitters[i];
  2487.                         if ((x < Math.min(currSplitter - sbHPosition + RESIZE_FUDGE_FACTOR, size().width - verticalScrollbarWidth)) &&
  2488.                             (x > currSplitter - sbHPosition - RESIZE_FUDGE_FACTOR))
  2489.                         {
  2490.                             dragColumn = i;
  2491.                             isDragging = true;
  2492.                             offscreenImageGraphics.setClip(0, 0, Math.max(splitters[splitters.length-1], size().width+sbHPosition), size().height);
  2493.                             mouseMotion.mouseDragged(e); //draw drag line immediately
  2494.                             return;
  2495.                         }
  2496.                     }
  2497.                 }
  2498.  
  2499.                 if (allowSorting)
  2500.                 {
  2501.                     // check for sort button push
  2502.                     for (int i = 0; i < headings.length; i++)
  2503.                     {
  2504.                         //int max = i == headings.length - 1 ? size().width : splitters[i+1]-sbHPosition;
  2505.  
  2506.                         if ((x > splitters[i] - sbHPosition) && (x < splitters[i+1] - sbHPosition))
  2507.                         {
  2508.                            clickedInHeadings = true;
  2509.                            columnClicked = i;
  2510.                            drawHeading(true);
  2511.                            repaint();
  2512.                            return;
  2513.                         }
  2514.                     }
  2515.                 }
  2516.  
  2517.                 return;
  2518.             }
  2519.  
  2520.             //Check if the user is clicking in the corner box
  2521.             {
  2522.                 Dimension size = getSize();
  2523.                 if (x >= size.width - verticalScrollbarWidth || y >= size.height - scrollbarHeight)
  2524.                     return;
  2525.             }
  2526.  
  2527.             //Calc row clicked in
  2528.             int clickedInRow = ((int) (y - headingHeight) / cellHeight) + topRow;
  2529.  
  2530.             //Range check the row that was clicked in
  2531.             if (clickedInRow < 0 || clickedInRow > cells.rows() - 1)
  2532.                 return;
  2533.  
  2534.             //Save off the old selection
  2535.             int oldRowSelect = selectedRow;
  2536.  
  2537.             // set new row selection
  2538.             changeSelection(clickedInRow, e.getModifiers());
  2539.  
  2540.             // detect a double click
  2541.             long when = e.getWhen();
  2542.             if ((selectedRow == oldRowSelect) || (oldRowSelect < 0))
  2543.             {
  2544.                 // if clicked on same row track time elapsed since last mouseDown
  2545.                 long clickSpeed = when - clickTime;
  2546.  
  2547.                 if (clickSpeed < CLICKTHRESHOLD)
  2548.                     sourceActionEvent(selectedRow);
  2549.             }
  2550.  
  2551.             clickTime = when;
  2552.         }
  2553.  
  2554.         /**
  2555.          * Handles Mouse Released events
  2556.          * @param e the MouseEvent
  2557.          */
  2558.         public void mouseReleased(MouseEvent e)
  2559.         {
  2560.             int x = e.getX();
  2561.  
  2562.             if (isDragging)
  2563.             {
  2564.                 //fix any drag that went off the left side
  2565.                 if (x < 0)
  2566.                     x = 0;
  2567.  
  2568.                 //Adjust splitters to delta
  2569.                 boolean splittersChanged = false;
  2570.                 {
  2571.                     xDragLast = Math.max(splitters[dragColumn - 1] + minColumnWidth,x + sbHPosition);
  2572.  
  2573.                     int splitterDelta = xDragLast - splitters[dragColumn];
  2574.  
  2575.                     for (int i = dragColumn + 1;i < splitters.length;i++)
  2576.                     {
  2577.                         int splitterWas = splitters[i];
  2578.                         splitters[i] += splitterDelta;
  2579.                         if (splitterWas != splitters[i])
  2580.                             splittersChanged = true;
  2581.                     }
  2582.  
  2583.                     if (splitters[dragColumn] != xDragLast)
  2584.                     {
  2585.                         splitters[dragColumn] = xDragLast;
  2586.                         splittersChanged = true;
  2587.                     }
  2588.                 }
  2589.  
  2590.                 //Stop dragging
  2591.                 xDragLast = -1;
  2592.                 isDragging = false;
  2593.  
  2594.                 //???RKM??? Cursor should not be assumed as default
  2595.                 if (e.getY() > headingHeight)
  2596.                     setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  2597.  
  2598.                 //If the columnSizes were being calculated, switch to set column sizes
  2599.                 if (splittersChanged && columnSizes == null)
  2600.                 {
  2601.                     //Switch the column sizes - done this way so they could be vetoed
  2602.                     try
  2603.                     {
  2604.                         setColumnSizes(intArrayToStringArray(getColumnSizesFromSplitters()));
  2605.                     }
  2606.                     catch(PropertyVetoException veto)
  2607.                     {
  2608.                     }
  2609.                 }
  2610.  
  2611.                 triggerRedraw();
  2612.  
  2613.                 return;
  2614.             }
  2615.  
  2616.             if (columnClicked != -1)
  2617.             {
  2618.                 // Find sort for that column
  2619.                 CompareCells compareCellsFunc = getColumnSorter(columnClicked);
  2620.  
  2621.                 // it was a sorting request
  2622.                 compareCellsFunc.setCurrentBitSet(highlightedRows);
  2623.                 compareCellsFunc.setSelectedRow(selectedRow);
  2624.  
  2625.                 if (columnClicked == lastColumnClicked)
  2626.                     compareCellsFunc.reverse();
  2627.                 cells.sort(compareCellsFunc, columnClicked);
  2628.  
  2629.                 selectedRow = compareCellsFunc.getSelectedRow();
  2630.  
  2631.                 //Rember the last column click, so we can detect when to swap the sort order
  2632.                 lastColumnClicked = columnClicked;
  2633.  
  2634.                 columnClicked = -1;
  2635.  
  2636.                 triggerRedraw();
  2637.             }
  2638.         }
  2639.  
  2640.         public void mouseExited(MouseEvent e)
  2641.         {
  2642.             if ((getCursor().getType() != Cursor.DEFAULT_CURSOR) && (!isDragging))
  2643.                 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  2644.         }
  2645.     }
  2646.  
  2647.     /**
  2648.      * This is the MouseMotion Event handling innerclass.
  2649.      */
  2650.     class MouseMotion implements java.awt.event.MouseMotionListener, java.io.Serializable
  2651.     {
  2652.         /**
  2653.          * Handles Mouse Moved events
  2654.          * @param e the MouseEvent
  2655.          */
  2656.         public void mouseMoved(MouseEvent e)
  2657.         {
  2658.             if (allowResizingOfColumns)
  2659.             {
  2660.                 boolean isCloseEnough = false;
  2661.  
  2662.                 // Use resize cursor ?
  2663.                 if (e.getY() < headingHeight)
  2664.                 {
  2665.                     int x = e.getX();
  2666.  
  2667.                     // Moving mouse around in header area
  2668.                     // is it close enough to be a column size drag?
  2669.                     for (int i = 1;i < splitters.length - 1;i++)
  2670.                     {
  2671.                         int currSplitter = splitters[i];
  2672.                         if ((x < Math.min(currSplitter - sbHPosition + RESIZE_FUDGE_FACTOR, size().width - verticalScrollbarWidth)) &&
  2673.                             (x > currSplitter - sbHPosition - RESIZE_FUDGE_FACTOR))
  2674.                         {
  2675.                             isCloseEnough = true;
  2676.                             break;
  2677.                         }
  2678.                     }
  2679.                 }
  2680.  
  2681.                 int newCursor = (isCloseEnough ? Cursor.W_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR);
  2682.  
  2683.                 if (newCursor != getCursor().getType())
  2684.                     setCursor(new Cursor(newCursor));
  2685.             }
  2686.         }
  2687.  
  2688.         /**
  2689.          * Handles Mouse Dragged events
  2690.          * @param e the MouseEvent
  2691.          */
  2692.         public void mouseDragged(MouseEvent e)
  2693.         {
  2694.             int x = e.getX();
  2695.             int y = e.getY();
  2696.             Dimension s = size();
  2697.  
  2698.             if (!isDragging)
  2699.             {
  2700.                 if (clickedInHeadings)
  2701.                 {
  2702.                     // check for sort button push outside of button
  2703.                     int max;
  2704.                     int min;
  2705.  
  2706.                     if (columnClicked > -1)
  2707.                     {
  2708.                         if (columnClicked == 0)
  2709.                             min = 0;
  2710.                         else
  2711.                             min = splitters[columnClicked]-sbHPosition;
  2712.  
  2713.                         if (columnClicked == headings.length - 1)
  2714.                             max = s.width;
  2715.                         else
  2716.                             max = splitters[columnClicked+1]-sbHPosition;
  2717.  
  2718.                         if ((x < min) || (x > max) || (y > headingHeight) || (y < 0))
  2719.                         {
  2720.                             memoryClick = columnClicked;
  2721.                             columnClicked = -1;
  2722.                             drawHeading(false);
  2723.                             //???RKM??? Seems crazy to repaint entire component, just to redraw column heading rect
  2724.                             Graphics g = getGraphics();
  2725.                             paint(g);
  2726.                             //???RKM??? Get smarter about what is blit to the scene, this could be made nicer
  2727.                             //repaint();
  2728.                         }
  2729.                     }
  2730.                     else if (memoryClick > -1)
  2731.                     {
  2732.                         if (memoryClick == 0)
  2733.                             min = 0;
  2734.                         else
  2735.                             min = splitters[memoryClick]-sbHPosition;
  2736.  
  2737.                         if (memoryClick == headings.length - 1)
  2738.                             max = s.width;
  2739.                         else
  2740.                             max = splitters[memoryClick+1]-sbHPosition;
  2741.  
  2742.                         if ((x > min) && (x < max) && (y < headingHeight) && (y > 0))
  2743.                         {
  2744.                             columnClicked = memoryClick;
  2745.                             memoryClick = -1;
  2746.                             drawHeading(true);
  2747.                             //???RKM??? Seems crazy to repaint entire component, just to redraw column heading rect
  2748.                             Graphics g = getGraphics();
  2749.                             paint(g);
  2750.                             //???RKM??? Get smarter about what is blit to the scene, this could be made nicer
  2751.                             //repaint();
  2752.                         }
  2753.                     }
  2754.                 }
  2755.             }
  2756.             else
  2757.             {
  2758.                 //fix any drag that went off the left side
  2759.                 if (x < 0)
  2760.                     x = 0;
  2761.  
  2762.                 if (x + sbHPosition < splitters[dragColumn-1] + minColumnWidth)
  2763.                     return;
  2764.  
  2765.                 //erase prvious drag line and draw in new position
  2766.                 int x1 = Math.min(x, xDragLast-sbHPosition);
  2767.                 int x2 = Math.max(x, xDragLast-sbHPosition);
  2768.  
  2769.                 offscreenImageGraphics.setColor(colorBg);
  2770.                 offscreenImageGraphics.setXORMode(Color.gray);
  2771.                 offscreenImageGraphics.drawLine(xDragLast,0,xDragLast,s.height);
  2772.                 offscreenImageGraphics.drawLine(x+sbHPosition, 0, x+sbHPosition, s.height);
  2773.                 offscreenImageGraphics.setColor(getForeground());
  2774.                 offscreenImageGraphics.setPaintMode();
  2775.  
  2776.                 //save x position of drag line
  2777.                 xDragLast = x + sbHPosition;
  2778.                 repaint(x1, 0, x2-x1+1, size().height);
  2779.             }
  2780.         }
  2781.     }
  2782.  
  2783.     /**
  2784.      * This is the Key Event handling innerclass.
  2785.      */
  2786.     class Key extends java.awt.event.KeyAdapter implements java.io.Serializable
  2787.     {
  2788.         /**
  2789.          * Handles Key Pressed events
  2790.          * @param e the KeyEvent
  2791.          */
  2792.         public void keyPressed(KeyEvent e)
  2793.         {
  2794.             int modifiers = e.getModifiers();
  2795.  
  2796.             switch (e.getKeyCode())
  2797.             {
  2798.                 case KeyEvent.VK_DOWN:
  2799.                     if (selectedRow < cells.rows() - 1)
  2800.                         changeSelection(selectedRow + 1, modifiers);
  2801.                     break;
  2802.  
  2803.                 case KeyEvent.VK_PAGE_DOWN:
  2804.                     changeSelection(Math.min(selectedRow + getNumVisibleRows(), cells.rows()-1), modifiers);
  2805.                     break;
  2806.  
  2807.                 case KeyEvent.VK_UP:
  2808.                     if (selectedRow > 0)
  2809.                         changeSelection(selectedRow - 1, modifiers);
  2810.                     break;
  2811.  
  2812.                 case KeyEvent.VK_PAGE_UP:
  2813.                     changeSelection(Math.max(selectedRow - getNumVisibleRows(), 0), modifiers);
  2814.                     break;
  2815.  
  2816.                 case KeyEvent.VK_RIGHT:
  2817.                 {
  2818.                     int max = horizontalScrollbar.getMaximum()-(isSun1_1?size().width-verticalScrollbarWidth:0);
  2819.                     if (sbHShow && sbHPosition < max)
  2820.                     {
  2821.                         horizontalScrollbar.setValue(Math.min(sbHPosition += hScrollbarLineIncrement, max));
  2822.                         repaint();
  2823.                     }
  2824.                     break;
  2825.                 }
  2826.  
  2827.                 case KeyEvent.VK_LEFT:
  2828.                 {
  2829.                     if (sbHPosition > 0)
  2830.                     {
  2831.                         horizontalScrollbar.setValue(Math.max(sbHPosition-=hScrollbarLineIncrement, 0));
  2832.                         repaint();
  2833.                     }
  2834.                     break;
  2835.                 }
  2836.  
  2837.                 case KeyEvent.VK_ENTER:
  2838.                     if (selectedRow > -1)
  2839.                     {
  2840.                         sourceActionEvent(selectedRow);
  2841.                     }
  2842.                     break;
  2843.             }
  2844.         }
  2845.     }
  2846.  
  2847.  
  2848.     /**
  2849.      * This is the Adjustment Event handling innerclass.
  2850.      */
  2851.     class Adjustment implements java.awt.event.AdjustmentListener, java.io.Serializable
  2852.     {
  2853.         /**
  2854.          * Handles Scrollbar events
  2855.          * @param e the AdjustmentEvent
  2856.          */
  2857.         public void adjustmentValueChanged(AdjustmentEvent e)
  2858.         {
  2859.             if (e.getSource() == verticalScrollbar)
  2860.             {
  2861.                 if (topRow != verticalScrollbar.getValue())
  2862.                 {
  2863.                     topRow = verticalScrollbar.getValue();
  2864.                     sbVPosition = topRow;
  2865.  
  2866.                     triggerRedraw();
  2867.                 }
  2868.             }
  2869.             else if (e.getSource() == horizontalScrollbar)
  2870.             {
  2871.                 if (sbHPosition != horizontalScrollbar.getValue())
  2872.                 {
  2873.                     sbHPosition = horizontalScrollbar.getValue();
  2874.  
  2875.                     triggerRedraw();
  2876.                 }
  2877.             }
  2878.         }
  2879.     }
  2880.  
  2881.     /**
  2882.      * Fires an item event to the listeners.
  2883.      * @param eventType value indicating the kind of state change, one of:
  2884.      * ItemEvent.SELECTED or ItemEvent.DESELECTED
  2885.      * @see java.awt.event.ItemEvent#SELECTED
  2886.      * @see java.awt.event.ItemEvent#DESELECTED
  2887.      * @see #addItemListener
  2888.      * @see #removeItemListener
  2889.      */
  2890.     protected void sourceItemEvent(int eventType)
  2891.     {
  2892.         if (itemListener != null)
  2893.             itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this, eventType));
  2894.     }
  2895.  
  2896.     protected void repaintFocus()
  2897.     {
  2898.         if (selectedRow != -1)
  2899.             drawRows(selectedRow, 1, true);
  2900.         paintSelection(getSelectedRows());
  2901.     }
  2902.  
  2903.     class Focus implements FocusListener, java.io.Serializable
  2904.     {
  2905.         public void focusGained(FocusEvent e)
  2906.         {
  2907.             hasFocus = true;
  2908.             repaintFocus();
  2909.         }
  2910.  
  2911.         public void focusLost(FocusEvent e)
  2912.         {
  2913.             hasFocus = false;
  2914.             repaintFocus();
  2915.         }
  2916.     }
  2917.  
  2918.     /**
  2919.      * Fires an action event to the listeners.
  2920.      * The action command sent is the value specified in <code>actionCommand</code> concatenated
  2921.      * with the selectedRow. By default the <code>actionCommand</code> is "RowSelected:", so a
  2922.      * typical action command sent might be "RowSelected:0".
  2923.      * @param selectedRow the zero-relative index of the row generating the event
  2924.      * @see #actionCommand
  2925.      */
  2926.     protected void sourceActionEvent(int selectedRow)
  2927.     {
  2928.         //Sending the selected row along with the command name, for convenience.
  2929.         if (actionListener != null)
  2930.             actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand + selectedRow));
  2931.     }
  2932.  
  2933.     /**
  2934.      * Returns the number of visible rows that can fit in the MulitList.
  2935.      * Note: This could be more than the number of rows in the MultiList.
  2936.      */
  2937.     protected int getNumVisibleRows()
  2938.     {
  2939.         return ((int)((size().height - headingHeight - scrollbarHeight - BORDER - (headingHeight > 0 ? 0 : BORDER)) / cellHeight));
  2940.     }
  2941.  
  2942.     /**
  2943.      * Draws the specifed rows into an offscreen image.
  2944.      * @param cellRow the zero-relative index of the first row to draw
  2945.      * @param the number of rows to draw
  2946.      * @param toRepaint if true, calls the <code>repaint</code> method to update the screen
  2947.      */
  2948.     protected void drawRows(int cellRow,int numRowsToDraw,boolean toRepaint)
  2949.     {
  2950.         //Calc index of row in visible area (ie first row on screen is zero - could be row twenty)
  2951.         int visibleRowIndex = cellRow - sbVPosition;
  2952.  
  2953.         //Range check visibleRowIndex
  2954.         if (visibleRowIndex < 0 || visibleRowIndex > getNumVisibleRows() - 1)
  2955.             return;
  2956.  
  2957.         MatrixEnumeration e = cells.elements();
  2958.         Cell c = null;
  2959.  
  2960.         if (cellRow > 0)
  2961.             c  = (Cell)e.advanceTo(cellRow);
  2962.  
  2963.         //iterate the rows and paint each one
  2964.         while (e.hasMoreElements() || c != null)
  2965.         {
  2966.             int x = 1;
  2967.  
  2968.             //Calc if rowIsSelected, once, outside the loop
  2969.             boolean rowIsSelected = highlightedRows.get(cellRow);
  2970.  
  2971.             //Calc topOfCell, onc, outside of loop
  2972.             int topOfCell = headingHeight + (headingHeight > 0 ? 0 : BORDER) + (visibleRowIndex * cellHeight);
  2973.  
  2974.             //Iterate the cols of the current row
  2975.                int cols = headings.length;
  2976.             for (int i = 0;i < cols;i++)
  2977.             {
  2978.                 int w = splitters[i+1] - splitters[i];
  2979.  
  2980.                 if(offscreenImage != null)
  2981.                 {
  2982.                     offscreenImageGraphics.setColor(rowIsSelected ? colorHBg : colorBg);
  2983.                     offscreenImageGraphics.fillRect(x, topOfCell, w + 4, cellHeight);
  2984.                     offscreenImageGraphics.setColor(rowIsSelected?colorHFg :colorFg);
  2985.                 }
  2986.  
  2987.                 if (c == null)
  2988.                     c = (Cell)e.nextElement();
  2989.  
  2990.                 if (offscreenImage != null && (c != null) && (e.currRow() == cellRow) && (e.currCol() == i))
  2991.                 {
  2992.                     c.drawCell(offscreenImageGraphics, getColumnAlignment(i), splitters[i]+3, topOfCell, w, cellHeight, cellAscent);
  2993.                     c = null;
  2994.                 }
  2995.  
  2996.                 if (c != null && e.currRow() < cellRow)
  2997.                     c = null;
  2998.  
  2999.                 x = splitters[i+1]-3;
  3000.             }
  3001.  
  3002.             if (hasFocus & focusIndicatedVisually)
  3003.             {
  3004.                 if (offscreenImage != null && cellRow == selectedRow)
  3005.                 {
  3006.                     Rectangle r1 = offscreenImageGraphics.getClipRect();
  3007.  
  3008.                     if(r1 != null)
  3009.                     {
  3010.                         offscreenImageGraphics.setColor(colorBg);
  3011.                         offscreenImageGraphics.setXORMode(Color.gray);
  3012.                         offscreenImageGraphics.drawRect(1, topOfCell, r1.width-3, cellHeight-1);
  3013.                         offscreenImageGraphics.setPaintMode();
  3014.                     }
  3015.                 }
  3016.             }
  3017.  
  3018.             if (offscreenImage != null && toRepaint)
  3019.             {
  3020.                 Rectangle r1 = offscreenImageGraphics.getClipRect();
  3021.  
  3022.                 if(r1 != null)
  3023.                 {
  3024.                     repaint(0, topOfCell, r1.width, cellHeight);
  3025.                 }
  3026.             }
  3027.  
  3028.             if (--numRowsToDraw == 0)
  3029.                 break;
  3030.  
  3031.             cellRow++;
  3032.  
  3033.             visibleRowIndex++;
  3034.         }
  3035.     }
  3036.  
  3037.     /**
  3038.      * Adds a listener for all property change events.
  3039.      * @param listener the listener to add
  3040.      * @see #removePropertyChangeListener
  3041.      */
  3042.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
  3043.     {
  3044.         changes.addPropertyChangeListener(listener);
  3045.     }
  3046.  
  3047.     /**
  3048.      * Removes a listener for all property change events.
  3049.      * @param listener the listener to remove
  3050.      * @see #addPropertyChangeListener
  3051.      */
  3052.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
  3053.     {
  3054.         changes.removePropertyChangeListener(listener);
  3055.     }
  3056.  
  3057.     /**
  3058.      * Adds a listener for all vetoable property change events.
  3059.      * @param listener the listener to add
  3060.      * @see #removeVetoableChangeListener
  3061.      */
  3062.     public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
  3063.     {
  3064.         vetos.addVetoableChangeListener(listener);
  3065.     }
  3066.  
  3067.     /**
  3068.      * Removes a listener for all vetoable property change events.
  3069.      * @param listener the listener to remove
  3070.      * @see #addVetoableChangeListener
  3071.      */
  3072.     public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
  3073.     {
  3074.         vetos.removeVetoableChangeListener(listener);
  3075.     }
  3076.  
  3077.     /**
  3078.      * Takes no action.
  3079.      * This is a standard Java AWT method which gets called to specify
  3080.      * which layout manager should be used to layout the components in
  3081.      * standard containers.
  3082.      *
  3083.      * Since layout managers CANNOT BE USED with this container the standard
  3084.      * setLayout has been OVERRIDDEN for this container and does nothing.
  3085.      *
  3086.      * @param l the layout manager to use to layout this container's components
  3087.      * (IGNORED)
  3088.      * @see java.awt.Container#getLayout
  3089.      **/
  3090.     public void setLayout(LayoutManager mgr) {
  3091.     }
  3092.  
  3093.     public boolean isFocusTraversable()
  3094.     {
  3095.         return true;
  3096.     }
  3097.  
  3098.     static Color thirtyThreePercentGray = new Color(0x212121);
  3099.     static Color sixtySixPercentGray = new Color(0x434343);
  3100.  
  3101.     /**
  3102.      * Draws the specified column heading into an offscreen image.
  3103.      * @param columnHeadingRect the heading bounds
  3104.      * @param isSelected true if the column heading is selected
  3105.      */
  3106.     protected void drawColumnHeading
  3107.             (Rectangle    columnHeadingRect,
  3108.              boolean    isSelected)
  3109.     {
  3110.         if (symantec.itools.lang.OS.isMacintosh())
  3111.         {
  3112.             //Frame the column heading
  3113.             offscreenImageGraphics.setColor(Color.black);
  3114.             offscreenImageGraphics.drawRect(columnHeadingRect.x, columnHeadingRect.y, columnHeadingRect.width - 1, columnHeadingRect.height - 1);
  3115.  
  3116.             //Draw the gray in it
  3117.             offscreenImageGraphics.setColor(isSelected ? Color.gray : headingBg);
  3118.             offscreenImageGraphics.fillRect
  3119.                 (columnHeadingRect.x + 2,
  3120.                  columnHeadingRect.y + 2,
  3121.                  columnHeadingRect.width - 4,
  3122.                  columnHeadingRect.height - 4);
  3123.  
  3124.             if (isSelected)
  3125.                  offscreenImageGraphics.setColor(Color.black);
  3126.  
  3127.             //Draw dot in lower left hand corner
  3128.             offscreenImageGraphics.drawLine
  3129.                 (columnHeadingRect.x + 1,
  3130.                  columnHeadingRect.y + columnHeadingRect.height - 2,
  3131.                  columnHeadingRect.x + 1,
  3132.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3133.  
  3134.             //Draw dot in upper right hand corner
  3135.             offscreenImageGraphics.drawLine
  3136.                 (columnHeadingRect.x + columnHeadingRect.width - 2,
  3137.                  columnHeadingRect.y + 1,
  3138.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3139.                  columnHeadingRect.y + 1);
  3140.  
  3141.             //Draw white left side vertical line (upper hilight)
  3142.             offscreenImageGraphics.setColor(isSelected ? thirtyThreePercentGray: Color.white);
  3143.             offscreenImageGraphics.drawLine
  3144.                 (columnHeadingRect.x + 1,
  3145.                  columnHeadingRect.y + 1,
  3146.                  columnHeadingRect.x + 1,
  3147.                  columnHeadingRect.y + columnHeadingRect.height - 3);
  3148.  
  3149.             //Draw white top side horrizontal line (upper hilight)
  3150.             offscreenImageGraphics.drawLine
  3151.                 (columnHeadingRect.x + 2,
  3152.                  columnHeadingRect.y + 1,
  3153.                  columnHeadingRect.x + columnHeadingRect.width - 3,
  3154.                  columnHeadingRect.y + 1);
  3155.  
  3156.             //Draw shadow
  3157.             offscreenImageGraphics.setColor(isSelected ? sixtySixPercentGray: Color.gray);
  3158.  
  3159.             //Draw horizontal shadow
  3160.             offscreenImageGraphics.drawLine
  3161.                 (columnHeadingRect.x + 2,
  3162.                  columnHeadingRect.y + columnHeadingRect.height - 2,
  3163.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3164.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3165.  
  3166.             //Draw vertical shadow
  3167.             offscreenImageGraphics.drawLine
  3168.                 (columnHeadingRect.x + columnHeadingRect.width - 2,
  3169.                  columnHeadingRect.y + 2,
  3170.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3171.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3172.         }
  3173.         else
  3174.         {
  3175.             columnHeadingRect.x += 1;
  3176.             columnHeadingRect.y += 1;
  3177.             columnHeadingRect.width -= 2;
  3178.             columnHeadingRect.height -= 2;
  3179.  
  3180.             draw3DBox(columnHeadingRect, !isSelected);
  3181.  
  3182.             offscreenImageGraphics.setColor(headingBg);
  3183.             offscreenImageGraphics.fillRect(columnHeadingRect.x+1, columnHeadingRect.y+1, columnHeadingRect.width-1, columnHeadingRect.height-1);
  3184.         }
  3185.     }
  3186.  
  3187.     //???RKM??? Can't we call draw3DRect here???
  3188.     /**
  3189.      * Draws a rectangle with 3-dimensional effect into the offscreen image.
  3190.      * @param r the rectangle bounds
  3191.      * @param up true to draw as if raised, false to draw as if lowered
  3192.      */
  3193.     protected void draw3DBox(Rectangle r, boolean up)
  3194.     {
  3195.         int x0 = r.x;
  3196.         int y0 = r.y;
  3197.         int x1 = r.x + r.width;
  3198.         int y1 = r.y + r.height;
  3199.  
  3200.         offscreenImageGraphics.setColor(up?Color.black: Color.white);
  3201.         offscreenImageGraphics.drawLine(x1,   y0, x1,   y1); //right
  3202.         offscreenImageGraphics.drawLine(x1+1, y0, x1+1, y1); //right
  3203.  
  3204.         offscreenImageGraphics.drawLine(x0,y1,x1,y1);        //bottom
  3205.         offscreenImageGraphics.drawLine(x0,y1+1, x1, y1+1);  //bottom
  3206.  
  3207.         offscreenImageGraphics.setColor(up?Color.white:Color.gray);
  3208.         offscreenImageGraphics.drawLine(x0,y0,x1-2,y0);     //top
  3209.         offscreenImageGraphics.drawLine(x0,y0,x0,y1-1);     //left
  3210.     }
  3211.  
  3212.     /**
  3213.      * Calculates the height of the column headings and stores that in
  3214.      * headingHeight.
  3215.      */
  3216.     protected void calculateHeadingHeight()
  3217.     {
  3218.         FontMetrics fontMetrics = getFontMetrics(headingFont);
  3219.         headingHeight = fontMetrics.getHeight() + fontMetrics.getLeading() + 7;
  3220.     }
  3221.  
  3222.     /**
  3223.      * Draws all column headings into an offscreen image.
  3224.      * @param down true if mouse is down in heading
  3225.      */
  3226.     protected void drawHeading(boolean down)
  3227.     {
  3228.         //If we aren't drawing headings, get out of here
  3229.         if (!headingVisible)
  3230.             return;
  3231.  
  3232.         //If we have any headers
  3233.         if (headings.length > 0)
  3234.         {
  3235.             //Save font we entered this routine with
  3236.             Font saveFont = offscreenImageGraphics.getFont();
  3237.  
  3238.             //Set font to heading font and get font metric for it (we'll need it later)
  3239.             offscreenImageGraphics.setFont(headingFont);
  3240.             FontMetrics fontMetrics = offscreenImageGraphics.getFontMetrics();
  3241.  
  3242.             //Init currSplitter to 0th elem, avoid two array indexes per loop
  3243.             int currSplitter = splitters[0];
  3244.  
  3245.             //Loop though all of the headings
  3246.             for (int i = 0; i < headings.length; i++)
  3247.             {
  3248.                 int nextSplitter = splitters[i+1];
  3249.  
  3250.                 int currHeadingWidth = nextSplitter - currSplitter;
  3251.  
  3252.                 //Draw column heading
  3253.                 Rectangle columnHeaderRect =
  3254.                     new Rectangle(currSplitter,0,currHeadingWidth,headingHeight);
  3255.  
  3256.                 boolean clickingInCurrColumn = (columnClicked == i) ? true : false;
  3257.  
  3258.                 drawColumnHeading(columnHeaderRect, clickingInCurrColumn);
  3259.  
  3260.                 String currHeading = headings[i];
  3261.                 if (currHeading == null)
  3262.                     continue;
  3263.  
  3264.                 offscreenImageGraphics.setColor(headingFg);
  3265.  
  3266.                 int stringWidth = fontMetrics.stringWidth(currHeading);
  3267.                 int w = currHeadingWidth - 3;
  3268.                 int shift = down && clickingInCurrColumn ? 1 : 0;
  3269.  
  3270.                 int stringBaseline = headingHeight - 6 + shift;
  3271.  
  3272.                 //Save clip and clip to heading rect
  3273.                 Shape saveClip = offscreenImageGraphics.getClip();
  3274.                 offscreenImageGraphics.setClip(columnHeaderRect.x,columnHeaderRect.y,columnHeaderRect.width,columnHeaderRect.height);
  3275.  
  3276.                 switch (getColumnAlignment(i))
  3277.                 {
  3278.                     case MultiList.LEFT:
  3279.                     {
  3280.                         offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3281.                         break;
  3282.                     }
  3283.  
  3284.                     case MultiList.CENTER:
  3285.                     {
  3286.                         if (stringWidth > w)
  3287.                             offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3288.                         else
  3289.                             offscreenImageGraphics.drawString(currHeading, currSplitter + (w-stringWidth)/2+shift, stringBaseline);
  3290.  
  3291.                         break;
  3292.                     }
  3293.  
  3294.                     case MultiList.RIGHT:
  3295.                     {
  3296.                         if (stringWidth > w)
  3297.                             offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3298.                         else
  3299.                             offscreenImageGraphics.drawString(currHeading, currSplitter+w-stringWidth-6+shift, stringBaseline);
  3300.  
  3301.                         break;
  3302.                     }
  3303.                 }//switch
  3304.  
  3305.                 //Restore clip
  3306.                 offscreenImageGraphics.setClip(saveClip);
  3307.  
  3308.                 if (clickingInCurrColumn)
  3309.                     offscreenImageGraphics.drawLine(columnHeaderRect.x+1, headingHeight - 1, nextSplitter - 3,  headingHeight - 1);
  3310.  
  3311.                 //Set currSplitter to nextSplitter (loop iterations - one array index per loop)
  3312.                 currSplitter = nextSplitter - 1;
  3313.             }
  3314.  
  3315.             //Restore font
  3316.             offscreenImageGraphics.setFont(saveFont);
  3317.         }
  3318.  
  3319.         //If the column headers do not fill up the width, add column
  3320.         {
  3321.             Dimension dim = size();
  3322.  
  3323.             int lastSplitter = splitters[splitters.length - 1];
  3324.  
  3325.             if (lastSplitter < sbHPosition + dim.width - verticalScrollbarWidth)
  3326.             {
  3327.                 Rectangle fillerHeaderRect =
  3328.                     new Rectangle(lastSplitter - 1,0,sbHPosition + dim.width - verticalScrollbarWidth - lastSplitter + 1, headingHeight);
  3329.                 drawColumnHeading(fillerHeaderRect, false);
  3330.             }
  3331.         }
  3332.     }
  3333.  
  3334.     /**
  3335.      * Converts the given array of int into a corresponding array of String.
  3336.      * @param intArray the array of ints
  3337.      * @return an array of String
  3338.      */
  3339.     protected String[] intArrayToStringArray(int[] intArray)
  3340.     {
  3341.         if (intArray == null)
  3342.             return null;
  3343.  
  3344.         String[] list = new String[intArray.length];
  3345.  
  3346.         for (int i = 0;i < intArray.length;i++)
  3347.         {
  3348.             String intString = "";
  3349.             try
  3350.             {
  3351.                 intString = String.valueOf(intArray[i]);
  3352.             }
  3353.             catch (Exception e)
  3354.             {
  3355.             }
  3356.             list[i] = intString;
  3357.         }
  3358.  
  3359.         return list;
  3360.     }
  3361.  
  3362.     /**
  3363.      * Returns an array of column sizes as determined from the current splitter information.
  3364.      */
  3365.     protected int[] getColumnSizesFromSplitters()
  3366.     {
  3367.         int[] splitterColumnSizes = new int[headings.length];
  3368.  
  3369.         for (int i = 0;i < headings.length;i++)
  3370.             splitterColumnSizes[i] = splitters[i + 1] - splitters[i];
  3371.  
  3372.         return splitterColumnSizes;
  3373.     }
  3374.  
  3375.     /**
  3376.      * Triggers an entire redraw of this component, unless redrawing is
  3377.      * currently suppressed.
  3378.      * @see #setSupressRedraw
  3379.      */
  3380.     protected void triggerRedraw()
  3381.     {
  3382.         //If we're not suppressing redraws
  3383.         if (!isSuppressRedraw)
  3384.         {
  3385.             //Set forceRedraw so contents of offscreen is updated
  3386.             forceRedraw = true;
  3387.             repaint();
  3388.         }
  3389.         else redrawWasSupressed = true;
  3390.     }
  3391.  
  3392.     /**
  3393.      * Handles cases where the user might pass either an array of items,
  3394.      * or a single string with itemds separated by the ';' character.
  3395.      * This may occur in the <code>setHeadings</code>,
  3396.      * <code>setColumnAlignments</code>, or <code>setColumnSizes</code>
  3397.      * methods.
  3398.      * @param list the input list
  3399.      * @param an array of items, one per String
  3400.      * @see #setHeadings
  3401.      * @see #setColumnAlignments
  3402.      * @see #setColumnSizes
  3403.      */
  3404.     protected String[] tokenizeStringArrayIfNeeded(String[] list)
  3405.     {
  3406.         if (list != null)
  3407.         {
  3408.             //Handle case where user thinks ; separates entries
  3409.             if (list.length == 1 && list[0].indexOf(";") != -1)
  3410.             {
  3411.                 //Convert the first element into an array of strings
  3412.                 java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(list[0],";");
  3413.  
  3414.                 int numColumns = tokenizer.countTokens();
  3415.  
  3416.                 String[] convertedList = new String[numColumns];
  3417.  
  3418.                 int currCol = 0;
  3419.                 while (tokenizer.hasMoreTokens())
  3420.                 {
  3421.                     String columnHeader = tokenizer.nextToken();
  3422.  
  3423.                     convertedList[currCol] = columnHeader;
  3424.  
  3425.                     currCol++;
  3426.                 }
  3427.  
  3428.                 list = convertedList;
  3429.             }
  3430.         }
  3431.  
  3432.         return list;
  3433.     }
  3434.  
  3435.     /**
  3436.      * Initializes new headings with the given list of heading titles.
  3437.      * @param list array of heading title strings
  3438.      */
  3439.     protected void calcHeadings(String[] list)
  3440.     {
  3441.         //    RKM    In order to reduce chance of a bug injection, I've allowed the calculation below to
  3442.         //        occur, even in the case where the result is bogus
  3443.  
  3444.         headings = new String[list.length];
  3445.         splitters = new int[list.length + 1];
  3446.  
  3447.         //???RKM??? So what do we do if columnAlignments is not the same width
  3448.  
  3449.         for (int i = 0; i < list.length; ++i)
  3450.         {
  3451.             try
  3452.             {
  3453.                 setHeading(list[i], i);
  3454.             }
  3455.             catch (PropertyVetoException e){}
  3456.         }
  3457.  
  3458.         //If there were column sizes, reapply them now
  3459.         if (columnSizes != null)
  3460.         {
  3461.             try
  3462.             {
  3463.                 setColumnSizes(intArrayToStringArray(columnSizes));
  3464.             }
  3465.             catch(PropertyVetoException veto)
  3466.             {
  3467.             }
  3468.         }
  3469.     }
  3470.  
  3471.     /**
  3472.      * The background color of cell text.
  3473.      * @see #getCellBg
  3474.      * @see #setCellBg
  3475.      */
  3476.     protected Color        colorBg = Color.white;
  3477.     /**
  3478.      * The foreground color of cell text.
  3479.      * @see #getCellFg
  3480.      * @see #setCellFg
  3481.      */
  3482.     protected Color        colorFg = Color.black;
  3483.     //???RKM??? Remove if when MRJ fixes it's bugs
  3484.     /**
  3485.      * The background color of hilighted cell text.
  3486.      * Automatically determined.
  3487.      */
  3488.     protected Color        colorHBg = symantec.itools.lang.OS.isMacintosh() ? new Color(0,0,128) : SystemColor.textHighlight;
  3489.     /**
  3490.      * The foreground color of hilighted cell text.
  3491.      * Automatically determined.
  3492.      */
  3493.     protected Color        colorHFg = symantec.itools.lang.OS.isMacintosh() ? Color.white : SystemColor.textHighlightText;
  3494.     /**
  3495.      * The background color of column heading text.
  3496.      * @see #getHeadingBg
  3497.      * @see #setHeadingBg
  3498.      */
  3499.     protected Color        headingBg = Color.lightGray;
  3500.     /**
  3501.      * The foreground color of column heading text.
  3502.      * @see #getHeadingFg
  3503.      * @see #setHeadingFg
  3504.      */
  3505.     protected Color        headingFg = Color.black;
  3506.     /**
  3507.      * Allow sorting. Default is true.
  3508.      */
  3509.     protected boolean        allowSorting = true;
  3510.     /**
  3511.      * Indicate focus when drawing. Default is true.
  3512.      */
  3513.     protected boolean        focusIndicatedVisually = true;
  3514.     /**
  3515.      * Allow multiple selections. Default is false.
  3516.      */
  3517.     protected boolean        multiSelect = false;
  3518.     /**
  3519.      * Allow columns to be resized. Default is true.
  3520.      */
  3521.     protected boolean        allowResizingOfColumns = true;
  3522.     /* *
  3523.      * Checks for scrollbars that have different max value Members used to
  3524.      * calculate headings, when size of component is not known (pre add).
  3525.      */
  3526.  
  3527.     /**
  3528.      * The heading titles.
  3529.      * @see #getHeadings
  3530.      * @see #setHeadings
  3531.      * @see #getHeading
  3532.      * @see #setHeading
  3533.      */
  3534.     protected String        headings[];
  3535.     /**
  3536.      * Is the heading visible. Default is true.
  3537.      */
  3538.     protected boolean        headingVisible = true;
  3539.     /**
  3540.      * The heading height in pixels.
  3541.      * If the heading is showing, it includes one pixel for the border (they overlap).
  3542.      */
  3543.     protected int            headingHeight = 0;
  3544.     /**
  3545.      * The font used to draw heading text.
  3546.      * @see #getHeadingFont
  3547.      * @see #setHeadingFont
  3548.      */
  3549.     protected Font            headingFont;
  3550.  
  3551.     /**
  3552.      * The location of the boundries between columns, in pixels.
  3553.      */
  3554.     protected int            splitters[];
  3555.  
  3556.     /**
  3557.      * The column alignments.
  3558.      * Values are one of: LEFT, CENTER, or RIGHT.
  3559.      * @see #LEFT
  3560.      * @see #CENTER
  3561.      * @see #RIGHT
  3562.      * @see #setColumnAlignments
  3563.      */
  3564.     protected int            columnAlignments[];
  3565.     /**
  3566.      * The column alignment to use by default.
  3567.      * The initial value is LEFT.
  3568.      * @see #LEFT
  3569.      * @see #CENTER
  3570.      * @see #RIGHT
  3571.      * @see #getDefaultColumnAlignment
  3572.      * @see #setDefaultColumnAlignment
  3573.      */
  3574.     protected int            defaultColumnAlignment = LEFT;
  3575.  
  3576.     /**
  3577.      * If non-null, the the width of each column in pixels.
  3578.      * If null, the columns are automatically sized.
  3579.      * @see #getColumnSizes
  3580.      * @see #setColumnSizes
  3581.      */
  3582.     protected int            columnSizes[] = null;
  3583.  
  3584.     /**
  3585.      * If sorting is allowed, the column clicked in.
  3586.      * @see #isAllowSorting
  3587.      * @see #setAllowSorting
  3588.      */
  3589.     protected int            columnClicked = -1;
  3590.     /**
  3591.      * If sorting is allowed, the column previously clicked in.
  3592.      * @see #isAllowSorting
  3593.      * @see #setAllowSorting
  3594.      */
  3595.     protected int            lastColumnClicked = -1;
  3596.     /**
  3597.      * Temp storage for columnClicked when repainting during a mouse drag.
  3598.      */
  3599.     protected int            memoryClick = -1;
  3600.  
  3601.     //???RKM??? What if multiSelect is true ???
  3602.     /**
  3603.      * The most recently selected row.
  3604.      */
  3605.     protected int            selectedRow = -1;
  3606.     /**
  3607.      * Flags indicating which rows are currently selected.
  3608.      */
  3609.     protected BitSet        highlightedRows = new BitSet();
  3610.  
  3611.     /**
  3612.      * The cells.
  3613.      */
  3614.     protected Matrix        cells = new Matrix();
  3615.     /**
  3616.      * The font used to display cell text.
  3617.      * @see #getCellFont
  3618.      * @see #setCellFont
  3619.      */
  3620.     protected Font            cellFont;
  3621.     /**
  3622.      * The height of cells, in pixels.
  3623.      */
  3624.     protected int            cellHeight = 0;
  3625.     /**
  3626.      * The ascent metric of the cell font.
  3627.      */
  3628.     protected int            cellAscent = 0;
  3629.     /**
  3630.      * The descent metric of the cell font.
  3631.      */
  3632.     protected int            cellDescent = 0;
  3633.  
  3634.     /**
  3635.      * The zero-relative index of the first visible row in the list.
  3636.      */
  3637.     protected int            topRow;
  3638.  
  3639.     /**
  3640.      * The latest vertical size of this component.
  3641.      */
  3642.     protected int            cachedHeight = -1;
  3643.     /**
  3644.      * The latest horizontal size of this component.
  3645.      */
  3646.     protected int            cachedWidth = -1;
  3647.     /**
  3648.      * The latest overall row width.
  3649.      */
  3650.     protected int            cachedLastSplitter = -1;
  3651.  
  3652.     /**
  3653.      * The zero-relative index of the column being resized via the UI.
  3654.      */
  3655.     protected int            dragColumn = -1;
  3656.     /**
  3657.      * The latest horizontal position the the splitter being dragged to resize a column.
  3658.      */
  3659.     protected int            xDragLast = -1;
  3660.     /**
  3661.      * The vertical scrollbar value.
  3662.      */
  3663.     protected int            sbVPosition = 0;
  3664.     /**
  3665.      * The horizontal scrollbar value.
  3666.      */
  3667.     protected int            sbHPosition = 0;
  3668.     /**
  3669.      * The width of the vertical scrollbar. 0 if it is not displayed.
  3670.      */
  3671.     protected int            verticalScrollbarWidth = 0;
  3672.     /**
  3673.      * The height of the horizontal scrollbar. 0 if it is not displayed.
  3674.      */
  3675.     protected int            scrollbarHeight = 0;
  3676.     /**
  3677.      * The unit increment of the horizontal scrollbar.
  3678.      * The default value is 4.
  3679.      */
  3680.     protected int            hScrollbarLineIncrement = 4;
  3681.     /**
  3682.      * The minimum allowed column width, in pixels.
  3683.      */
  3684.     protected int            minColumnWidth = 10;
  3685.     /**
  3686.      * The time of the previous click.
  3687.      * Used to detect when a row is double-clicked.
  3688.      */
  3689.     transient protected long        clickTime = 0;
  3690.     /**
  3691.      * The first part of the action command.
  3692.      * It is sent when a row is double-clicked, or enter is pressed.
  3693.      * @see #sourceActionEvent
  3694.      */
  3695.     protected String        actionCommand = "RowSelected:";
  3696.     /**
  3697.      * The vertical scrollbar.
  3698.      * Always exists. Shown as needed.
  3699.      */
  3700.     protected Scrollbar    verticalScrollbar;
  3701.     /**
  3702.      * The horizontal scrollbar.
  3703.      * Always exists. Shown as needed.
  3704.      */
  3705.     protected Scrollbar    horizontalScrollbar;
  3706.  
  3707.     //Sorting variables
  3708.     /**
  3709.      * The default cell comparer.
  3710.      * @see #getDefaultColumnSorter
  3711.      * @see #setDefaultColumnSorter
  3712.      */
  3713.     protected CompareCells defaultColumnSorter            = new CompareTextAndImageCells();
  3714.     /**
  3715.      * The cell compare routines to use for each column.
  3716.      * The routines are indexed using an Integer of the column index.
  3717.      * @see #getColumnSorter
  3718.      * @see #setColumnSorter
  3719.      */
  3720.     protected Hashtable columnCompareCellsRoutines        = new Hashtable();
  3721.  
  3722.     //Event sourcers
  3723.     /**
  3724.      * The action listener to keep track of listeners for our action event.
  3725.      */
  3726.     protected ActionListener    actionListener    = null;
  3727.     /**
  3728.      * The action listener to keep track of listeners for our item event.
  3729.      */
  3730.     protected ItemListener        itemListener    = null;
  3731.     /**
  3732.      * The action listener to keep track of listeners for our focus event.
  3733.      */
  3734.     protected FocusListener    focusListener    = null;
  3735.  
  3736.     //Event listeners
  3737.     private Mouse                mouse        = null;
  3738.     private MouseMotion        mouseMotion    = null;
  3739.     private Key                key            = null;
  3740.     private Focus                focus        = null;
  3741.     private Adjustment            adjustment    = null;
  3742.  
  3743.     private VetoableChangeSupport vetos    = new VetoableChangeSupport(this);
  3744.     private PropertyChangeSupport changes    = new PropertyChangeSupport(this);
  3745.  
  3746.     /**
  3747.      * Column splitter is being dragged to resize a column.
  3748.      */
  3749.     transient protected boolean        isDragging = false;
  3750.     /**
  3751.      * Mouse button has been pressed in a column heading.
  3752.      */
  3753.     transient protected boolean        clickedInHeadings = false;
  3754.     /**
  3755.      * Vertical scrollbar is visible.
  3756.      */
  3757.     transient protected boolean        sbVShow = false;
  3758.     /**
  3759.      * Horizontal scrollbar is visible.
  3760.      */
  3761.     transient protected boolean        sbHShow = false;
  3762.     /**
  3763.      * True if next paint will redraw the entire component even if
  3764.      * its size has not changed.
  3765.      */
  3766.     transient protected boolean        forceRedraw = false;
  3767.     /**
  3768.      * Column sizes need to be recalculated.
  3769.      */
  3770.     transient protected boolean        forceColumnSizeRecalc = false;
  3771.     /**
  3772.      * Offscreen image needs to be flushed.
  3773.      */
  3774.     transient protected boolean        forceFullRedraw = false;
  3775.     /**
  3776.      * Redrawing of component is currently suppressed.
  3777.      * @see #setSupressRedraw
  3778.      */
  3779.     transient protected boolean        isSuppressRedraw = false;
  3780.     /**
  3781.      * Redraw would have occurred if it hadn't been suppressed.
  3782.      */
  3783.     transient protected boolean        redrawWasSupressed = false;
  3784.     /**
  3785.      * This component has the focus.
  3786.      */
  3787.     transient protected boolean        hasFocus = false;
  3788.  
  3789.     //
  3790.     //Offscreen image graphics
  3791.     //
  3792.  
  3793.     /**
  3794.      * The offscreen image. Used to draw the component before painting it to the screen.
  3795.      */
  3796.     transient protected Image offscreenImage = null;
  3797.     /**
  3798.      * The graphics context used to draw into the offscreen image.
  3799.      */
  3800.     transient protected Graphics offscreenImageGraphics = null;
  3801.  
  3802.     transient protected ResourceBundle errors;
  3803.  
  3804.     /**
  3805.      * True if running under Java 1.1.
  3806.      */
  3807.     static protected boolean isSun1_1;
  3808.  
  3809.     static
  3810.     {
  3811.         //Calc it once
  3812.  
  3813.         String vendor = System.getProperty("java.vendor");
  3814.         String version = System.getProperty("java.version");
  3815.         isSun1_1 = ((vendor.startsWith("Sun Microsystems Inc.") ||
  3816.                     (vendor.startsWith("Apple"))                ||
  3817.                     (vendor.startsWith("Symantec Corporation"))  ||
  3818.                     (vendor.startsWith("Netscape"))) &&
  3819.                    ((version.startsWith("11"))
  3820.                     || (version.startsWith("1.1"))));
  3821.     }
  3822. }
  3823.  
  3824.  
  3825. class DeprecatedException
  3826.     extends RuntimeException
  3827. {
  3828.     DeprecatedException(String msg)
  3829.     {
  3830.         super(msg);
  3831.     }
  3832. }