Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package javax.swing.plaf.basic;
   27   
   28   import sun.swing.DefaultLookup;
   29   import sun.swing.UIAction;
   30   
   31   import javax.swing;
   32   import javax.swing.event;
   33   import javax.swing.plaf;
   34   import javax.swing.text.Position;
   35   
   36   import java.awt;
   37   import java.awt.event;
   38   import java.awt.datatransfer.Transferable;
   39   import java.awt.geom.Point2D;
   40   
   41   import java.beans.PropertyChangeListener;
   42   import java.beans.PropertyChangeEvent;
   43   
   44   import sun.swing.SwingUtilities2;
   45   import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
   46   
   47   /**
   48    * An extensible implementation of {@code ListUI}.
   49    * <p>
   50    * {@code BasicListUI} instances cannot be shared between multiple
   51    * lists.
   52    *
   53    * @author Hans Muller
   54    * @author Philip Milne
   55    * @author Shannon Hickey (drag and drop)
   56    */
   57   public class BasicListUI extends ListUI
   58   {
   59       private static final StringBuilder BASELINE_COMPONENT_KEY =
   60           new StringBuilder("List.baselineComponent");
   61   
   62       protected JList list = null;
   63       protected CellRendererPane rendererPane;
   64   
   65       // Listeners that this UI attaches to the JList
   66       protected FocusListener focusListener;
   67       protected MouseInputListener mouseInputListener;
   68       protected ListSelectionListener listSelectionListener;
   69       protected ListDataListener listDataListener;
   70       protected PropertyChangeListener propertyChangeListener;
   71       private Handler handler;
   72   
   73       protected int[] cellHeights = null;
   74       protected int cellHeight = -1;
   75       protected int cellWidth = -1;
   76       protected int updateLayoutStateNeeded = modelChanged;
   77       /**
   78        * Height of the list. When asked to paint, if the current size of
   79        * the list differs, this will update the layout state.
   80        */
   81       private int listHeight;
   82   
   83       /**
   84        * Width of the list. When asked to paint, if the current size of
   85        * the list differs, this will update the layout state.
   86        */
   87       private int listWidth;
   88   
   89       /**
   90        * The layout orientation of the list.
   91        */
   92       private int layoutOrientation;
   93   
   94       // Following ivars are used if the list is laying out horizontally
   95   
   96       /**
   97        * Number of columns to create.
   98        */
   99       private int columnCount;
  100       /**
  101        * Preferred height to make the list, this is only used if the
  102        * the list is layed out horizontally.
  103        */
  104       private int preferredHeight;
  105       /**
  106        * Number of rows per column. This is only used if the row height is
  107        * fixed.
  108        */
  109       private int rowsPerColumn;
  110   
  111       /**
  112        * The time factor to treate the series of typed alphanumeric key
  113        * as prefix for first letter navigation.
  114        */
  115       private long timeFactor = 1000L;
  116   
  117       /**
  118        * Local cache of JList's client property "List.isFileList"
  119        */
  120       private boolean isFileList = false;
  121   
  122       /**
  123        * Local cache of JList's component orientation property
  124        */
  125       private boolean isLeftToRight = true;
  126   
  127       /* The bits below define JList property changes that affect layout.
  128        * When one of these properties changes we set a bit in
  129        * updateLayoutStateNeeded.  The change is dealt with lazily, see
  130        * maybeUpdateLayoutState.  Changes to the JLists model, e.g. the
  131        * models length changed, are handled similarly, see DataListener.
  132        */
  133   
  134       protected final static int modelChanged = 1 << 0;
  135       protected final static int selectionModelChanged = 1 << 1;
  136       protected final static int fontChanged = 1 << 2;
  137       protected final static int fixedCellWidthChanged = 1 << 3;
  138       protected final static int fixedCellHeightChanged = 1 << 4;
  139       protected final static int prototypeCellValueChanged = 1 << 5;
  140       protected final static int cellRendererChanged = 1 << 6;
  141       private final static int layoutOrientationChanged = 1 << 7;
  142       private final static int heightChanged = 1 << 8;
  143       private final static int widthChanged = 1 << 9;
  144       private final static int componentOrientationChanged = 1 << 10;
  145   
  146       private static final int DROP_LINE_THICKNESS = 2;
  147   
  148       static void loadActionMap(LazyActionMap map) {
  149           map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN));
  150           map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND));
  151           map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD));
  152           map.put(new Actions(Actions.SELECT_NEXT_COLUMN));
  153           map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND));
  154           map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD));
  155           map.put(new Actions(Actions.SELECT_PREVIOUS_ROW));
  156           map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND));
  157           map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD));
  158           map.put(new Actions(Actions.SELECT_NEXT_ROW));
  159           map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND));
  160           map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD));
  161           map.put(new Actions(Actions.SELECT_FIRST_ROW));
  162           map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND));
  163           map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD));
  164           map.put(new Actions(Actions.SELECT_LAST_ROW));
  165           map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND));
  166           map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD));
  167           map.put(new Actions(Actions.SCROLL_UP));
  168           map.put(new Actions(Actions.SCROLL_UP_EXTEND));
  169           map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD));
  170           map.put(new Actions(Actions.SCROLL_DOWN));
  171           map.put(new Actions(Actions.SCROLL_DOWN_EXTEND));
  172           map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD));
  173           map.put(new Actions(Actions.SELECT_ALL));
  174           map.put(new Actions(Actions.CLEAR_SELECTION));
  175           map.put(new Actions(Actions.ADD_TO_SELECTION));
  176           map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
  177           map.put(new Actions(Actions.EXTEND_TO));
  178           map.put(new Actions(Actions.MOVE_SELECTION_TO));
  179   
  180           map.put(TransferHandler.getCutAction().getValue(Action.NAME),
  181                   TransferHandler.getCutAction());
  182           map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
  183                   TransferHandler.getCopyAction());
  184           map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
  185                   TransferHandler.getPasteAction());
  186       }
  187   
  188       /**
  189        * Paint one List cell: compute the relevant state, get the "rubber stamp"
  190        * cell renderer component, and then use the CellRendererPane to paint it.
  191        * Subclasses may want to override this method rather than paint().
  192        *
  193        * @see #paint
  194        */
  195       protected void paintCell(
  196           Graphics g,
  197           int row,
  198           Rectangle rowBounds,
  199           ListCellRenderer cellRenderer,
  200           ListModel dataModel,
  201           ListSelectionModel selModel,
  202           int leadIndex)
  203       {
  204           Object value = dataModel.getElementAt(row);
  205           boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
  206           boolean isSelected = selModel.isSelectedIndex(row);
  207   
  208           Component rendererComponent =
  209               cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
  210   
  211           int cx = rowBounds.x;
  212           int cy = rowBounds.y;
  213           int cw = rowBounds.width;
  214           int ch = rowBounds.height;
  215   
  216           if (isFileList) {
  217               // Shrink renderer to preferred size. This is mostly used on Windows
  218               // where selection is only shown around the file name, instead of
  219               // across the whole list cell.
  220               int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4);
  221               if (!isLeftToRight) {
  222                   cx += (cw - w);
  223               }
  224               cw = w;
  225           }
  226   
  227           rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
  228       }
  229   
  230   
  231       /**
  232        * Paint the rows that intersect the Graphics objects clipRect.  This
  233        * method calls paintCell as necessary.  Subclasses
  234        * may want to override these methods.
  235        *
  236        * @see #paintCell
  237        */
  238       public void paint(Graphics g, JComponent c) {
  239           Shape clip = g.getClip();
  240           paintImpl(g, c);
  241           g.setClip(clip);
  242   
  243           paintDropLine(g);
  244       }
  245   
  246       private void paintImpl(Graphics g, JComponent c)
  247       {
  248           switch (layoutOrientation) {
  249           case JList.VERTICAL_WRAP:
  250               if (list.getHeight() != listHeight) {
  251                   updateLayoutStateNeeded |= heightChanged;
  252                   redrawList();
  253               }
  254               break;
  255           case JList.HORIZONTAL_WRAP:
  256               if (list.getWidth() != listWidth) {
  257                   updateLayoutStateNeeded |= widthChanged;
  258                   redrawList();
  259               }
  260               break;
  261           default:
  262               break;
  263           }
  264           maybeUpdateLayoutState();
  265   
  266           ListCellRenderer renderer = list.getCellRenderer();
  267           ListModel dataModel = list.getModel();
  268           ListSelectionModel selModel = list.getSelectionModel();
  269           int size;
  270   
  271           if ((renderer == null) || (size = dataModel.getSize()) == 0) {
  272               return;
  273           }
  274   
  275           // Determine how many columns we need to paint
  276           Rectangle paintBounds = g.getClipBounds();
  277   
  278           int startColumn, endColumn;
  279           if (c.getComponentOrientation().isLeftToRight()) {
  280               startColumn = convertLocationToColumn(paintBounds.x,
  281                                                     paintBounds.y);
  282               endColumn = convertLocationToColumn(paintBounds.x +
  283                                                   paintBounds.width,
  284                                                   paintBounds.y);
  285           } else {
  286               startColumn = convertLocationToColumn(paintBounds.x +
  287                                                   paintBounds.width,
  288                                                   paintBounds.y);
  289               endColumn = convertLocationToColumn(paintBounds.x,
  290                                                     paintBounds.y);
  291           }
  292           int maxY = paintBounds.y + paintBounds.height;
  293           int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list);
  294           int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ?
  295                              columnCount : 1;
  296   
  297   
  298           for (int colCounter = startColumn; colCounter <= endColumn;
  299                colCounter++) {
  300               // And then how many rows in this columnn
  301               int row = convertLocationToRowInColumn(paintBounds.y, colCounter);
  302               int rowCount = getRowCount(colCounter);
  303               int index = getModelIndex(colCounter, row);
  304               Rectangle rowBounds = getCellBounds(list, index, index);
  305   
  306               if (rowBounds == null) {
  307                   // Not valid, bail!
  308                   return;
  309               }
  310               while (row < rowCount && rowBounds.y < maxY &&
  311                      index < size) {
  312                   rowBounds.height = getHeight(colCounter, row);
  313                   g.setClip(rowBounds.x, rowBounds.y, rowBounds.width,
  314                             rowBounds.height);
  315                   g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width,
  316                              paintBounds.height);
  317                   paintCell(g, index, rowBounds, renderer, dataModel, selModel,
  318                             leadIndex);
  319                   rowBounds.y += rowBounds.height;
  320                   index += rowIncrement;
  321                   row++;
  322               }
  323           }
  324           // Empty out the renderer pane, allowing renderers to be gc'ed.
  325           rendererPane.removeAll();
  326       }
  327   
  328       private void paintDropLine(Graphics g) {
  329           JList.DropLocation loc = list.getDropLocation();
  330           if (loc == null || !loc.isInsert()) {
  331               return;
  332           }
  333   
  334           Color c = DefaultLookup.getColor(list, this, "List.dropLineColor", null);
  335           if (c != null) {
  336               g.setColor(c);
  337               Rectangle rect = getDropLineRect(loc);
  338               g.fillRect(rect.x, rect.y, rect.width, rect.height);
  339           }
  340       }
  341   
  342       private Rectangle getDropLineRect(JList.DropLocation loc) {
  343           int size = list.getModel().getSize();
  344   
  345           if (size == 0) {
  346               Insets insets = list.getInsets();
  347               if (layoutOrientation == JList.HORIZONTAL_WRAP) {
  348                   if (isLeftToRight) {
  349                       return new Rectangle(insets.left, insets.top, DROP_LINE_THICKNESS, 20);
  350                   } else {
  351                       return new Rectangle(list.getWidth() - DROP_LINE_THICKNESS - insets.right,
  352                                            insets.top, DROP_LINE_THICKNESS, 20);
  353                   }
  354               } else {
  355                   return new Rectangle(insets.left, insets.top,
  356                                        list.getWidth() - insets.left - insets.right,
  357                                        DROP_LINE_THICKNESS);
  358               }
  359           }
  360   
  361           Rectangle rect = null;
  362           int index = loc.getIndex();
  363           boolean decr = false;
  364   
  365           if (layoutOrientation == JList.HORIZONTAL_WRAP) {
  366               if (index == size) {
  367                   decr = true;
  368               } else if (index != 0 && convertModelToRow(index)
  369                                            != convertModelToRow(index - 1)) {
  370   
  371                   Rectangle prev = getCellBounds(list, index - 1);
  372                   Rectangle me = getCellBounds(list, index);
  373                   Point p = loc.getDropPoint();
  374   
  375                   if (isLeftToRight) {
  376                       decr = Point2D.distance(prev.x + prev.width,
  377                                               prev.y + (int)(prev.height / 2.0),
  378                                               p.x, p.y)
  379                              < Point2D.distance(me.x,
  380                                                 me.y + (int)(me.height / 2.0),
  381                                                 p.x, p.y);
  382                   } else {
  383                       decr = Point2D.distance(prev.x,
  384                                               prev.y + (int)(prev.height / 2.0),
  385                                               p.x, p.y)
  386                              < Point2D.distance(me.x + me.width,
  387                                                 me.y + (int)(prev.height / 2.0),
  388                                                 p.x, p.y);
  389                   }
  390               }
  391   
  392               if (decr) {
  393                   index--;
  394                   rect = getCellBounds(list, index);
  395                   if (isLeftToRight) {
  396                       rect.x += rect.width;
  397                   } else {
  398                       rect.x -= DROP_LINE_THICKNESS;
  399                   }
  400               } else {
  401                   rect = getCellBounds(list, index);
  402                   if (!isLeftToRight) {
  403                       rect.x += rect.width - DROP_LINE_THICKNESS;
  404                   }
  405               }
  406   
  407               if (rect.x >= list.getWidth()) {
  408                   rect.x = list.getWidth() - DROP_LINE_THICKNESS;
  409               } else if (rect.x < 0) {
  410                   rect.x = 0;
  411               }
  412   
  413               rect.width = DROP_LINE_THICKNESS;
  414           } else if (layoutOrientation == JList.VERTICAL_WRAP) {
  415               if (index == size) {
  416                   index--;
  417                   rect = getCellBounds(list, index);
  418                   rect.y += rect.height;
  419               } else if (index != 0 && convertModelToColumn(index)
  420                                            != convertModelToColumn(index - 1)) {
  421   
  422                   Rectangle prev = getCellBounds(list, index - 1);
  423                   Rectangle me = getCellBounds(list, index);
  424                   Point p = loc.getDropPoint();
  425                   if (Point2D.distance(prev.x + (int)(prev.width / 2.0),
  426                                        prev.y + prev.height,
  427                                        p.x, p.y)
  428                           < Point2D.distance(me.x + (int)(me.width / 2.0),
  429                                              me.y,
  430                                              p.x, p.y)) {
  431   
  432                       index--;
  433                       rect = getCellBounds(list, index);
  434                       rect.y += rect.height;
  435                   } else {
  436                       rect = getCellBounds(list, index);
  437                   }
  438               } else {
  439                   rect = getCellBounds(list, index);
  440               }
  441   
  442               if (rect.y >= list.getHeight()) {
  443                   rect.y = list.getHeight() - DROP_LINE_THICKNESS;
  444               }
  445   
  446               rect.height = DROP_LINE_THICKNESS;
  447           } else {
  448               if (index == size) {
  449                   index--;
  450                   rect = getCellBounds(list, index);
  451                   rect.y += rect.height;
  452               } else {
  453                   rect = getCellBounds(list, index);
  454               }
  455   
  456               if (rect.y >= list.getHeight()) {
  457                   rect.y = list.getHeight() - DROP_LINE_THICKNESS;
  458               }
  459   
  460               rect.height = DROP_LINE_THICKNESS;
  461           }
  462   
  463           return rect;
  464       }
  465   
  466       /**
  467        * Returns the baseline.
  468        *
  469        * @throws NullPointerException {@inheritDoc}
  470        * @throws IllegalArgumentException {@inheritDoc}
  471        * @see javax.swing.JComponent#getBaseline(int, int)
  472        * @since 1.6
  473        */
  474       public int getBaseline(JComponent c, int width, int height) {
  475           super.getBaseline(c, width, height);
  476           int rowHeight = list.getFixedCellHeight();
  477           UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
  478           Component renderer = (Component)lafDefaults.get(
  479                   BASELINE_COMPONENT_KEY);
  480           if (renderer == null) {
  481               ListCellRenderer lcr = (ListCellRenderer)UIManager.get(
  482                       "List.cellRenderer");
  483   
  484               // fix for 6711072 some LAFs like Nimbus do not provide this
  485               // UIManager key and we should not through a NPE here because of it
  486               if (lcr == null) {
  487                   lcr = new DefaultListCellRenderer();
  488               }
  489               renderer = lcr.getListCellRendererComponent(
  490                       list, "a", -1, false, false);
  491               lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
  492           }
  493           renderer.setFont(list.getFont());
  494           // JList actually has much more complex behavior here.
  495           // If rowHeight != -1 the rowHeight is either the max of all cell
  496           // heights (layout orientation != VERTICAL), or is variable depending
  497           // upon the cell.  We assume a default size.
  498           // We could theoretically query the real renderer, but that would
  499           // not work for an empty model and the results may vary with
  500           // the content.
  501           if (rowHeight == -1) {
  502               rowHeight = renderer.getPreferredSize().height;
  503           }
  504           return renderer.getBaseline(Integer.MAX_VALUE, rowHeight) +
  505                   list.getInsets().top;
  506       }
  507   
  508       /**
  509        * Returns an enum indicating how the baseline of the component
  510        * changes as the size changes.
  511        *
  512        * @throws NullPointerException {@inheritDoc}
  513        * @see javax.swing.JComponent#getBaseline(int, int)
  514        * @since 1.6
  515        */
  516       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
  517               JComponent c) {
  518           super.getBaselineResizeBehavior(c);
  519           return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
  520       }
  521   
  522       /**
  523        * The preferredSize of the list depends upon the layout orientation.
  524        * <table summary="Describes the preferred size for each layout orientation">
  525        * <tr><th>Layout Orientation</th><th>Preferred Size</th></tr>
  526        * <tr>
  527        *   <td>JList.VERTICAL
  528        *   <td>The preferredSize of the list is total height of the rows
  529        *       and the maximum width of the cells.  If JList.fixedCellHeight
  530        *       is specified then the total height of the rows is just
  531        *       (cellVerticalMargins + fixedCellHeight) * model.getSize() where
  532        *       rowVerticalMargins is the space we allocate for drawing
  533        *       the yellow focus outline.  Similarly if fixedCellWidth is
  534        *       specified then we just use that.
  535        *   </td>
  536        * <tr>
  537        *   <td>JList.VERTICAL_WRAP
  538        *   <td>If the visible row count is greater than zero, the preferredHeight
  539        *       is the maximum cell height * visibleRowCount. If the visible row
  540        *       count is <= 0, the preferred height is either the current height
  541        *       of the list, or the maximum cell height, whichever is
  542        *       bigger. The preferred width is than the maximum cell width *
  543        *       number of columns needed. Where the number of columns needs is
  544        *       list.height / max cell height. Max cell height is either the fixed
  545        *       cell height, or is determined by iterating through all the cells
  546        *       to find the maximum height from the ListCellRenderer.
  547        * <tr>
  548        *   <td>JList.HORIZONTAL_WRAP
  549        *   <td>If the visible row count is greater than zero, the preferredHeight
  550        *       is the maximum cell height * adjustedRowCount.  Where
  551        *       visibleRowCount is used to determine the number of columns.
  552        *       Because this lays out horizontally the number of rows is
  553        *       then determined from the column count.  For example, lets say
  554        *       you have a model with 10 items and the visible row count is 8.
  555        *       The number of columns needed to display this is 2, but you no
  556        *       longer need 8 rows to display this, you only need 5, thus
  557        *       the adjustedRowCount is 5.
  558        *       <p>If the visible row
  559        *       count is <= 0, the preferred height is dictated by the
  560        *       number of columns, which will be as many as can fit in the width
  561        *       of the <code>JList</code> (width / max cell width), with at
  562        *       least one column.  The preferred height then becomes the
  563        *       model size / number of columns * maximum cell height.
  564        *       Max cell height is either the fixed
  565        *       cell height, or is determined by iterating through all the cells
  566        *       to find the maximum height from the ListCellRenderer.
  567        * </table>
  568        * The above specifies the raw preferred width and height. The resulting
  569        * preferred width is the above width + insets.left + insets.right and
  570        * the resulting preferred height is the above height + insets.top +
  571        * insets.bottom. Where the <code>Insets</code> are determined from
  572        * <code>list.getInsets()</code>.
  573        *
  574        * @param c The JList component.
  575        * @return The total size of the list.
  576        */
  577       public Dimension getPreferredSize(JComponent c) {
  578           maybeUpdateLayoutState();
  579   
  580           int lastRow = list.getModel().getSize() - 1;
  581           if (lastRow < 0) {
  582               return new Dimension(0, 0);
  583           }
  584   
  585           Insets insets = list.getInsets();
  586           int width = cellWidth * columnCount + insets.left + insets.right;
  587           int height;
  588   
  589           if (layoutOrientation != JList.VERTICAL) {
  590               height = preferredHeight;
  591           }
  592           else {
  593               Rectangle bounds = getCellBounds(list, lastRow);
  594   
  595               if (bounds != null) {
  596                   height = bounds.y + bounds.height + insets.bottom;
  597               }
  598               else {
  599                   height = 0;
  600               }
  601           }
  602           return new Dimension(width, height);
  603       }
  604   
  605   
  606       /**
  607        * Selected the previous row and force it to be visible.
  608        *
  609        * @see JList#ensureIndexIsVisible
  610        */
  611       protected void selectPreviousIndex() {
  612           int s = list.getSelectedIndex();
  613           if(s > 0) {
  614               s -= 1;
  615               list.setSelectedIndex(s);
  616               list.ensureIndexIsVisible(s);
  617           }
  618       }
  619   
  620   
  621       /**
  622        * Selected the previous row and force it to be visible.
  623        *
  624        * @see JList#ensureIndexIsVisible
  625        */
  626       protected void selectNextIndex()
  627       {
  628           int s = list.getSelectedIndex();
  629           if((s + 1) < list.getModel().getSize()) {
  630               s += 1;
  631               list.setSelectedIndex(s);
  632               list.ensureIndexIsVisible(s);
  633           }
  634       }
  635   
  636   
  637       /**
  638        * Registers the keyboard bindings on the <code>JList</code> that the
  639        * <code>BasicListUI</code> is associated with. This method is called at
  640        * installUI() time.
  641        *
  642        * @see #installUI
  643        */
  644       protected void installKeyboardActions() {
  645           InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
  646   
  647           SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
  648                                              inputMap);
  649   
  650           LazyActionMap.installLazyActionMap(list, BasicListUI.class,
  651                                              "List.actionMap");
  652       }
  653   
  654       InputMap getInputMap(int condition) {
  655           if (condition == JComponent.WHEN_FOCUSED) {
  656               InputMap keyMap = (InputMap)DefaultLookup.get(
  657                                list, this, "List.focusInputMap");
  658               InputMap rtlKeyMap;
  659   
  660               if (isLeftToRight ||
  661                   ((rtlKeyMap = (InputMap)DefaultLookup.get(list, this,
  662                                 "List.focusInputMap.RightToLeft")) == null)) {
  663                       return keyMap;
  664               } else {
  665                   rtlKeyMap.setParent(keyMap);
  666                   return rtlKeyMap;
  667               }
  668           }
  669           return null;
  670       }
  671   
  672       /**
  673        * Unregisters keyboard actions installed from
  674        * <code>installKeyboardActions</code>.
  675        * This method is called at uninstallUI() time - subclassess should
  676        * ensure that all of the keyboard actions registered at installUI
  677        * time are removed here.
  678        *
  679        * @see #installUI
  680        */
  681       protected void uninstallKeyboardActions() {
  682           SwingUtilities.replaceUIActionMap(list, null);
  683           SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
  684       }
  685   
  686   
  687       /**
  688        * Creates and installs the listeners for the JList, its model, and its
  689        * selectionModel.  This method is called at installUI() time.
  690        *
  691        * @see #installUI
  692        * @see #uninstallListeners
  693        */
  694       protected void installListeners()
  695       {
  696           TransferHandler th = list.getTransferHandler();
  697           if (th == null || th instanceof UIResource) {
  698               list.setTransferHandler(defaultTransferHandler);
  699               // default TransferHandler doesn't support drop
  700               // so we don't want drop handling
  701               if (list.getDropTarget() instanceof UIResource) {
  702                   list.setDropTarget(null);
  703               }
  704           }
  705   
  706           focusListener = createFocusListener();
  707           mouseInputListener = createMouseInputListener();
  708           propertyChangeListener = createPropertyChangeListener();
  709           listSelectionListener = createListSelectionListener();
  710           listDataListener = createListDataListener();
  711   
  712           list.addFocusListener(focusListener);
  713           list.addMouseListener(mouseInputListener);
  714           list.addMouseMotionListener(mouseInputListener);
  715           list.addPropertyChangeListener(propertyChangeListener);
  716           list.addKeyListener(getHandler());
  717   
  718           ListModel model = list.getModel();
  719           if (model != null) {
  720               model.addListDataListener(listDataListener);
  721           }
  722   
  723           ListSelectionModel selectionModel = list.getSelectionModel();
  724           if (selectionModel != null) {
  725               selectionModel.addListSelectionListener(listSelectionListener);
  726           }
  727       }
  728   
  729   
  730       /**
  731        * Removes the listeners from the JList, its model, and its
  732        * selectionModel.  All of the listener fields, are reset to
  733        * null here.  This method is called at uninstallUI() time,
  734        * it should be kept in sync with installListeners.
  735        *
  736        * @see #uninstallUI
  737        * @see #installListeners
  738        */
  739       protected void uninstallListeners()
  740       {
  741           list.removeFocusListener(focusListener);
  742           list.removeMouseListener(mouseInputListener);
  743           list.removeMouseMotionListener(mouseInputListener);
  744           list.removePropertyChangeListener(propertyChangeListener);
  745           list.removeKeyListener(getHandler());
  746   
  747           ListModel model = list.getModel();
  748           if (model != null) {
  749               model.removeListDataListener(listDataListener);
  750           }
  751   
  752           ListSelectionModel selectionModel = list.getSelectionModel();
  753           if (selectionModel != null) {
  754               selectionModel.removeListSelectionListener(listSelectionListener);
  755           }
  756   
  757           focusListener = null;
  758           mouseInputListener  = null;
  759           listSelectionListener = null;
  760           listDataListener = null;
  761           propertyChangeListener = null;
  762           handler = null;
  763       }
  764   
  765   
  766       /**
  767        * Initializes list properties such as font, foreground, and background,
  768        * and adds the CellRendererPane. The font, foreground, and background
  769        * properties are only set if their current value is either null
  770        * or a UIResource, other properties are set if the current
  771        * value is null.
  772        *
  773        * @see #uninstallDefaults
  774        * @see #installUI
  775        * @see CellRendererPane
  776        */
  777       protected void installDefaults()
  778       {
  779           list.setLayout(null);
  780   
  781           LookAndFeel.installBorder(list, "List.border");
  782   
  783           LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");
  784   
  785           LookAndFeel.installProperty(list, "opaque", Boolean.TRUE);
  786   
  787           if (list.getCellRenderer() == null) {
  788               list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer")));
  789           }
  790   
  791           Color sbg = list.getSelectionBackground();
  792           if (sbg == null || sbg instanceof UIResource) {
  793               list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
  794           }
  795   
  796           Color sfg = list.getSelectionForeground();
  797           if (sfg == null || sfg instanceof UIResource) {
  798               list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
  799           }
  800   
  801           Long l = (Long)UIManager.get("List.timeFactor");
  802           timeFactor = (l!=null) ? l.longValue() : 1000L;
  803   
  804           updateIsFileList();
  805       }
  806   
  807       private void updateIsFileList() {
  808           boolean b = Boolean.TRUE.equals(list.getClientProperty("List.isFileList"));
  809           if (b != isFileList) {
  810               isFileList = b;
  811               Font oldFont = list.getFont();
  812               if (oldFont == null || oldFont instanceof UIResource) {
  813                   Font newFont = UIManager.getFont(b ? "FileChooser.listFont" : "List.font");
  814                   if (newFont != null && newFont != oldFont) {
  815                       list.setFont(newFont);
  816                   }
  817               }
  818           }
  819       }
  820   
  821   
  822       /**
  823        * Sets the list properties that have not been explicitly overridden to
  824        * {@code null}. A property is considered overridden if its current value
  825        * is not a {@code UIResource}.
  826        *
  827        * @see #installDefaults
  828        * @see #uninstallUI
  829        * @see CellRendererPane
  830        */
  831       protected void uninstallDefaults()
  832       {
  833           LookAndFeel.uninstallBorder(list);
  834           if (list.getFont() instanceof UIResource) {
  835               list.setFont(null);
  836           }
  837           if (list.getForeground() instanceof UIResource) {
  838               list.setForeground(null);
  839           }
  840           if (list.getBackground() instanceof UIResource) {
  841               list.setBackground(null);
  842           }
  843           if (list.getSelectionBackground() instanceof UIResource) {
  844               list.setSelectionBackground(null);
  845           }
  846           if (list.getSelectionForeground() instanceof UIResource) {
  847               list.setSelectionForeground(null);
  848           }
  849           if (list.getCellRenderer() instanceof UIResource) {
  850               list.setCellRenderer(null);
  851           }
  852           if (list.getTransferHandler() instanceof UIResource) {
  853               list.setTransferHandler(null);
  854           }
  855       }
  856   
  857   
  858       /**
  859        * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
  860        * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
  861        * in order.
  862        *
  863        * @see #installDefaults
  864        * @see #installListeners
  865        * @see #installKeyboardActions
  866        */
  867       public void installUI(JComponent c)
  868       {
  869           list = (JList)c;
  870   
  871           layoutOrientation = list.getLayoutOrientation();
  872   
  873           rendererPane = new CellRendererPane();
  874           list.add(rendererPane);
  875   
  876           columnCount = 1;
  877   
  878           updateLayoutStateNeeded = modelChanged;
  879           isLeftToRight = list.getComponentOrientation().isLeftToRight();
  880   
  881           installDefaults();
  882           installListeners();
  883           installKeyboardActions();
  884       }
  885   
  886   
  887       /**
  888        * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
  889        * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>
  890        * in order.  Sets this.list to null.
  891        *
  892        * @see #uninstallListeners
  893        * @see #uninstallKeyboardActions
  894        * @see #uninstallDefaults
  895        */
  896       public void uninstallUI(JComponent c)
  897       {
  898           uninstallListeners();
  899           uninstallDefaults();
  900           uninstallKeyboardActions();
  901   
  902           cellWidth = cellHeight = -1;
  903           cellHeights = null;
  904   
  905           listWidth = listHeight = -1;
  906   
  907           list.remove(rendererPane);
  908           rendererPane = null;
  909           list = null;
  910       }
  911   
  912   
  913       /**
  914        * Returns a new instance of BasicListUI.  BasicListUI delegates are
  915        * allocated one per JList.
  916        *
  917        * @return A new ListUI implementation for the Windows look and feel.
  918        */
  919       public static ComponentUI createUI(JComponent list) {
  920           return new BasicListUI();
  921       }
  922   
  923   
  924       /**
  925        * {@inheritDoc}
  926        * @throws NullPointerException {@inheritDoc}
  927        */
  928       public int locationToIndex(JList list, Point location) {
  929           maybeUpdateLayoutState();
  930           return convertLocationToModel(location.x, location.y);
  931       }
  932   
  933   
  934       /**
  935        * {@inheritDoc}
  936        */
  937       public Point indexToLocation(JList list, int index) {
  938           maybeUpdateLayoutState();
  939           Rectangle rect = getCellBounds(list, index, index);
  940   
  941           if (rect != null) {
  942               return new Point(rect.x, rect.y);
  943           }
  944           return null;
  945       }
  946   
  947   
  948       /**
  949        * {@inheritDoc}
  950        */
  951       public Rectangle getCellBounds(JList list, int index1, int index2) {
  952           maybeUpdateLayoutState();
  953   
  954           int minIndex = Math.min(index1, index2);
  955           int maxIndex = Math.max(index1, index2);
  956   
  957           if (minIndex >= list.getModel().getSize()) {
  958               return null;
  959           }
  960   
  961           Rectangle minBounds = getCellBounds(list, minIndex);
  962   
  963           if (minBounds == null) {
  964               return null;
  965           }
  966           if (minIndex == maxIndex) {
  967               return minBounds;
  968           }
  969           Rectangle maxBounds = getCellBounds(list, maxIndex);
  970   
  971           if (maxBounds != null) {
  972               if (layoutOrientation == JList.HORIZONTAL_WRAP) {
  973                   int minRow = convertModelToRow(minIndex);
  974                   int maxRow = convertModelToRow(maxIndex);
  975   
  976                   if (minRow != maxRow) {
  977                       minBounds.x = 0;
  978                       minBounds.width = list.getWidth();
  979                   }
  980               }
  981               else if (minBounds.x != maxBounds.x) {
  982                   // Different columns
  983                   minBounds.y = 0;
  984                   minBounds.height = list.getHeight();
  985               }
  986               minBounds.add(maxBounds);
  987           }
  988           return minBounds;
  989       }
  990   
  991       /**
  992        * Gets the bounds of the specified model index, returning the resulting
  993        * bounds, or null if <code>index</code> is not valid.
  994        */
  995       private Rectangle getCellBounds(JList list, int index) {
  996           maybeUpdateLayoutState();
  997   
  998           int row = convertModelToRow(index);
  999           int column = convertModelToColumn(index);
 1000   
 1001           if (row == -1 || column == -1) {
 1002               return null;
 1003           }
 1004   
 1005           Insets insets = list.getInsets();
 1006           int x;
 1007           int w = cellWidth;
 1008           int y = insets.top;
 1009           int h;
 1010           switch (layoutOrientation) {
 1011           case JList.VERTICAL_WRAP:
 1012           case JList.HORIZONTAL_WRAP:
 1013               if (isLeftToRight) {
 1014                   x = insets.left + column * cellWidth;
 1015               } else {
 1016                   x = list.getWidth() - insets.right - (column+1) * cellWidth;
 1017               }
 1018               y += cellHeight * row;
 1019               h = cellHeight;
 1020               break;
 1021           default:
 1022               x = insets.left;
 1023               if (cellHeights == null) {
 1024                   y += (cellHeight * row);
 1025               }
 1026               else if (row >= cellHeights.length) {
 1027                   y = 0;
 1028               }
 1029               else {
 1030                   for(int i = 0; i < row; i++) {
 1031                       y += cellHeights[i];
 1032                   }
 1033               }
 1034               w = list.getWidth() - (insets.left + insets.right);
 1035               h = getRowHeight(index);
 1036               break;
 1037           }
 1038           return new Rectangle(x, y, w, h);
 1039       }
 1040   
 1041       /**
 1042        * Returns the height of the specified row based on the current layout.
 1043        *
 1044        * @return The specified row height or -1 if row isn't valid.
 1045        * @see #convertYToRow
 1046        * @see #convertRowToY
 1047        * @see #updateLayoutState
 1048        */
 1049       protected int getRowHeight(int row)
 1050       {
 1051           return getHeight(0, row);
 1052       }
 1053   
 1054   
 1055       /**
 1056        * Convert the JList relative coordinate to the row that contains it,
 1057        * based on the current layout.  If y0 doesn't fall within any row,
 1058        * return -1.
 1059        *
 1060        * @return The row that contains y0, or -1.
 1061        * @see #getRowHeight
 1062        * @see #updateLayoutState
 1063        */
 1064       protected int convertYToRow(int y0)
 1065       {
 1066           return convertLocationToRow(0, y0, false);
 1067       }
 1068   
 1069   
 1070       /**
 1071        * Return the JList relative Y coordinate of the origin of the specified
 1072        * row or -1 if row isn't valid.
 1073        *
 1074        * @return The Y coordinate of the origin of row, or -1.
 1075        * @see #getRowHeight
 1076        * @see #updateLayoutState
 1077        */
 1078       protected int convertRowToY(int row)
 1079       {
 1080           if (row >= getRowCount(0) || row < 0) {
 1081               return -1;
 1082           }
 1083           Rectangle bounds = getCellBounds(list, row, row);
 1084           return bounds.y;
 1085       }
 1086   
 1087       /**
 1088        * Returns the height of the cell at the passed in location.
 1089        */
 1090       private int getHeight(int column, int row) {
 1091           if (column < 0 || column > columnCount || row < 0) {
 1092               return -1;
 1093           }
 1094           if (layoutOrientation != JList.VERTICAL) {
 1095               return cellHeight;
 1096           }
 1097           if (row >= list.getModel().getSize()) {
 1098               return -1;
 1099           }
 1100           return (cellHeights == null) ? cellHeight :
 1101                              ((row < cellHeights.length) ? cellHeights[row] : -1);
 1102       }
 1103   
 1104       /**
 1105        * Returns the row at location x/y.
 1106        *
 1107        * @param closest If true and the location doesn't exactly match a
 1108        *                particular location, this will return the closest row.
 1109        */
 1110       private int convertLocationToRow(int x, int y0, boolean closest) {
 1111           int size = list.getModel().getSize();
 1112   
 1113           if (size <= 0) {
 1114               return -1;
 1115           }
 1116           Insets insets = list.getInsets();
 1117           if (cellHeights == null) {
 1118               int row = (cellHeight == 0) ? 0 :
 1119                              ((y0 - insets.top) / cellHeight);
 1120               if (closest) {
 1121                   if (row < 0) {
 1122                       row = 0;
 1123                   }
 1124                   else if (row >= size) {
 1125                       row = size - 1;
 1126                   }
 1127               }
 1128               return row;
 1129           }
 1130           else if (size > cellHeights.length) {
 1131               return -1;
 1132           }
 1133           else {
 1134               int y = insets.top;
 1135               int row = 0;
 1136   
 1137               if (closest && y0 < y) {
 1138                   return 0;
 1139               }
 1140               int i;
 1141               for (i = 0; i < size; i++) {
 1142                   if ((y0 >= y) && (y0 < y + cellHeights[i])) {
 1143                       return row;
 1144                   }
 1145                   y += cellHeights[i];
 1146                   row += 1;
 1147               }
 1148               return i - 1;
 1149           }
 1150       }
 1151   
 1152       /**
 1153        * Returns the closest row that starts at the specified y-location
 1154        * in the passed in column.
 1155        */
 1156       private int convertLocationToRowInColumn(int y, int column) {
 1157           int x = 0;
 1158   
 1159           if (layoutOrientation != JList.VERTICAL) {
 1160               if (isLeftToRight) {
 1161                   x = column * cellWidth;
 1162               } else {
 1163                   x = list.getWidth() - (column+1)*cellWidth - list.getInsets().right;
 1164               }
 1165           }
 1166           return convertLocationToRow(x, y, true);
 1167       }
 1168   
 1169       /**
 1170        * Returns the closest location to the model index of the passed in
 1171        * location.
 1172        */
 1173       private int convertLocationToModel(int x, int y) {
 1174           int row = convertLocationToRow(x, y, true);
 1175           int column = convertLocationToColumn(x, y);
 1176   
 1177           if (row >= 0 && column >= 0) {
 1178               return getModelIndex(column, row);
 1179           }
 1180           return -1;
 1181       }
 1182   
 1183       /**
 1184        * Returns the number of rows in the given column.
 1185        */
 1186       private int getRowCount(int column) {
 1187           if (column < 0 || column >= columnCount) {
 1188               return -1;
 1189           }
 1190           if (layoutOrientation == JList.VERTICAL ||
 1191                     (column == 0 && columnCount == 1)) {
 1192               return list.getModel().getSize();
 1193           }
 1194           if (column >= columnCount) {
 1195               return -1;
 1196           }
 1197           if (layoutOrientation == JList.VERTICAL_WRAP) {
 1198               if (column < (columnCount - 1)) {
 1199                   return rowsPerColumn;
 1200               }
 1201               return list.getModel().getSize() - (columnCount - 1) *
 1202                           rowsPerColumn;
 1203           }
 1204           // JList.HORIZONTAL_WRAP
 1205           int diff = columnCount - (columnCount * rowsPerColumn -
 1206                                     list.getModel().getSize());
 1207   
 1208           if (column >= diff) {
 1209               return Math.max(0, rowsPerColumn - 1);
 1210           }
 1211           return rowsPerColumn;
 1212       }
 1213   
 1214       /**
 1215        * Returns the model index for the specified display location.
 1216        * If <code>column</code>x<code>row</code> is beyond the length of the
 1217        * model, this will return the model size - 1.
 1218        */
 1219       private int getModelIndex(int column, int row) {
 1220           switch (layoutOrientation) {
 1221           case JList.VERTICAL_WRAP:
 1222               return Math.min(list.getModel().getSize() - 1, rowsPerColumn *
 1223                               column + Math.min(row, rowsPerColumn-1));
 1224           case JList.HORIZONTAL_WRAP:
 1225               return Math.min(list.getModel().getSize() - 1, row * columnCount +
 1226                               column);
 1227           default:
 1228               return row;
 1229           }
 1230       }
 1231   
 1232       /**
 1233        * Returns the closest column to the passed in location.
 1234        */
 1235       private int convertLocationToColumn(int x, int y) {
 1236           if (cellWidth > 0) {
 1237               if (layoutOrientation == JList.VERTICAL) {
 1238                   return 0;
 1239               }
 1240               Insets insets = list.getInsets();
 1241               int col;
 1242               if (isLeftToRight) {
 1243                   col = (x - insets.left) / cellWidth;
 1244               } else {
 1245                   col = (list.getWidth() - x - insets.right - 1) / cellWidth;
 1246               }
 1247               if (col < 0) {
 1248                   return 0;
 1249               }
 1250               else if (col >= columnCount) {
 1251                   return columnCount - 1;
 1252               }
 1253               return col;
 1254           }
 1255           return 0;
 1256       }
 1257   
 1258       /**
 1259        * Returns the row that the model index <code>index</code> will be
 1260        * displayed in..
 1261        */
 1262       private int convertModelToRow(int index) {
 1263           int size = list.getModel().getSize();
 1264   
 1265           if ((index < 0) || (index >= size)) {
 1266               return -1;
 1267           }
 1268   
 1269           if (layoutOrientation != JList.VERTICAL && columnCount > 1 &&
 1270                                                      rowsPerColumn > 0) {
 1271               if (layoutOrientation == JList.VERTICAL_WRAP) {
 1272                   return index % rowsPerColumn;
 1273               }
 1274               return index / columnCount;
 1275           }
 1276           return index;
 1277       }
 1278   
 1279       /**
 1280        * Returns the column that the model index <code>index</code> will be
 1281        * displayed in.
 1282        */
 1283       private int convertModelToColumn(int index) {
 1284           int size = list.getModel().getSize();
 1285   
 1286           if ((index < 0) || (index >= size)) {
 1287               return -1;
 1288           }
 1289   
 1290           if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 &&
 1291                                                      columnCount > 1) {
 1292               if (layoutOrientation == JList.VERTICAL_WRAP) {
 1293                   return index / rowsPerColumn;
 1294               }
 1295               return index % columnCount;
 1296           }
 1297           return 0;
 1298       }
 1299   
 1300       /**
 1301        * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
 1302        * updateLayoutStateNeeded.  This method should be called by methods
 1303        * before doing any computation based on the geometry of the list.
 1304        * For example it's the first call in paint() and getPreferredSize().
 1305        *
 1306        * @see #updateLayoutState
 1307        */
 1308       protected void maybeUpdateLayoutState()
 1309       {
 1310           if (updateLayoutStateNeeded != 0) {
 1311               updateLayoutState();
 1312               updateLayoutStateNeeded = 0;
 1313           }
 1314       }
 1315   
 1316   
 1317       /**
 1318        * Recompute the value of cellHeight or cellHeights based
 1319        * and cellWidth, based on the current font and the current
 1320        * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
 1321        *
 1322        * @see #maybeUpdateLayoutState
 1323        */
 1324       protected void updateLayoutState()
 1325       {
 1326           /* If both JList fixedCellWidth and fixedCellHeight have been
 1327            * set, then initialize cellWidth and cellHeight, and set
 1328            * cellHeights to null.
 1329            */
 1330   
 1331           int fixedCellHeight = list.getFixedCellHeight();
 1332           int fixedCellWidth = list.getFixedCellWidth();
 1333   
 1334           cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
 1335   
 1336           if (fixedCellHeight != -1) {
 1337               cellHeight = fixedCellHeight;
 1338               cellHeights = null;
 1339           }
 1340           else {
 1341               cellHeight = -1;
 1342               cellHeights = new int[list.getModel().getSize()];
 1343           }
 1344   
 1345           /* If either of  JList fixedCellWidth and fixedCellHeight haven't
 1346            * been set, then initialize cellWidth and cellHeights by
 1347            * scanning through the entire model.  Note: if the renderer is
 1348            * null, we just set cellWidth and cellHeights[*] to zero,
 1349            * if they're not set already.
 1350            */
 1351   
 1352           if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
 1353   
 1354               ListModel dataModel = list.getModel();
 1355               int dataModelSize = dataModel.getSize();
 1356               ListCellRenderer renderer = list.getCellRenderer();
 1357   
 1358               if (renderer != null) {
 1359                   for(int index = 0; index < dataModelSize; index++) {
 1360                       Object value = dataModel.getElementAt(index);
 1361                       Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
 1362                       rendererPane.add(c);
 1363                       Dimension cellSize = c.getPreferredSize();
 1364                       if (fixedCellWidth == -1) {
 1365                           cellWidth = Math.max(cellSize.width, cellWidth);
 1366                       }
 1367                       if (fixedCellHeight == -1) {
 1368                           cellHeights[index] = cellSize.height;
 1369                       }
 1370                   }
 1371               }
 1372               else {
 1373                   if (cellWidth == -1) {
 1374                       cellWidth = 0;
 1375                   }
 1376                   if (cellHeights == null) {
 1377                       cellHeights = new int[dataModelSize];
 1378                   }
 1379                   for(int index = 0; index < dataModelSize; index++) {
 1380                       cellHeights[index] = 0;
 1381                   }
 1382               }
 1383           }
 1384   
 1385           columnCount = 1;
 1386           if (layoutOrientation != JList.VERTICAL) {
 1387               updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight);
 1388           }
 1389       }
 1390   
 1391       /**
 1392        * Invoked when the list is layed out horizontally to determine how
 1393        * many columns to create.
 1394        * <p>
 1395        * This updates the <code>rowsPerColumn, </code><code>columnCount</code>,
 1396        * <code>preferredHeight</code> and potentially <code>cellHeight</code>
 1397        * instance variables.
 1398        */
 1399       private void updateHorizontalLayoutState(int fixedCellWidth,
 1400                                                int fixedCellHeight) {
 1401           int visRows = list.getVisibleRowCount();
 1402           int dataModelSize = list.getModel().getSize();
 1403           Insets insets = list.getInsets();
 1404   
 1405           listHeight = list.getHeight();
 1406           listWidth = list.getWidth();
 1407   
 1408           if (dataModelSize == 0) {
 1409               rowsPerColumn = columnCount = 0;
 1410               preferredHeight = insets.top + insets.bottom;
 1411               return;
 1412           }
 1413   
 1414           int height;
 1415   
 1416           if (fixedCellHeight != -1) {
 1417               height = fixedCellHeight;
 1418           }
 1419           else {
 1420               // Determine the max of the renderer heights.
 1421               int maxHeight = 0;
 1422               if (cellHeights.length > 0) {
 1423                   maxHeight = cellHeights[cellHeights.length - 1];
 1424                   for (int counter = cellHeights.length - 2;
 1425                        counter >= 0; counter--) {
 1426                       maxHeight = Math.max(maxHeight, cellHeights[counter]);
 1427                   }
 1428               }
 1429               height = cellHeight = maxHeight;
 1430               cellHeights = null;
 1431           }
 1432           // The number of rows is either determined by the visible row
 1433           // count, or by the height of the list.
 1434           rowsPerColumn = dataModelSize;
 1435           if (visRows > 0) {
 1436               rowsPerColumn = visRows;
 1437               columnCount = Math.max(1, dataModelSize / rowsPerColumn);
 1438               if (dataModelSize > 0 && dataModelSize > rowsPerColumn &&
 1439                   dataModelSize % rowsPerColumn != 0) {
 1440                   columnCount++;
 1441               }
 1442               if (layoutOrientation == JList.HORIZONTAL_WRAP) {
 1443                   // Because HORIZONTAL_WRAP flows differently, the
 1444                   // rowsPerColumn needs to be adjusted.
 1445                   rowsPerColumn = (dataModelSize / columnCount);
 1446                   if (dataModelSize % columnCount > 0) {
 1447                       rowsPerColumn++;
 1448                   }
 1449               }
 1450           }
 1451           else if (layoutOrientation == JList.VERTICAL_WRAP && height != 0) {
 1452               rowsPerColumn = Math.max(1, (listHeight - insets.top -
 1453                                            insets.bottom) / height);
 1454               columnCount = Math.max(1, dataModelSize / rowsPerColumn);
 1455               if (dataModelSize > 0 && dataModelSize > rowsPerColumn &&
 1456                   dataModelSize % rowsPerColumn != 0) {
 1457                   columnCount++;
 1458               }
 1459           }
 1460           else if (layoutOrientation == JList.HORIZONTAL_WRAP && cellWidth > 0 &&
 1461                    listWidth > 0) {
 1462               columnCount = Math.max(1, (listWidth - insets.left -
 1463                                          insets.right) / cellWidth);
 1464               rowsPerColumn = dataModelSize / columnCount;
 1465               if (dataModelSize % columnCount > 0) {
 1466                   rowsPerColumn++;
 1467               }
 1468           }
 1469           preferredHeight = rowsPerColumn * cellHeight + insets.top +
 1470                                 insets.bottom;
 1471       }
 1472   
 1473       private Handler getHandler() {
 1474           if (handler == null) {
 1475               handler = new Handler();
 1476           }
 1477           return handler;
 1478       }
 1479   
 1480       /**
 1481        * Mouse input, and focus handling for JList.  An instance of this
 1482        * class is added to the appropriate java.awt.Component lists
 1483        * at installUI() time.  Note keyboard input is handled with JComponent
 1484        * KeyboardActions, see installKeyboardActions().
 1485        * <p>
 1486        * <strong>Warning:</strong>
 1487        * Serialized objects of this class will not be compatible with
 1488        * future Swing releases. The current serialization support is
 1489        * appropriate for short term storage or RMI between applications running
 1490        * the same version of Swing.  As of 1.4, support for long term storage
 1491        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1492        * has been added to the <code>java.beans</code> package.
 1493        * Please see {@link java.beans.XMLEncoder}.
 1494        *
 1495        * @see #createMouseInputListener
 1496        * @see #installKeyboardActions
 1497        * @see #installUI
 1498        */
 1499       public class MouseInputHandler implements MouseInputListener
 1500       {
 1501           public void mouseClicked(MouseEvent e) {
 1502               getHandler().mouseClicked(e);
 1503           }
 1504   
 1505           public void mouseEntered(MouseEvent e) {
 1506               getHandler().mouseEntered(e);
 1507           }
 1508   
 1509           public void mouseExited(MouseEvent e) {
 1510               getHandler().mouseExited(e);
 1511           }
 1512   
 1513           public void mousePressed(MouseEvent e) {
 1514               getHandler().mousePressed(e);
 1515           }
 1516   
 1517           public void mouseDragged(MouseEvent e) {
 1518               getHandler().mouseDragged(e);
 1519           }
 1520   
 1521           public void mouseMoved(MouseEvent e) {
 1522               getHandler().mouseMoved(e);
 1523           }
 1524   
 1525           public void mouseReleased(MouseEvent e) {
 1526               getHandler().mouseReleased(e);
 1527           }
 1528       }
 1529   
 1530   
 1531       /**
 1532        * Creates a delegate that implements MouseInputListener.
 1533        * The delegate is added to the corresponding java.awt.Component listener
 1534        * lists at installUI() time. Subclasses can override this method to return
 1535        * a custom MouseInputListener, e.g.
 1536        * <pre>
 1537        * class MyListUI extends BasicListUI {
 1538        *    protected MouseInputListener <b>createMouseInputListener</b>() {
 1539        *        return new MyMouseInputHandler();
 1540        *    }
 1541        *    public class MyMouseInputHandler extends MouseInputHandler {
 1542        *        public void mouseMoved(MouseEvent e) {
 1543        *            // do some extra work when the mouse moves
 1544        *            super.mouseMoved(e);
 1545        *        }
 1546        *    }
 1547        * }
 1548        * </pre>
 1549        *
 1550        * @see MouseInputHandler
 1551        * @see #installUI
 1552        */
 1553       protected MouseInputListener createMouseInputListener() {
 1554           return getHandler();
 1555       }
 1556   
 1557       /**
 1558        * This class should be treated as a &quot;protected&quot; inner class.
 1559        * Instantiate it only within subclasses of {@code BasicListUI}.
 1560        */
 1561       public class FocusHandler implements FocusListener
 1562       {
 1563           protected void repaintCellFocus()
 1564           {
 1565               getHandler().repaintCellFocus();
 1566           }
 1567   
 1568           /* The focusGained() focusLost() methods run when the JList
 1569            * focus changes.
 1570            */
 1571   
 1572           public void focusGained(FocusEvent e) {
 1573               getHandler().focusGained(e);
 1574           }
 1575   
 1576           public void focusLost(FocusEvent e) {
 1577               getHandler().focusLost(e);
 1578           }
 1579       }
 1580   
 1581       protected FocusListener createFocusListener() {
 1582           return getHandler();
 1583       }
 1584   
 1585       /**
 1586        * The ListSelectionListener that's added to the JLists selection
 1587        * model at installUI time, and whenever the JList.selectionModel property
 1588        * changes.  When the selection changes we repaint the affected rows.
 1589        * <p>
 1590        * <strong>Warning:</strong>
 1591        * Serialized objects of this class will not be compatible with
 1592        * future Swing releases. The current serialization support is
 1593        * appropriate for short term storage or RMI between applications running
 1594        * the same version of Swing.  As of 1.4, support for long term storage
 1595        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1596        * has been added to the <code>java.beans</code> package.
 1597        * Please see {@link java.beans.XMLEncoder}.
 1598        *
 1599        * @see #createListSelectionListener
 1600        * @see #getCellBounds
 1601        * @see #installUI
 1602        */
 1603       public class ListSelectionHandler implements ListSelectionListener
 1604       {
 1605           public void valueChanged(ListSelectionEvent e)
 1606           {
 1607               getHandler().valueChanged(e);
 1608           }
 1609       }
 1610   
 1611   
 1612       /**
 1613        * Creates an instance of ListSelectionHandler that's added to
 1614        * the JLists by selectionModel as needed.  Subclasses can override
 1615        * this method to return a custom ListSelectionListener, e.g.
 1616        * <pre>
 1617        * class MyListUI extends BasicListUI {
 1618        *    protected ListSelectionListener <b>createListSelectionListener</b>() {
 1619        *        return new MySelectionListener();
 1620        *    }
 1621        *    public class MySelectionListener extends ListSelectionHandler {
 1622        *        public void valueChanged(ListSelectionEvent e) {
 1623        *            // do some extra work when the selection changes
 1624        *            super.valueChange(e);
 1625        *        }
 1626        *    }
 1627        * }
 1628        * </pre>
 1629        *
 1630        * @see ListSelectionHandler
 1631        * @see #installUI
 1632        */
 1633       protected ListSelectionListener createListSelectionListener() {
 1634           return getHandler();
 1635       }
 1636   
 1637   
 1638       private void redrawList() {
 1639           list.revalidate();
 1640           list.repaint();
 1641       }
 1642   
 1643   
 1644       /**
 1645        * The ListDataListener that's added to the JLists model at
 1646        * installUI time, and whenever the JList.model property changes.
 1647        * <p>
 1648        * <strong>Warning:</strong>
 1649        * Serialized objects of this class will not be compatible with
 1650        * future Swing releases. The current serialization support is
 1651        * appropriate for short term storage or RMI between applications running
 1652        * the same version of Swing.  As of 1.4, support for long term storage
 1653        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1654        * has been added to the <code>java.beans</code> package.
 1655        * Please see {@link java.beans.XMLEncoder}.
 1656        *
 1657        * @see JList#getModel
 1658        * @see #maybeUpdateLayoutState
 1659        * @see #createListDataListener
 1660        * @see #installUI
 1661        */
 1662       public class ListDataHandler implements ListDataListener
 1663       {
 1664           public void intervalAdded(ListDataEvent e) {
 1665               getHandler().intervalAdded(e);
 1666           }
 1667   
 1668   
 1669           public void intervalRemoved(ListDataEvent e)
 1670           {
 1671               getHandler().intervalRemoved(e);
 1672           }
 1673   
 1674   
 1675           public void contentsChanged(ListDataEvent e) {
 1676               getHandler().contentsChanged(e);
 1677           }
 1678       }
 1679   
 1680   
 1681       /**
 1682        * Creates an instance of ListDataListener that's added to
 1683        * the JLists by model as needed.  Subclasses can override
 1684        * this method to return a custom ListDataListener, e.g.
 1685        * <pre>
 1686        * class MyListUI extends BasicListUI {
 1687        *    protected ListDataListener <b>createListDataListener</b>() {
 1688        *        return new MyListDataListener();
 1689        *    }
 1690        *    public class MyListDataListener extends ListDataHandler {
 1691        *        public void contentsChanged(ListDataEvent e) {
 1692        *            // do some extra work when the models contents change
 1693        *            super.contentsChange(e);
 1694        *        }
 1695        *    }
 1696        * }
 1697        * </pre>
 1698        *
 1699        * @see ListDataListener
 1700        * @see JList#getModel
 1701        * @see #installUI
 1702        */
 1703       protected ListDataListener createListDataListener() {
 1704           return getHandler();
 1705       }
 1706   
 1707   
 1708       /**
 1709        * The PropertyChangeListener that's added to the JList at
 1710        * installUI time.  When the value of a JList property that
 1711        * affects layout changes, we set a bit in updateLayoutStateNeeded.
 1712        * If the JLists model changes we additionally remove our listeners
 1713        * from the old model.  Likewise for the JList selectionModel.
 1714        * <p>
 1715        * <strong>Warning:</strong>
 1716        * Serialized objects of this class will not be compatible with
 1717        * future Swing releases. The current serialization support is
 1718        * appropriate for short term storage or RMI between applications running
 1719        * the same version of Swing.  As of 1.4, support for long term storage
 1720        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1721        * has been added to the <code>java.beans</code> package.
 1722        * Please see {@link java.beans.XMLEncoder}.
 1723        *
 1724        * @see #maybeUpdateLayoutState
 1725        * @see #createPropertyChangeListener
 1726        * @see #installUI
 1727        */
 1728       public class PropertyChangeHandler implements PropertyChangeListener
 1729       {
 1730           public void propertyChange(PropertyChangeEvent e)
 1731           {
 1732               getHandler().propertyChange(e);
 1733           }
 1734       }
 1735   
 1736   
 1737       /**
 1738        * Creates an instance of PropertyChangeHandler that's added to
 1739        * the JList by installUI().  Subclasses can override this method
 1740        * to return a custom PropertyChangeListener, e.g.
 1741        * <pre>
 1742        * class MyListUI extends BasicListUI {
 1743        *    protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
 1744        *        return new MyPropertyChangeListener();
 1745        *    }
 1746        *    public class MyPropertyChangeListener extends PropertyChangeHandler {
 1747        *        public void propertyChange(PropertyChangeEvent e) {
 1748        *            if (e.getPropertyName().equals("model")) {
 1749        *                // do some extra work when the model changes
 1750        *            }
 1751        *            super.propertyChange(e);
 1752        *        }
 1753        *    }
 1754        * }
 1755        * </pre>
 1756        *
 1757        * @see PropertyChangeListener
 1758        * @see #installUI
 1759        */
 1760       protected PropertyChangeListener createPropertyChangeListener() {
 1761           return getHandler();
 1762       }
 1763   
 1764       /** Used by IncrementLeadSelectionAction. Indicates the action should
 1765        * change the lead, and not select it. */
 1766       private static final int CHANGE_LEAD = 0;
 1767       /** Used by IncrementLeadSelectionAction. Indicates the action should
 1768        * change the selection and lead. */
 1769       private static final int CHANGE_SELECTION = 1;
 1770       /** Used by IncrementLeadSelectionAction. Indicates the action should
 1771        * extend the selection from the anchor to the next index. */
 1772       private static final int EXTEND_SELECTION = 2;
 1773   
 1774   
 1775       private static class Actions extends UIAction {
 1776           private static final String SELECT_PREVIOUS_COLUMN =
 1777                                       "selectPreviousColumn";
 1778           private static final String SELECT_PREVIOUS_COLUMN_EXTEND =
 1779                                       "selectPreviousColumnExtendSelection";
 1780           private static final String SELECT_PREVIOUS_COLUMN_CHANGE_LEAD =
 1781                                       "selectPreviousColumnChangeLead";
 1782           private static final String SELECT_NEXT_COLUMN = "selectNextColumn";
 1783           private static final String SELECT_NEXT_COLUMN_EXTEND =
 1784                                       "selectNextColumnExtendSelection";
 1785           private static final String SELECT_NEXT_COLUMN_CHANGE_LEAD =
 1786                                       "selectNextColumnChangeLead";
 1787           private static final String SELECT_PREVIOUS_ROW = "selectPreviousRow";
 1788           private static final String SELECT_PREVIOUS_ROW_EXTEND =
 1789                                        "selectPreviousRowExtendSelection";
 1790           private static final String SELECT_PREVIOUS_ROW_CHANGE_LEAD =
 1791                                        "selectPreviousRowChangeLead";
 1792           private static final String SELECT_NEXT_ROW = "selectNextRow";
 1793           private static final String SELECT_NEXT_ROW_EXTEND =
 1794                                        "selectNextRowExtendSelection";
 1795           private static final String SELECT_NEXT_ROW_CHANGE_LEAD =
 1796                                        "selectNextRowChangeLead";
 1797           private static final String SELECT_FIRST_ROW = "selectFirstRow";
 1798           private static final String SELECT_FIRST_ROW_EXTEND =
 1799                                        "selectFirstRowExtendSelection";
 1800           private static final String SELECT_FIRST_ROW_CHANGE_LEAD =
 1801                                        "selectFirstRowChangeLead";
 1802           private static final String SELECT_LAST_ROW = "selectLastRow";
 1803           private static final String SELECT_LAST_ROW_EXTEND =
 1804                                        "selectLastRowExtendSelection";
 1805           private static final String SELECT_LAST_ROW_CHANGE_LEAD =
 1806                                        "selectLastRowChangeLead";
 1807           private static final String SCROLL_UP = "scrollUp";
 1808           private static final String SCROLL_UP_EXTEND =
 1809                                        "scrollUpExtendSelection";
 1810           private static final String SCROLL_UP_CHANGE_LEAD =
 1811                                        "scrollUpChangeLead";
 1812           private static final String SCROLL_DOWN = "scrollDown";
 1813           private static final String SCROLL_DOWN_EXTEND =
 1814                                        "scrollDownExtendSelection";
 1815           private static final String SCROLL_DOWN_CHANGE_LEAD =
 1816                                        "scrollDownChangeLead";
 1817           private static final String SELECT_ALL = "selectAll";
 1818           private static final String CLEAR_SELECTION = "clearSelection";
 1819   
 1820           // add the lead item to the selection without changing lead or anchor
 1821           private static final String ADD_TO_SELECTION = "addToSelection";
 1822   
 1823           // toggle the selected state of the lead item and move the anchor to it
 1824           private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
 1825   
 1826           // extend the selection to the lead item
 1827           private static final String EXTEND_TO = "extendTo";
 1828   
 1829           // move the anchor to the lead and ensure only that item is selected
 1830           private static final String MOVE_SELECTION_TO = "moveSelectionTo";
 1831   
 1832           Actions(String name) {
 1833               super(name);
 1834           }
 1835           public void actionPerformed(ActionEvent e) {
 1836               String name = getName();
 1837               JList list = (JList)e.getSource();
 1838               BasicListUI ui = (BasicListUI)BasicLookAndFeel.getUIOfType(
 1839                        list.getUI(), BasicListUI.class);
 1840   
 1841               if (name == SELECT_PREVIOUS_COLUMN) {
 1842                   changeSelection(list, CHANGE_SELECTION,
 1843                                   getNextColumnIndex(list, ui, -1), -1);
 1844               }
 1845               else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) {
 1846                   changeSelection(list, EXTEND_SELECTION,
 1847                                   getNextColumnIndex(list, ui, -1), -1);
 1848               }
 1849               else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) {
 1850                   changeSelection(list, CHANGE_LEAD,
 1851                                   getNextColumnIndex(list, ui, -1), -1);
 1852               }
 1853               else if (name == SELECT_NEXT_COLUMN) {
 1854                   changeSelection(list, CHANGE_SELECTION,
 1855                                   getNextColumnIndex(list, ui, 1), 1);
 1856               }
 1857               else if (name == SELECT_NEXT_COLUMN_EXTEND) {
 1858                   changeSelection(list, EXTEND_SELECTION,
 1859                                   getNextColumnIndex(list, ui, 1), 1);
 1860               }
 1861               else if (name == SELECT_NEXT_COLUMN_CHANGE_LEAD) {
 1862                   changeSelection(list, CHANGE_LEAD,
 1863                                   getNextColumnIndex(list, ui, 1), 1);
 1864               }
 1865               else if (name == SELECT_PREVIOUS_ROW) {
 1866                   changeSelection(list, CHANGE_SELECTION,
 1867                                   getNextIndex(list, ui, -1), -1);
 1868               }
 1869               else if (name == SELECT_PREVIOUS_ROW_EXTEND) {
 1870                   changeSelection(list, EXTEND_SELECTION,
 1871                                   getNextIndex(list, ui, -1), -1);
 1872               }
 1873               else if (name == SELECT_PREVIOUS_ROW_CHANGE_LEAD) {
 1874                   changeSelection(list, CHANGE_LEAD,
 1875                                   getNextIndex(list, ui, -1), -1);
 1876               }
 1877               else if (name == SELECT_NEXT_ROW) {
 1878                   changeSelection(list, CHANGE_SELECTION,
 1879                                   getNextIndex(list, ui, 1), 1);
 1880               }
 1881               else if (name == SELECT_NEXT_ROW_EXTEND) {
 1882                   changeSelection(list, EXTEND_SELECTION,
 1883                                   getNextIndex(list, ui, 1), 1);
 1884               }
 1885               else if (name == SELECT_NEXT_ROW_CHANGE_LEAD) {
 1886                   changeSelection(list, CHANGE_LEAD,
 1887                                   getNextIndex(list, ui, 1), 1);
 1888               }
 1889               else if (name == SELECT_FIRST_ROW) {
 1890                   changeSelection(list, CHANGE_SELECTION, 0, -1);
 1891               }
 1892               else if (name == SELECT_FIRST_ROW_EXTEND) {
 1893                   changeSelection(list, EXTEND_SELECTION, 0, -1);
 1894               }
 1895               else if (name == SELECT_FIRST_ROW_CHANGE_LEAD) {
 1896                   changeSelection(list, CHANGE_LEAD, 0, -1);
 1897               }
 1898               else if (name == SELECT_LAST_ROW) {
 1899                   changeSelection(list, CHANGE_SELECTION,
 1900                                   list.getModel().getSize() - 1, 1);
 1901               }
 1902               else if (name == SELECT_LAST_ROW_EXTEND) {
 1903                   changeSelection(list, EXTEND_SELECTION,
 1904                                   list.getModel().getSize() - 1, 1);
 1905               }
 1906               else if (name == SELECT_LAST_ROW_CHANGE_LEAD) {
 1907                   changeSelection(list, CHANGE_LEAD,
 1908                                   list.getModel().getSize() - 1, 1);
 1909               }
 1910               else if (name == SCROLL_UP) {
 1911                   changeSelection(list, CHANGE_SELECTION,
 1912                                   getNextPageIndex(list, -1), -1);
 1913               }
 1914               else if (name == SCROLL_UP_EXTEND) {
 1915                   changeSelection(list, EXTEND_SELECTION,
 1916                                   getNextPageIndex(list, -1), -1);
 1917               }
 1918               else if (name == SCROLL_UP_CHANGE_LEAD) {
 1919                   changeSelection(list, CHANGE_LEAD,
 1920                                   getNextPageIndex(list, -1), -1);
 1921               }
 1922               else if (name == SCROLL_DOWN) {
 1923                   changeSelection(list, CHANGE_SELECTION,
 1924                                   getNextPageIndex(list, 1), 1);
 1925               }
 1926               else if (name == SCROLL_DOWN_EXTEND) {
 1927                   changeSelection(list, EXTEND_SELECTION,
 1928                                   getNextPageIndex(list, 1), 1);
 1929               }
 1930               else if (name == SCROLL_DOWN_CHANGE_LEAD) {
 1931                   changeSelection(list, CHANGE_LEAD,
 1932                                   getNextPageIndex(list, 1), 1);
 1933               }
 1934               else if (name == SELECT_ALL) {
 1935                   selectAll(list);
 1936               }
 1937               else if (name == CLEAR_SELECTION) {
 1938                   clearSelection(list);
 1939               }
 1940               else if (name == ADD_TO_SELECTION) {
 1941                   int index = adjustIndex(
 1942                       list.getSelectionModel().getLeadSelectionIndex(), list);
 1943   
 1944                   if (!list.isSelectedIndex(index)) {
 1945                       int oldAnchor = list.getSelectionModel().getAnchorSelectionIndex();
 1946                       list.setValueIsAdjusting(true);
 1947                       list.addSelectionInterval(index, index);
 1948                       list.getSelectionModel().setAnchorSelectionIndex(oldAnchor);
 1949                       list.setValueIsAdjusting(false);
 1950                   }
 1951               }
 1952               else if (name == TOGGLE_AND_ANCHOR) {
 1953                   int index = adjustIndex(
 1954                       list.getSelectionModel().getLeadSelectionIndex(), list);
 1955   
 1956                   if (list.isSelectedIndex(index)) {
 1957                       list.removeSelectionInterval(index, index);
 1958                   } else {
 1959                       list.addSelectionInterval(index, index);
 1960                   }
 1961               }
 1962               else if (name == EXTEND_TO) {
 1963                   changeSelection(
 1964                       list, EXTEND_SELECTION,
 1965                       adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list),
 1966                       0);
 1967               }
 1968               else if (name == MOVE_SELECTION_TO) {
 1969                   changeSelection(
 1970                       list, CHANGE_SELECTION,
 1971                       adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list),
 1972                       0);
 1973               }
 1974           }
 1975   
 1976           public boolean isEnabled(Object c) {
 1977               Object name = getName();
 1978               if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD ||
 1979                       name == SELECT_NEXT_COLUMN_CHANGE_LEAD ||
 1980                       name == SELECT_PREVIOUS_ROW_CHANGE_LEAD ||
 1981                       name == SELECT_NEXT_ROW_CHANGE_LEAD ||
 1982                       name == SELECT_FIRST_ROW_CHANGE_LEAD ||
 1983                       name == SELECT_LAST_ROW_CHANGE_LEAD ||
 1984                       name == SCROLL_UP_CHANGE_LEAD ||
 1985                       name == SCROLL_DOWN_CHANGE_LEAD) {
 1986   
 1987                   // discontinuous selection actions are only enabled for
 1988                   // DefaultListSelectionModel
 1989                   return c != null && ((JList)c).getSelectionModel()
 1990                                           instanceof DefaultListSelectionModel;
 1991               }
 1992   
 1993               return true;
 1994           }
 1995   
 1996           private void clearSelection(JList list) {
 1997               list.clearSelection();
 1998           }
 1999   
 2000           private void selectAll(JList list) {
 2001               int size = list.getModel().getSize();
 2002               if (size > 0) {
 2003                   ListSelectionModel lsm = list.getSelectionModel();
 2004                   int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
 2005   
 2006                   if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
 2007                       if (lead == -1) {
 2008                           int min = adjustIndex(list.getMinSelectionIndex(), list);
 2009                           lead = (min == -1 ? 0 : min);
 2010                       }
 2011   
 2012                       list.setSelectionInterval(lead, lead);
 2013                       list.ensureIndexIsVisible(lead);
 2014                   } else {
 2015                       list.setValueIsAdjusting(true);
 2016   
 2017                       int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);
 2018   
 2019                       list.setSelectionInterval(0, size - 1);
 2020   
 2021                       // this is done to restore the anchor and lead
 2022                       SwingUtilities2.setLeadAnchorWithoutSelection(lsm, anchor, lead);
 2023   
 2024                       list.setValueIsAdjusting(false);
 2025                   }
 2026               }
 2027           }
 2028   
 2029           private int getNextPageIndex(JList list, int direction) {
 2030               if (list.getModel().getSize() == 0) {
 2031                   return -1;
 2032               }
 2033   
 2034               int index = -1;
 2035               Rectangle visRect = list.getVisibleRect();
 2036               ListSelectionModel lsm = list.getSelectionModel();
 2037               int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
 2038               Rectangle leadRect =
 2039                   (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead);
 2040   
 2041               if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
 2042                   list.getVisibleRowCount() <= 0) {
 2043                   if (!list.getComponentOrientation().isLeftToRight()) {
 2044                       direction = -direction;
 2045                   }
 2046                   // apply for horizontal scrolling: the step for next
 2047                   // page index is number of visible columns
 2048                   if (direction < 0) {
 2049                       // left
 2050                       visRect.x = leadRect.x + leadRect.width - visRect.width;
 2051                       Point p = new Point(visRect.x - 1, leadRect.y);
 2052                       index = list.locationToIndex(p);
 2053                       Rectangle cellBounds = list.getCellBounds(index, index);
 2054                       if (visRect.intersects(cellBounds)) {
 2055                           p.x = cellBounds.x - 1;
 2056                           index = list.locationToIndex(p);
 2057                           cellBounds = list.getCellBounds(index, index);
 2058                       }
 2059                       // this is necessary for right-to-left orientation only
 2060                       if (cellBounds.y != leadRect.y) {
 2061                           p.x = cellBounds.x + cellBounds.width;
 2062                           index = list.locationToIndex(p);
 2063                       }
 2064                   }
 2065                   else {
 2066                       // right
 2067                       visRect.x = leadRect.x;
 2068                       Point p = new Point(visRect.x + visRect.width, leadRect.y);
 2069                       index = list.locationToIndex(p);
 2070                       Rectangle cellBounds = list.getCellBounds(index, index);
 2071                       if (visRect.intersects(cellBounds)) {
 2072                           p.x = cellBounds.x + cellBounds.width;
 2073                           index = list.locationToIndex(p);
 2074                           cellBounds = list.getCellBounds(index, index);
 2075                       }
 2076                       if (cellBounds.y != leadRect.y) {
 2077                           p.x = cellBounds.x - 1;
 2078                           index = list.locationToIndex(p);
 2079                       }
 2080                   }
 2081               }
 2082               else {
 2083                   if (direction < 0) {
 2084                       // up
 2085                       // go to the first visible cell
 2086                       Point p = new Point(leadRect.x, visRect.y);
 2087                       index = list.locationToIndex(p);
 2088                       if (lead <= index) {
 2089                           // if lead is the first visible cell (or above it)
 2090                           // adjust the visible rect up
 2091                           visRect.y = leadRect.y + leadRect.height - visRect.height;
 2092                           p.y = visRect.y;
 2093                           index = list.locationToIndex(p);
 2094                           Rectangle cellBounds = list.getCellBounds(index, index);
 2095                           // go one cell down if first visible cell doesn't fit
 2096                           // into adjasted visible rectangle
 2097                           if (cellBounds.y < visRect.y) {
 2098                               p.y = cellBounds.y + cellBounds.height;
 2099                               index = list.locationToIndex(p);
 2100                               cellBounds = list.getCellBounds(index, index);
 2101                           }
 2102                           // if index isn't less then lead
 2103                           // try to go to cell previous to lead
 2104                           if (cellBounds.y >= leadRect.y) {
 2105                               p.y = leadRect.y - 1;
 2106                               index = list.locationToIndex(p);
 2107                           }
 2108                       }
 2109                   }
 2110                   else {
 2111                       // down
 2112                       // go to the last completely visible cell
 2113                       Point p = new Point(leadRect.x,
 2114                                           visRect.y + visRect.height - 1);
 2115                       index = list.locationToIndex(p);
 2116                       Rectangle cellBounds = list.getCellBounds(index, index);
 2117                       // go up one cell if last visible cell doesn't fit
 2118                       // into visible rectangle
 2119                       if (cellBounds.y + cellBounds.height >
 2120                           visRect.y + visRect.height) {
 2121                           p.y = cellBounds.y - 1;
 2122                           index = list.locationToIndex(p);
 2123                           cellBounds = list.getCellBounds(index, index);
 2124                           index = Math.max(index, lead);
 2125                       }
 2126   
 2127                       if (lead >= index) {
 2128                           // if lead is the last completely visible index
 2129                           // (or below it) adjust the visible rect down
 2130                           visRect.y = leadRect.y;
 2131                           p.y = visRect.y + visRect.height - 1;
 2132                           index = list.locationToIndex(p);
 2133                           cellBounds = list.getCellBounds(index, index);
 2134                           // go one cell up if last visible cell doesn't fit
 2135                           // into adjasted visible rectangle
 2136                           if (cellBounds.y + cellBounds.height >
 2137                               visRect.y + visRect.height) {
 2138                               p.y = cellBounds.y - 1;
 2139                               index = list.locationToIndex(p);
 2140                               cellBounds = list.getCellBounds(index, index);
 2141                           }
 2142                           // if index isn't greater then lead
 2143                           // try to go to cell next after lead
 2144                           if (cellBounds.y <= leadRect.y) {
 2145                               p.y = leadRect.y + leadRect.height;
 2146                               index = list.locationToIndex(p);
 2147                           }
 2148                       }
 2149                   }
 2150               }
 2151               return index;
 2152           }
 2153   
 2154           private void changeSelection(JList list, int type,
 2155                                        int index, int direction) {
 2156               if (index >= 0 && index < list.getModel().getSize()) {
 2157                   ListSelectionModel lsm = list.getSelectionModel();
 2158   
 2159                   // CHANGE_LEAD is only valid with multiple interval selection
 2160                   if (type == CHANGE_LEAD &&
 2161                           list.getSelectionMode()
 2162                               != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) {
 2163   
 2164                       type = CHANGE_SELECTION;
 2165                   }
 2166   
 2167                   // IMPORTANT - This needs to happen before the index is changed.
 2168                   // This is because JFileChooser, which uses JList, also scrolls
 2169                   // the selected item into view. If that happens first, then
 2170                   // this method becomes a no-op.
 2171                   adjustScrollPositionIfNecessary(list, index, direction);
 2172   
 2173                   if (type == EXTEND_SELECTION) {
 2174                       int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list);
 2175                       if (anchor == -1) {
 2176                           anchor = 0;
 2177                       }
 2178   
 2179                       list.setSelectionInterval(anchor, index);
 2180                   }
 2181                   else if (type == CHANGE_SELECTION) {
 2182                       list.setSelectedIndex(index);
 2183                   }
 2184                   else {
 2185                       // casting should be safe since the action is only enabled
 2186                       // for DefaultListSelectionModel
 2187                       ((DefaultListSelectionModel)lsm).moveLeadSelectionIndex(index);
 2188                   }
 2189               }
 2190           }
 2191   
 2192           /**
 2193            * When scroll down makes selected index the last completely visible
 2194            * index. When scroll up makes selected index the first visible index.
 2195            * Adjust visible rectangle respect to list's component orientation.
 2196            */
 2197           private void adjustScrollPositionIfNecessary(JList list, int index,
 2198                                                        int direction) {
 2199               if (direction == 0) {
 2200                   return;
 2201               }
 2202               Rectangle cellBounds = list.getCellBounds(index, index);
 2203               Rectangle visRect = list.getVisibleRect();
 2204               if (cellBounds != null && !visRect.contains(cellBounds)) {
 2205                   if (list.getLayoutOrientation() == JList.VERTICAL_WRAP &&
 2206                       list.getVisibleRowCount() <= 0) {
 2207                       // horizontal
 2208                       if (list.getComponentOrientation().isLeftToRight()) {
 2209                           if (direction > 0) {
 2210                               // right for left-to-right
 2211                               int x =Math.max(0,
 2212                                   cellBounds.x + cellBounds.width - visRect.width);
 2213                               int startIndex =
 2214                                   list.locationToIndex(new Point(x, cellBounds.y));
 2215                               Rectangle startRect = list.getCellBounds(startIndex,
 2216                                                                        startIndex);
 2217                               if (startRect.x < x && startRect.x < cellBounds.x) {
 2218                                   startRect.x += startRect.width;
 2219                                   startIndex =
 2220                                       list.locationToIndex(startRect.getLocation());
 2221                                   startRect = list.getCellBounds(startIndex,
 2222                                                                  startIndex);
 2223                               }
 2224                               cellBounds = startRect;
 2225                           }
 2226                           cellBounds.width = visRect.width;
 2227                       }
 2228                       else {
 2229                           if (direction > 0) {
 2230                               // left for right-to-left
 2231                               int x = cellBounds.x + visRect.width;
 2232                               int rightIndex =
 2233                                   list.locationToIndex(new Point(x, cellBounds.y));
 2234                               Rectangle rightRect = list.getCellBounds(rightIndex,
 2235                                                                        rightIndex);
 2236                               if (rightRect.x + rightRect.width > x &&
 2237                                   rightRect.x > cellBounds.x) {
 2238                                   rightRect.width = 0;
 2239                               }
 2240                               cellBounds.x = Math.max(0,
 2241                                   rightRect.x + rightRect.width - visRect.width);
 2242                               cellBounds.width = visRect.width;
 2243                           }
 2244                           else {
 2245                               cellBounds.x += Math.max(0,
 2246                                   cellBounds.width - visRect.width);
 2247                               // adjust width to fit into visible rectangle
 2248                               cellBounds.width = Math.min(cellBounds.width,
 2249                                                           visRect.width);
 2250                           }
 2251                       }
 2252                   }
 2253                   else {
 2254                       // vertical
 2255                       if (direction > 0 &&
 2256                               (cellBounds.y < visRect.y ||
 2257                                       cellBounds.y + cellBounds.height
 2258                                               > visRect.y + visRect.height)) {
 2259                           //down
 2260                           int y = Math.max(0,
 2261                               cellBounds.y + cellBounds.height - visRect.height);
 2262                           int startIndex =
 2263                               list.locationToIndex(new Point(cellBounds.x, y));
 2264                           Rectangle startRect = list.getCellBounds(startIndex,
 2265                                                                    startIndex);
 2266                           if (startRect.y < y && startRect.y < cellBounds.y) {
 2267                               startRect.y += startRect.height;
 2268                               startIndex =
 2269                                   list.locationToIndex(startRect.getLocation());
 2270                               startRect =
 2271                                   list.getCellBounds(startIndex, startIndex);
 2272                           }
 2273                           cellBounds = startRect;
 2274                           cellBounds.height = visRect.height;
 2275                       }
 2276                       else {
 2277                           // adjust height to fit into visible rectangle
 2278                           cellBounds.height = Math.min(cellBounds.height, visRect.height);
 2279                       }
 2280                   }
 2281                   list.scrollRectToVisible(cellBounds);
 2282               }
 2283           }
 2284   
 2285           private int getNextColumnIndex(JList list, BasicListUI ui,
 2286                                          int amount) {
 2287               if (list.getLayoutOrientation() != JList.VERTICAL) {
 2288                   int index = adjustIndex(list.getLeadSelectionIndex(), list);
 2289                   int size = list.getModel().getSize();
 2290   
 2291                   if (index == -1) {
 2292                       return 0;
 2293                   } else if (size == 1) {
 2294                       // there's only one item so we should select it
 2295                       return 0;
 2296                   } else if (ui == null || ui.columnCount <= 1) {
 2297                       return -1;
 2298                   }
 2299   
 2300                   int column = ui.convertModelToColumn(index);
 2301                   int row = ui.convertModelToRow(index);
 2302   
 2303                   column += amount;
 2304                   if (column >= ui.columnCount || column < 0) {
 2305                       // No wrapping.
 2306                       return -1;
 2307                   }
 2308                   int maxRowCount = ui.getRowCount(column);
 2309                   if (row >= maxRowCount) {
 2310                       return -1;
 2311                   }
 2312                   return ui.getModelIndex(column, row);
 2313               }
 2314               // Won't change the selection.
 2315               return -1;
 2316           }
 2317   
 2318           private int getNextIndex(JList list, BasicListUI ui, int amount) {
 2319               int index = adjustIndex(list.getLeadSelectionIndex(), list);
 2320               int size = list.getModel().getSize();
 2321   
 2322               if (index == -1) {
 2323                   if (size > 0) {
 2324                       if (amount > 0) {
 2325                           index = 0;
 2326                       }
 2327                       else {
 2328                           index = size - 1;
 2329                       }
 2330                   }
 2331               } else if (size == 1) {
 2332                   // there's only one item so we should select it
 2333                   index = 0;
 2334               } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
 2335                   if (ui != null) {
 2336                       index += ui.columnCount * amount;
 2337                   }
 2338               } else {
 2339                   index += amount;
 2340               }
 2341   
 2342               return index;
 2343           }
 2344       }
 2345   
 2346   
 2347       private class Handler implements FocusListener, KeyListener,
 2348                             ListDataListener, ListSelectionListener,
 2349                             MouseInputListener, PropertyChangeListener,
 2350                             BeforeDrag {
 2351           //
 2352           // KeyListener
 2353           //
 2354           private String prefix = "";
 2355           private String typedString = "";
 2356           private long lastTime = 0L;
 2357   
 2358           /**
 2359            * Invoked when a key has been typed.
 2360            *
 2361            * Moves the keyboard focus to the first element whose prefix matches the
 2362            * sequence of alphanumeric keys pressed by the user with delay less
 2363            * than value of <code>timeFactor</code> property (or 1000 milliseconds
 2364            * if it is not defined). Subsequent same key presses move the keyboard
 2365            * focus to the next object that starts with the same letter until another
 2366            * key is pressed, then it is treated as the prefix with appropriate number
 2367            * of the same letters followed by first typed another letter.
 2368            */
 2369           public void keyTyped(KeyEvent e) {
 2370               JList src = (JList)e.getSource();
 2371               ListModel model = src.getModel();
 2372   
 2373               if (model.getSize() == 0 || e.isAltDown() ||
 2374                       BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
 2375                       isNavigationKey(e)) {
 2376                   // Nothing to select
 2377                   return;
 2378               }
 2379               boolean startingFromSelection = true;
 2380   
 2381               char c = e.getKeyChar();
 2382   
 2383               long time = e.getWhen();
 2384               int startIndex = adjustIndex(src.getLeadSelectionIndex(), list);
 2385               if (time - lastTime < timeFactor) {
 2386                   typedString += c;
 2387                   if((prefix.length() == 1) && (c == prefix.charAt(0))) {
 2388                       // Subsequent same key presses move the keyboard focus to the next
 2389                       // object that starts with the same letter.
 2390                       startIndex++;
 2391                   } else {
 2392                       prefix = typedString;
 2393                   }
 2394               } else {
 2395                   startIndex++;
 2396                   typedString = "" + c;
 2397                   prefix = typedString;
 2398               }
 2399               lastTime = time;
 2400   
 2401               if (startIndex < 0 || startIndex >= model.getSize()) {
 2402                   startingFromSelection = false;
 2403                   startIndex = 0;
 2404               }
 2405               int index = src.getNextMatch(prefix, startIndex,
 2406                                            Position.Bias.Forward);
 2407               if (index >= 0) {
 2408                   src.setSelectedIndex(index);
 2409                   src.ensureIndexIsVisible(index);
 2410               } else if (startingFromSelection) { // wrap
 2411                   index = src.getNextMatch(prefix, 0,
 2412                                            Position.Bias.Forward);
 2413                   if (index >= 0) {
 2414                       src.setSelectedIndex(index);
 2415                       src.ensureIndexIsVisible(index);
 2416                   }
 2417               }
 2418           }
 2419   
 2420           /**
 2421            * Invoked when a key has been pressed.
 2422            *
 2423            * Checks to see if the key event is a navigation key to prevent
 2424            * dispatching these keys for the first letter navigation.
 2425            */
 2426           public void keyPressed(KeyEvent e) {
 2427               if ( isNavigationKey(e) ) {
 2428                   prefix = "";
 2429                   typedString = "";
 2430                   lastTime = 0L;
 2431               }
 2432           }
 2433   
 2434           /**
 2435            * Invoked when a key has been released.
 2436            * See the class description for {@link KeyEvent} for a definition of
 2437            * a key released event.
 2438            */
 2439           public void keyReleased(KeyEvent e) {
 2440           }
 2441   
 2442           /**
 2443            * Returns whether or not the supplied key event maps to a key that is used for
 2444            * navigation.  This is used for optimizing key input by only passing non-
 2445            * navigation keys to the first letter navigation mechanism.
 2446            */
 2447           private boolean isNavigationKey(KeyEvent event) {
 2448               InputMap inputMap = list.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 2449               KeyStroke key = KeyStroke.getKeyStrokeForEvent(event);
 2450   
 2451               if (inputMap != null && inputMap.get(key) != null) {
 2452                   return true;
 2453               }
 2454               return false;
 2455           }
 2456   
 2457           //
 2458           // PropertyChangeListener
 2459           //
 2460           public void propertyChange(PropertyChangeEvent e) {
 2461               String propertyName = e.getPropertyName();
 2462   
 2463               /* If the JList.model property changes, remove our listener,
 2464                * listDataListener from the old model and add it to the new one.
 2465                */
 2466               if (propertyName == "model") {
 2467                   ListModel oldModel = (ListModel)e.getOldValue();
 2468                   ListModel newModel = (ListModel)e.getNewValue();
 2469                   if (oldModel != null) {
 2470                       oldModel.removeListDataListener(listDataListener);
 2471                   }
 2472                   if (newModel != null) {
 2473                       newModel.addListDataListener(listDataListener);
 2474                   }
 2475                   updateLayoutStateNeeded |= modelChanged;
 2476                   redrawList();
 2477               }
 2478   
 2479               /* If the JList.selectionModel property changes, remove our listener,
 2480                * listSelectionListener from the old selectionModel and add it to the new one.
 2481                */
 2482               else if (propertyName == "selectionModel") {
 2483                   ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
 2484                   ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
 2485                   if (oldModel != null) {
 2486                       oldModel.removeListSelectionListener(listSelectionListener);
 2487                   }
 2488                   if (newModel != null) {
 2489                       newModel.addListSelectionListener(listSelectionListener);
 2490                   }
 2491                   updateLayoutStateNeeded |= modelChanged;
 2492                   redrawList();
 2493               }
 2494               else if (propertyName == "cellRenderer") {
 2495                   updateLayoutStateNeeded |= cellRendererChanged;
 2496                   redrawList();
 2497               }
 2498               else if (propertyName == "font") {
 2499                   updateLayoutStateNeeded |= fontChanged;
 2500                   redrawList();
 2501               }
 2502               else if (propertyName == "prototypeCellValue") {
 2503                   updateLayoutStateNeeded |= prototypeCellValueChanged;
 2504                   redrawList();
 2505               }
 2506               else if (propertyName == "fixedCellHeight") {
 2507                   updateLayoutStateNeeded |= fixedCellHeightChanged;
 2508                   redrawList();
 2509               }
 2510               else if (propertyName == "fixedCellWidth") {
 2511                   updateLayoutStateNeeded |= fixedCellWidthChanged;
 2512                   redrawList();
 2513               }
 2514               else if (propertyName == "selectionForeground") {
 2515                   list.repaint();
 2516               }
 2517               else if (propertyName == "selectionBackground") {
 2518                   list.repaint();
 2519               }
 2520               else if ("layoutOrientation" == propertyName) {
 2521                   updateLayoutStateNeeded |= layoutOrientationChanged;
 2522                   layoutOrientation = list.getLayoutOrientation();
 2523                   redrawList();
 2524               }
 2525               else if ("visibleRowCount" == propertyName) {
 2526                   if (layoutOrientation != JList.VERTICAL) {
 2527                       updateLayoutStateNeeded |= layoutOrientationChanged;
 2528                       redrawList();
 2529                   }
 2530               }
 2531               else if ("componentOrientation" == propertyName) {
 2532                   isLeftToRight = list.getComponentOrientation().isLeftToRight();
 2533                   updateLayoutStateNeeded |= componentOrientationChanged;
 2534                   redrawList();
 2535   
 2536                   InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
 2537                   SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
 2538                                                    inputMap);
 2539               } else if ("List.isFileList" == propertyName) {
 2540                   updateIsFileList();
 2541                   redrawList();
 2542               } else if ("dropLocation" == propertyName) {
 2543                   JList.DropLocation oldValue = (JList.DropLocation)e.getOldValue();
 2544                   repaintDropLocation(oldValue);
 2545                   repaintDropLocation(list.getDropLocation());
 2546               }
 2547           }
 2548   
 2549           private void repaintDropLocation(JList.DropLocation loc) {
 2550               if (loc == null) {
 2551                   return;
 2552               }
 2553   
 2554               Rectangle r;
 2555   
 2556               if (loc.isInsert()) {
 2557                   r = getDropLineRect(loc);
 2558               } else {
 2559                   r = getCellBounds(list, loc.getIndex());
 2560               }
 2561   
 2562               if (r != null) {
 2563                   list.repaint(r);
 2564               }
 2565           }
 2566   
 2567           //
 2568           // ListDataListener
 2569           //
 2570           public void intervalAdded(ListDataEvent e) {
 2571               updateLayoutStateNeeded = modelChanged;
 2572   
 2573               int minIndex = Math.min(e.getIndex0(), e.getIndex1());
 2574               int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
 2575   
 2576               /* Sync the SelectionModel with the DataModel.
 2577                */
 2578   
 2579               ListSelectionModel sm = list.getSelectionModel();
 2580               if (sm != null) {
 2581                   sm.insertIndexInterval(minIndex, maxIndex - minIndex+1, true);
 2582               }
 2583   
 2584               /* Repaint the entire list, from the origin of
 2585                * the first added cell, to the bottom of the
 2586                * component.
 2587                */
 2588               redrawList();
 2589           }
 2590   
 2591   
 2592           public void intervalRemoved(ListDataEvent e)
 2593           {
 2594               updateLayoutStateNeeded = modelChanged;
 2595   
 2596               /* Sync the SelectionModel with the DataModel.
 2597                */
 2598   
 2599               ListSelectionModel sm = list.getSelectionModel();
 2600               if (sm != null) {
 2601                   sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
 2602               }
 2603   
 2604               /* Repaint the entire list, from the origin of
 2605                * the first removed cell, to the bottom of the
 2606                * component.
 2607                */
 2608   
 2609               redrawList();
 2610           }
 2611   
 2612   
 2613           public void contentsChanged(ListDataEvent e) {
 2614               updateLayoutStateNeeded = modelChanged;
 2615               redrawList();
 2616           }
 2617   
 2618   
 2619           //
 2620           // ListSelectionListener
 2621           //
 2622           public void valueChanged(ListSelectionEvent e) {
 2623               maybeUpdateLayoutState();
 2624   
 2625               int size = list.getModel().getSize();
 2626               int firstIndex = Math.min(size - 1, Math.max(e.getFirstIndex(), 0));
 2627               int lastIndex = Math.min(size - 1, Math.max(e.getLastIndex(), 0));
 2628   
 2629               Rectangle bounds = getCellBounds(list, firstIndex, lastIndex);
 2630   
 2631               if (bounds != null) {
 2632                   list.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
 2633               }
 2634           }
 2635   
 2636           //
 2637           // MouseListener
 2638           //
 2639           public void mouseClicked(MouseEvent e) {
 2640           }
 2641   
 2642           public void mouseEntered(MouseEvent e) {
 2643           }
 2644   
 2645           public void mouseExited(MouseEvent e) {
 2646           }
 2647   
 2648           // Whether or not the mouse press (which is being considered as part
 2649           // of a drag sequence) also caused the selection change to be fully
 2650           // processed.
 2651           private boolean dragPressDidSelection;
 2652   
 2653           public void mousePressed(MouseEvent e) {
 2654               if (SwingUtilities2.shouldIgnore(e, list)) {
 2655                   return;
 2656               }
 2657   
 2658               boolean dragEnabled = list.getDragEnabled();
 2659               boolean grabFocus = true;
 2660   
 2661               // different behavior if drag is enabled
 2662               if (dragEnabled) {
 2663                   int row = SwingUtilities2.loc2IndexFileList(list, e.getPoint());
 2664                   // if we have a valid row and this is a drag initiating event
 2665                   if (row != -1 && DragRecognitionSupport.mousePressed(e)) {
 2666                       dragPressDidSelection = false;
 2667   
 2668                       if (BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
 2669                           // do nothing for control - will be handled on release
 2670                           // or when drag starts
 2671                           return;
 2672                       } else if (!e.isShiftDown() && list.isSelectedIndex(row)) {
 2673                           // clicking on something that's already selected
 2674                           // and need to make it the lead now
 2675                           list.addSelectionInterval(row, row);
 2676                           return;
 2677                       }
 2678   
 2679                       // could be a drag initiating event - don't grab focus
 2680                       grabFocus = false;
 2681   
 2682                       dragPressDidSelection = true;
 2683                   }
 2684               } else {
 2685                   // When drag is enabled mouse drags won't change the selection
 2686                   // in the list, so we only set the isAdjusting flag when it's
 2687                   // not enabled
 2688                   list.setValueIsAdjusting(true);
 2689               }
 2690   
 2691               if (grabFocus) {
 2692                   SwingUtilities2.adjustFocus(list);
 2693               }
 2694   
 2695               adjustSelection(e);
 2696           }
 2697   
 2698           private void adjustSelection(MouseEvent e) {
 2699               int row = SwingUtilities2.loc2IndexFileList(list, e.getPoint());
 2700               if (row < 0) {
 2701                   // If shift is down in multi-select, we should do nothing.
 2702                   // For single select or non-shift-click, clear the selection
 2703                   if (isFileList &&
 2704                       e.getID() == MouseEvent.MOUSE_PRESSED &&
 2705                       (!e.isShiftDown() ||
 2706                        list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
 2707                       list.clearSelection();
 2708                   }
 2709               }
 2710               else {
 2711                   int anchorIndex = adjustIndex(list.getAnchorSelectionIndex(), list);
 2712                   boolean anchorSelected;
 2713                   if (anchorIndex == -1) {
 2714                       anchorIndex = 0;
 2715                       anchorSelected = false;
 2716                   } else {
 2717                       anchorSelected = list.isSelectedIndex(anchorIndex);
 2718                   }
 2719   
 2720                   if (BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
 2721                       if (e.isShiftDown()) {
 2722                           if (anchorSelected) {
 2723                               list.addSelectionInterval(anchorIndex, row);
 2724                           } else {
 2725                               list.removeSelectionInterval(anchorIndex, row);
 2726                               if (isFileList) {
 2727                                   list.addSelectionInterval(row, row);
 2728                                   list.getSelectionModel().setAnchorSelectionIndex(anchorIndex);
 2729                               }
 2730                           }
 2731                       } else if (list.isSelectedIndex(row)) {
 2732                           list.removeSelectionInterval(row, row);
 2733                       } else {
 2734                           list.addSelectionInterval(row, row);
 2735                       }
 2736                   } else if (e.isShiftDown()) {
 2737                       list.setSelectionInterval(anchorIndex, row);
 2738                   } else {
 2739                       list.setSelectionInterval(row, row);
 2740                   }
 2741               }
 2742           }
 2743   
 2744           public void dragStarting(MouseEvent me) {
 2745               if (BasicGraphicsUtils.isMenuShortcutKeyDown(me)) {
 2746                   int row = SwingUtilities2.loc2IndexFileList(list, me.getPoint());
 2747                   list.addSelectionInterval(row, row);
 2748               }
 2749           }
 2750   
 2751           public void mouseDragged(MouseEvent e) {
 2752               if (SwingUtilities2.shouldIgnore(e, list)) {
 2753                   return;
 2754               }
 2755   
 2756               if (list.getDragEnabled()) {
 2757                   DragRecognitionSupport.mouseDragged(e, this);
 2758                   return;
 2759               }
 2760   
 2761               if (e.isShiftDown() || BasicGraphicsUtils.isMenuShortcutKeyDown(e)) {
 2762                   return;
 2763               }
 2764   
 2765               int row = locationToIndex(list, e.getPoint());
 2766               if (row != -1) {
 2767                   // 4835633.  Dragging onto a File should not select it.
 2768                   if (isFileList) {
 2769                       return;
 2770                   }
 2771                   Rectangle cellBounds = getCellBounds(list, row, row);
 2772                   if (cellBounds != null) {
 2773                       list.scrollRectToVisible(cellBounds);
 2774                       list.setSelectionInterval(row, row);
 2775                   }
 2776               }
 2777           }
 2778   
 2779           public void mouseMoved(MouseEvent e) {
 2780           }
 2781   
 2782           public void mouseReleased(MouseEvent e) {
 2783               if (SwingUtilities2.shouldIgnore(e, list)) {
 2784                   return;
 2785               }
 2786   
 2787               if (list.getDragEnabled()) {
 2788                   MouseEvent me = DragRecognitionSupport.mouseReleased(e);
 2789                   if (me != null) {
 2790                       SwingUtilities2.adjustFocus(list);
 2791                       if (!dragPressDidSelection) {
 2792                           adjustSelection(me);
 2793                       }
 2794                   }
 2795               } else {
 2796                   list.setValueIsAdjusting(false);
 2797               }
 2798           }
 2799   
 2800           //
 2801           // FocusListener
 2802           //
 2803           protected void repaintCellFocus()
 2804           {
 2805               int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list);
 2806               if (leadIndex != -1) {
 2807                   Rectangle r = getCellBounds(list, leadIndex, leadIndex);
 2808                   if (r != null) {
 2809                       list.repaint(r.x, r.y, r.width, r.height);
 2810                   }
 2811               }
 2812           }
 2813   
 2814           /* The focusGained() focusLost() methods run when the JList
 2815            * focus changes.
 2816            */
 2817   
 2818           public void focusGained(FocusEvent e) {
 2819               repaintCellFocus();
 2820           }
 2821   
 2822           public void focusLost(FocusEvent e) {
 2823               repaintCellFocus();
 2824           }
 2825       }
 2826   
 2827       private static int adjustIndex(int index, JList list) {
 2828           return index < list.getModel().getSize() ? index : -1;
 2829       }
 2830   
 2831       private static final TransferHandler defaultTransferHandler = new ListTransferHandler();
 2832   
 2833       static class ListTransferHandler extends TransferHandler implements UIResource {
 2834   
 2835           /**
 2836            * Create a Transferable to use as the source for a data transfer.
 2837            *
 2838            * @param c  The component holding the data to be transfered.  This
 2839            *  argument is provided to enable sharing of TransferHandlers by
 2840            *  multiple components.
 2841            * @return  The representation of the data to be transfered.
 2842            *
 2843            */
 2844           protected Transferable createTransferable(JComponent c) {
 2845               if (c instanceof JList) {
 2846                   JList list = (JList) c;
 2847                   Object[] values = list.getSelectedValues();
 2848   
 2849                   if (values == null || values.length == 0) {
 2850                       return null;
 2851                   }
 2852   
 2853                   StringBuffer plainBuf = new StringBuffer();
 2854                   StringBuffer htmlBuf = new StringBuffer();
 2855   
 2856                   htmlBuf.append("<html>\n<body>\n<ul>\n");
 2857   
 2858                   for (int i = 0; i < values.length; i++) {
 2859                       Object obj = values[i];
 2860                       String val = ((obj == null) ? "" : obj.toString());
 2861                       plainBuf.append(val + "\n");
 2862                       htmlBuf.append("  <li>" + val + "\n");
 2863                   }
 2864   
 2865                   // remove the last newline
 2866                   plainBuf.deleteCharAt(plainBuf.length() - 1);
 2867                   htmlBuf.append("</ul>\n</body>\n</html>");
 2868   
 2869                   return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());
 2870               }
 2871   
 2872               return null;
 2873           }
 2874   
 2875           public int getSourceActions(JComponent c) {
 2876               return COPY;
 2877           }
 2878   
 2879       }
 2880   }

Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]