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