Source Code Cross Referenced for BasicListUI.java in  » 6.0-JDK-Core » swing » javax » swing » plaf » basic » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.plaf.basic 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        package javax.swing.plaf.basic;
0027
0028        import sun.swing.DefaultLookup;
0029        import sun.swing.UIAction;
0030
0031        import javax.swing.*;
0032        import javax.swing.event.*;
0033        import javax.swing.plaf.*;
0034        import javax.swing.text.Position;
0035
0036        import java.awt.*;
0037        import java.awt.event.*;
0038        import java.awt.datatransfer.Transferable;
0039        import java.awt.geom.Point2D;
0040
0041        import java.beans.PropertyChangeListener;
0042        import java.beans.PropertyChangeEvent;
0043
0044        import sun.swing.SwingUtilities2;
0045        import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
0046
0047        /**
0048         * An extensible implementation of {@code ListUI}.
0049         * <p>
0050         * {@code BasicListUI} instances cannot be shared between multiple
0051         * lists.
0052         *
0053         * @version 1.130 05/09/07
0054         * @author Hans Muller
0055         * @author Philip Milne
0056         * @author Shannon Hickey (drag and drop)
0057         */
0058        public class BasicListUI extends ListUI {
0059            private static final StringBuilder BASELINE_COMPONENT_KEY = new StringBuilder(
0060                    "List.baselineComponent");
0061
0062            protected JList list = null;
0063            protected CellRendererPane rendererPane;
0064
0065            // Listeners that this UI attaches to the JList
0066            protected FocusListener focusListener;
0067            protected MouseInputListener mouseInputListener;
0068            protected ListSelectionListener listSelectionListener;
0069            protected ListDataListener listDataListener;
0070            protected PropertyChangeListener propertyChangeListener;
0071            private Handler handler;
0072
0073            protected int[] cellHeights = null;
0074            protected int cellHeight = -1;
0075            protected int cellWidth = -1;
0076            protected int updateLayoutStateNeeded = modelChanged;
0077            /**
0078             * Height of the list. When asked to paint, if the current size of
0079             * the list differs, this will update the layout state.
0080             */
0081            private int listHeight;
0082
0083            /**
0084             * Width of the list. When asked to paint, if the current size of
0085             * the list differs, this will update the layout state.
0086             */
0087            private int listWidth;
0088
0089            /**
0090             * The layout orientation of the list.
0091             */
0092            private int layoutOrientation;
0093
0094            // Following ivars are used if the list is laying out horizontally
0095
0096            /**
0097             * Number of columns to create.
0098             */
0099            private int columnCount;
0100            /**
0101             * Preferred height to make the list, this is only used if the 
0102             * the list is layed out horizontally.
0103             */
0104            private int preferredHeight;
0105            /**
0106             * Number of rows per column. This is only used if the row height is
0107             * fixed.
0108             */
0109            private int rowsPerColumn;
0110
0111            /**
0112             * The time factor to treate the series of typed alphanumeric key
0113             * as prefix for first letter navigation.
0114             */
0115            private long timeFactor = 1000L;
0116
0117            /**
0118             * Local cache of JList's client property "List.isFileList"
0119             */
0120            private boolean isFileList = false;
0121
0122            /**
0123             * Local cache of JList's component orientation property
0124             */
0125            private boolean isLeftToRight = true;
0126
0127            /* The bits below define JList property changes that affect layout.
0128             * When one of these properties changes we set a bit in
0129             * updateLayoutStateNeeded.  The change is dealt with lazily, see
0130             * maybeUpdateLayoutState.  Changes to the JLists model, e.g. the
0131             * models length changed, are handled similarly, see DataListener.
0132             */
0133
0134            protected final static int modelChanged = 1 << 0;
0135            protected final static int selectionModelChanged = 1 << 1;
0136            protected final static int fontChanged = 1 << 2;
0137            protected final static int fixedCellWidthChanged = 1 << 3;
0138            protected final static int fixedCellHeightChanged = 1 << 4;
0139            protected final static int prototypeCellValueChanged = 1 << 5;
0140            protected final static int cellRendererChanged = 1 << 6;
0141            private final static int layoutOrientationChanged = 1 << 7;
0142            private final static int heightChanged = 1 << 8;
0143            private final static int widthChanged = 1 << 9;
0144            private final static int componentOrientationChanged = 1 << 10;
0145
0146            private static final int DROP_LINE_THICKNESS = 2;
0147
0148            static void loadActionMap(LazyActionMap map) {
0149                map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN));
0150                map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND));
0151                map
0152                        .put(new Actions(
0153                                Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD));
0154                map.put(new Actions(Actions.SELECT_NEXT_COLUMN));
0155                map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND));
0156                map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD));
0157                map.put(new Actions(Actions.SELECT_PREVIOUS_ROW));
0158                map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND));
0159                map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD));
0160                map.put(new Actions(Actions.SELECT_NEXT_ROW));
0161                map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND));
0162                map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD));
0163                map.put(new Actions(Actions.SELECT_FIRST_ROW));
0164                map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND));
0165                map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD));
0166                map.put(new Actions(Actions.SELECT_LAST_ROW));
0167                map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND));
0168                map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD));
0169                map.put(new Actions(Actions.SCROLL_UP));
0170                map.put(new Actions(Actions.SCROLL_UP_EXTEND));
0171                map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD));
0172                map.put(new Actions(Actions.SCROLL_DOWN));
0173                map.put(new Actions(Actions.SCROLL_DOWN_EXTEND));
0174                map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD));
0175                map.put(new Actions(Actions.SELECT_ALL));
0176                map.put(new Actions(Actions.CLEAR_SELECTION));
0177                map.put(new Actions(Actions.ADD_TO_SELECTION));
0178                map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
0179                map.put(new Actions(Actions.EXTEND_TO));
0180                map.put(new Actions(Actions.MOVE_SELECTION_TO));
0181
0182                map.put(TransferHandler.getCutAction().getValue(Action.NAME),
0183                        TransferHandler.getCutAction());
0184                map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
0185                        TransferHandler.getCopyAction());
0186                map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
0187                        TransferHandler.getPasteAction());
0188            }
0189
0190            /**
0191             * Paint one List cell: compute the relevant state, get the "rubber stamp"
0192             * cell renderer component, and then use the CellRendererPane to paint it.
0193             * Subclasses may want to override this method rather than paint().
0194             *
0195             * @see #paint
0196             */
0197            protected void paintCell(Graphics g, int row, Rectangle rowBounds,
0198                    ListCellRenderer cellRenderer, ListModel dataModel,
0199                    ListSelectionModel selModel, int leadIndex) {
0200                Object value = dataModel.getElementAt(row);
0201                boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
0202                boolean isSelected = selModel.isSelectedIndex(row);
0203
0204                Component rendererComponent = cellRenderer
0205                        .getListCellRendererComponent(list, value, row,
0206                                isSelected, cellHasFocus);
0207
0208                int cx = rowBounds.x;
0209                int cy = rowBounds.y;
0210                int cw = rowBounds.width;
0211                int ch = rowBounds.height;
0212
0213                if (isFileList) {
0214                    // Shrink renderer to preferred size. This is mostly used on Windows
0215                    // where selection is only shown around the file name, instead of
0216                    // across the whole list cell.
0217                    int w = Math.min(cw,
0218                            rendererComponent.getPreferredSize().width + 4);
0219                    if (!isLeftToRight) {
0220                        cx += (cw - w);
0221                    }
0222                    cw = w;
0223                }
0224
0225                rendererPane.paintComponent(g, rendererComponent, list, cx, cy,
0226                        cw, ch, true);
0227            }
0228
0229            /**
0230             * Paint the rows that intersect the Graphics objects clipRect.  This
0231             * method calls paintCell as necessary.  Subclasses
0232             * may want to override these methods.
0233             *
0234             * @see #paintCell
0235             */
0236            public void paint(Graphics g, JComponent c) {
0237                Shape clip = g.getClip();
0238                paintImpl(g, c);
0239                g.setClip(clip);
0240
0241                paintDropLine(g);
0242            }
0243
0244            private void paintImpl(Graphics g, JComponent c) {
0245                switch (layoutOrientation) {
0246                case JList.VERTICAL_WRAP:
0247                    if (list.getHeight() != listHeight) {
0248                        updateLayoutStateNeeded |= heightChanged;
0249                        redrawList();
0250                    }
0251                    break;
0252                case JList.HORIZONTAL_WRAP:
0253                    if (list.getWidth() != listWidth) {
0254                        updateLayoutStateNeeded |= widthChanged;
0255                        redrawList();
0256                    }
0257                    break;
0258                default:
0259                    break;
0260                }
0261                maybeUpdateLayoutState();
0262
0263                ListCellRenderer renderer = list.getCellRenderer();
0264                ListModel dataModel = list.getModel();
0265                ListSelectionModel selModel = list.getSelectionModel();
0266                int size;
0267
0268                if ((renderer == null) || (size = dataModel.getSize()) == 0) {
0269                    return;
0270                }
0271
0272                // Determine how many columns we need to paint
0273                Rectangle paintBounds = g.getClipBounds();
0274
0275                int startColumn, endColumn;
0276                if (c.getComponentOrientation().isLeftToRight()) {
0277                    startColumn = convertLocationToColumn(paintBounds.x,
0278                            paintBounds.y);
0279                    endColumn = convertLocationToColumn(paintBounds.x
0280                            + paintBounds.width, paintBounds.y);
0281                } else {
0282                    startColumn = convertLocationToColumn(paintBounds.x
0283                            + paintBounds.width, paintBounds.y);
0284                    endColumn = convertLocationToColumn(paintBounds.x,
0285                            paintBounds.y);
0286                }
0287                int maxY = paintBounds.y + paintBounds.height;
0288                int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list);
0289                int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ? columnCount
0290                        : 1;
0291
0292                for (int colCounter = startColumn; colCounter <= endColumn; colCounter++) {
0293                    // And then how many rows in this columnn
0294                    int row = convertLocationToRowInColumn(paintBounds.y,
0295                            colCounter);
0296                    int rowCount = getRowCount(colCounter);
0297                    int index = getModelIndex(colCounter, row);
0298                    Rectangle rowBounds = getCellBounds(list, index, index);
0299
0300                    if (rowBounds == null) {
0301                        // Not valid, bail!
0302                        return;
0303                    }
0304                    while (row < rowCount && rowBounds.y < maxY && index < size) {
0305                        rowBounds.height = getHeight(colCounter, row);
0306                        g.setClip(rowBounds.x, rowBounds.y, rowBounds.width,
0307                                rowBounds.height);
0308                        g.clipRect(paintBounds.x, paintBounds.y,
0309                                paintBounds.width, paintBounds.height);
0310                        paintCell(g, index, rowBounds, renderer, dataModel,
0311                                selModel, leadIndex);
0312                        rowBounds.y += rowBounds.height;
0313                        index += rowIncrement;
0314                        row++;
0315                    }
0316                }
0317                // Empty out the renderer pane, allowing renderers to be gc'ed.
0318                rendererPane.removeAll();
0319            }
0320
0321            private void paintDropLine(Graphics g) {
0322                JList.DropLocation loc = list.getDropLocation();
0323                if (loc == null || !loc.isInsert()) {
0324                    return;
0325                }
0326
0327                Color c = DefaultLookup.getColor(list, this ,
0328                        "List.dropLineColor", null);
0329                if (c != null) {
0330                    g.setColor(c);
0331                    Rectangle rect = getDropLineRect(loc);
0332                    g.fillRect(rect.x, rect.y, rect.width, rect.height);
0333                }
0334            }
0335
0336            private Rectangle getDropLineRect(JList.DropLocation loc) {
0337                int size = list.getModel().getSize();
0338
0339                if (size == 0) {
0340                    Insets insets = list.getInsets();
0341                    if (layoutOrientation == JList.HORIZONTAL_WRAP) {
0342                        if (isLeftToRight) {
0343                            return new Rectangle(insets.left, insets.top,
0344                                    DROP_LINE_THICKNESS, 20);
0345                        } else {
0346                            return new Rectangle(list.getWidth()
0347                                    - DROP_LINE_THICKNESS - insets.right,
0348                                    insets.top, DROP_LINE_THICKNESS, 20);
0349                        }
0350                    } else {
0351                        return new Rectangle(insets.left, insets.top, list
0352                                .getWidth()
0353                                - insets.left - insets.right,
0354                                DROP_LINE_THICKNESS);
0355                    }
0356                }
0357
0358                Rectangle rect = null;
0359                int index = loc.getIndex();
0360                boolean decr = false;
0361
0362                if (layoutOrientation == JList.HORIZONTAL_WRAP) {
0363                    if (index == size) {
0364                        decr = true;
0365                    } else if (index != 0
0366                            && convertModelToRow(index) != convertModelToRow(index - 1)) {
0367
0368                        Rectangle prev = getCellBounds(list, index - 1);
0369                        Rectangle me = getCellBounds(list, index);
0370                        Point p = loc.getDropPoint();
0371
0372                        if (isLeftToRight) {
0373                            decr = Point2D.distance(prev.x + prev.width, prev.y
0374                                    + (int) (prev.height / 2.0), p.x, p.y) < Point2D
0375                                    .distance(me.x, me.y
0376                                            + (int) (me.height / 2.0), p.x, p.y);
0377                        } else {
0378                            decr = Point2D.distance(prev.x, prev.y
0379                                    + (int) (prev.height / 2.0), p.x, p.y) < Point2D
0380                                    .distance(me.x + me.width, me.y
0381                                            + (int) (prev.height / 2.0), p.x,
0382                                            p.y);
0383                        }
0384                    }
0385
0386                    if (decr) {
0387                        index--;
0388                        rect = getCellBounds(list, index);
0389                        if (isLeftToRight) {
0390                            rect.x += rect.width;
0391                        } else {
0392                            rect.x -= DROP_LINE_THICKNESS;
0393                        }
0394                    } else {
0395                        rect = getCellBounds(list, index);
0396                        if (!isLeftToRight) {
0397                            rect.x += rect.width - DROP_LINE_THICKNESS;
0398                        }
0399                    }
0400
0401                    if (rect.x >= list.getWidth()) {
0402                        rect.x = list.getWidth() - DROP_LINE_THICKNESS;
0403                    } else if (rect.x < 0) {
0404                        rect.x = 0;
0405                    }
0406
0407                    rect.width = DROP_LINE_THICKNESS;
0408                } else if (layoutOrientation == JList.VERTICAL_WRAP) {
0409                    if (index == size) {
0410                        index--;
0411                        rect = getCellBounds(list, index);
0412                        rect.y += rect.height;
0413                    } else if (index != 0
0414                            && convertModelToColumn(index) != convertModelToColumn(index - 1)) {
0415
0416                        Rectangle prev = getCellBounds(list, index - 1);
0417                        Rectangle me = getCellBounds(list, index);
0418                        Point p = loc.getDropPoint();
0419                        if (Point2D.distance(prev.x + (int) (prev.width / 2.0),
0420                                prev.y + prev.height, p.x, p.y) < Point2D
0421                                .distance(me.x + (int) (me.width / 2.0), me.y,
0422                                        p.x, p.y)) {
0423
0424                            index--;
0425                            rect = getCellBounds(list, index);
0426                            rect.y += rect.height;
0427                        } else {
0428                            rect = getCellBounds(list, index);
0429                        }
0430                    } else {
0431                        rect = getCellBounds(list, index);
0432                    }
0433
0434                    if (rect.y >= list.getHeight()) {
0435                        rect.y = list.getHeight() - DROP_LINE_THICKNESS;
0436                    }
0437
0438                    rect.height = DROP_LINE_THICKNESS;
0439                } else {
0440                    if (index == size) {
0441                        index--;
0442                        rect = getCellBounds(list, index);
0443                        rect.y += rect.height;
0444                    } else {
0445                        rect = getCellBounds(list, index);
0446                    }
0447
0448                    if (rect.y >= list.getHeight()) {
0449                        rect.y = list.getHeight() - DROP_LINE_THICKNESS;
0450                    }
0451
0452                    rect.height = DROP_LINE_THICKNESS;
0453                }
0454
0455                return rect;
0456            }
0457
0458            /**
0459             * Returns the baseline.
0460             *
0461             * @throws NullPointerException {@inheritDoc}
0462             * @throws IllegalArgumentException {@inheritDoc}
0463             * @see javax.swing.JComponent#getBaseline(int, int)
0464             * @since 1.6
0465             */
0466            public int getBaseline(JComponent c, int width, int height) {
0467                super .getBaseline(c, width, height);
0468                int rowHeight = list.getFixedCellHeight();
0469                UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
0470                Component renderer = (Component) lafDefaults
0471                        .get(BASELINE_COMPONENT_KEY);
0472                if (renderer == null) {
0473                    ListCellRenderer lcr = (ListCellRenderer) UIManager
0474                            .get("List.cellRenderer");
0475                    renderer = lcr.getListCellRendererComponent(list, "a", -1,
0476                            false, false);
0477                    lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
0478                }
0479                renderer.setFont(list.getFont());
0480                // JList actually has much more complex behavior here.
0481                // If rowHeight != -1 the rowHeight is either the max of all cell
0482                // heights (layout orientation != VERTICAL), or is variable depending
0483                // upon the cell.  We assume a default size.
0484                // We could theoretically query the real renderer, but that would
0485                // not work for an empty model and the results may vary with 
0486                // the content.
0487                if (rowHeight == -1) {
0488                    rowHeight = renderer.getPreferredSize().height;
0489                }
0490                return renderer.getBaseline(Integer.MAX_VALUE, rowHeight)
0491                        + list.getInsets().top;
0492            }
0493
0494            /**
0495             * Returns an enum indicating how the baseline of the component
0496             * changes as the size changes.
0497             *
0498             * @throws NullPointerException {@inheritDoc}
0499             * @see javax.swing.JComponent#getBaseline(int, int)
0500             * @since 1.6
0501             */
0502            public Component.BaselineResizeBehavior getBaselineResizeBehavior(
0503                    JComponent c) {
0504                super .getBaselineResizeBehavior(c);
0505                return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
0506            }
0507
0508            /**
0509             * The preferredSize of the list depends upon the layout orientation.
0510             * <table summary="Describes the preferred size for each layout orientation">
0511             * <tr><th>Layout Orientation</th><th>Preferred Size</th></tr>
0512             * <tr>
0513             *   <td>JList.VERTICAL
0514             *   <td>The preferredSize of the list is total height of the rows
0515             *       and the maximum width of the cells.  If JList.fixedCellHeight
0516             *       is specified then the total height of the rows is just
0517             *       (cellVerticalMargins + fixedCellHeight) * model.getSize() where
0518             *       rowVerticalMargins is the space we allocate for drawing
0519             *       the yellow focus outline.  Similarly if fixedCellWidth is
0520             *       specified then we just use that.
0521             *   </td>
0522             * <tr>
0523             *   <td>JList.VERTICAL_WRAP
0524             *   <td>If the visible row count is greater than zero, the preferredHeight
0525             *       is the maximum cell height * visibleRowCount. If the visible row
0526             *       count is <= 0, the preferred height is either the current height
0527             *       of the list, or the maximum cell height, whichever is
0528             *       bigger. The preferred width is than the maximum cell width *
0529             *       number of columns needed. Where the number of columns needs is
0530             *       list.height / max cell height. Max cell height is either the fixed
0531             *       cell height, or is determined by iterating through all the cells
0532             *       to find the maximum height from the ListCellRenderer.
0533             * <tr>
0534             *   <td>JList.HORIZONTAL_WRAP
0535             *   <td>If the visible row count is greater than zero, the preferredHeight
0536             *       is the maximum cell height * adjustedRowCount.  Where
0537             *       visibleRowCount is used to determine the number of columns.
0538             *       Because this lays out horizontally the number of rows is
0539             *       then determined from the column count.  For example, lets say
0540             *       you have a model with 10 items and the visible row count is 8.
0541             *       The number of columns needed to display this is 2, but you no
0542             *       longer need 8 rows to display this, you only need 5, thus
0543             *       the adjustedRowCount is 5.
0544             *       <p>If the visible row
0545             *       count is <= 0, the preferred height is dictated by the 
0546             *       number of columns, which will be as many as can fit in the width
0547             *       of the <code>JList</code> (width / max cell width), with at
0548             *       least one column.  The preferred height then becomes the
0549             *       model size / number of columns * maximum cell height.
0550             *       Max cell height is either the fixed
0551             *       cell height, or is determined by iterating through all the cells
0552             *       to find the maximum height from the ListCellRenderer.
0553             * </table>
0554             * The above specifies the raw preferred width and height. The resulting
0555             * preferred width is the above width + insets.left + insets.right and
0556             * the resulting preferred height is the above height + insets.top +
0557             * insets.bottom. Where the <code>Insets</code> are determined from
0558             * <code>list.getInsets()</code>.
0559             *
0560             * @param c The JList component.
0561             * @return The total size of the list.
0562             */
0563            public Dimension getPreferredSize(JComponent c) {
0564                maybeUpdateLayoutState();
0565
0566                int lastRow = list.getModel().getSize() - 1;
0567                if (lastRow < 0) {
0568                    return new Dimension(0, 0);
0569                }
0570
0571                Insets insets = list.getInsets();
0572                int width = cellWidth * columnCount + insets.left
0573                        + insets.right;
0574                int height;
0575
0576                if (layoutOrientation != JList.VERTICAL) {
0577                    height = preferredHeight;
0578                } else {
0579                    Rectangle bounds = getCellBounds(list, lastRow);
0580
0581                    if (bounds != null) {
0582                        height = bounds.y + bounds.height + insets.bottom;
0583                    } else {
0584                        height = 0;
0585                    }
0586                }
0587                return new Dimension(width, height);
0588            }
0589
0590            /**
0591             * Selected the previous row and force it to be visible.
0592             *
0593             * @see JList#ensureIndexIsVisible
0594             */
0595            protected void selectPreviousIndex() {
0596                int s = list.getSelectedIndex();
0597                if (s > 0) {
0598                    s -= 1;
0599                    list.setSelectedIndex(s);
0600                    list.ensureIndexIsVisible(s);
0601                }
0602            }
0603
0604            /**
0605             * Selected the previous row and force it to be visible.
0606             *
0607             * @see JList#ensureIndexIsVisible
0608             */
0609            protected void selectNextIndex() {
0610                int s = list.getSelectedIndex();
0611                if ((s + 1) < list.getModel().getSize()) {
0612                    s += 1;
0613                    list.setSelectedIndex(s);
0614                    list.ensureIndexIsVisible(s);
0615                }
0616            }
0617
0618            /**
0619             * Registers the keyboard bindings on the <code>JList</code> that the
0620             * <code>BasicListUI</code> is associated with. This method is called at
0621             * installUI() time.
0622             *
0623             * @see #installUI
0624             */
0625            protected void installKeyboardActions() {
0626                InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
0627
0628                SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
0629                        inputMap);
0630
0631                LazyActionMap.installLazyActionMap(list, BasicListUI.class,
0632                        "List.actionMap");
0633            }
0634
0635            InputMap getInputMap(int condition) {
0636                if (condition == JComponent.WHEN_FOCUSED) {
0637                    InputMap keyMap = (InputMap) DefaultLookup.get(list, this ,
0638                            "List.focusInputMap");
0639                    InputMap rtlKeyMap;
0640
0641                    if (isLeftToRight
0642                            || ((rtlKeyMap = (InputMap) DefaultLookup.get(list,
0643                                    this , "List.focusInputMap.RightToLeft")) == null)) {
0644                        return keyMap;
0645                    } else {
0646                        rtlKeyMap.setParent(keyMap);
0647                        return rtlKeyMap;
0648                    }
0649                }
0650                return null;
0651            }
0652
0653            /**
0654             * Unregisters keyboard actions installed from
0655             * <code>installKeyboardActions</code>.
0656             * This method is called at uninstallUI() time - subclassess should
0657             * ensure that all of the keyboard actions registered at installUI
0658             * time are removed here.
0659             *
0660             * @see #installUI
0661             */
0662            protected void uninstallKeyboardActions() {
0663                SwingUtilities.replaceUIActionMap(list, null);
0664                SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
0665                        null);
0666            }
0667
0668            /**
0669             * Create and install the listeners for the JList, its model, and its
0670             * selectionModel.  This method is called at installUI() time.
0671             *
0672             * @see #installUI
0673             * @see #uninstallListeners
0674             */
0675            protected void installListeners() {
0676                TransferHandler th = list.getTransferHandler();
0677                if (th == null || th instanceof  UIResource) {
0678                    list.setTransferHandler(defaultTransferHandler);
0679                    // default TransferHandler doesn't support drop
0680                    // so we don't want drop handling
0681                    if (list.getDropTarget() instanceof  UIResource) {
0682                        list.setDropTarget(null);
0683                    }
0684                }
0685
0686                focusListener = createFocusListener();
0687                mouseInputListener = createMouseInputListener();
0688                propertyChangeListener = createPropertyChangeListener();
0689                listSelectionListener = createListSelectionListener();
0690                listDataListener = createListDataListener();
0691
0692                list.addFocusListener(focusListener);
0693                list.addMouseListener(mouseInputListener);
0694                list.addMouseMotionListener(mouseInputListener);
0695                list.addPropertyChangeListener(propertyChangeListener);
0696                list.addKeyListener(getHandler());
0697
0698                ListModel model = list.getModel();
0699                if (model != null) {
0700                    model.addListDataListener(listDataListener);
0701                }
0702
0703                ListSelectionModel selectionModel = list.getSelectionModel();
0704                if (selectionModel != null) {
0705                    selectionModel
0706                            .addListSelectionListener(listSelectionListener);
0707                }
0708            }
0709
0710            /**
0711             * Remove the listeners for the JList, its model, and its
0712             * selectionModel.  All of the listener fields, are reset to
0713             * null here.  This method is called at uninstallUI() time,
0714             * it should be kept in sync with installListeners.
0715             *
0716             * @see #uninstallUI
0717             * @see #installListeners
0718             */
0719            protected void uninstallListeners() {
0720                list.removeFocusListener(focusListener);
0721                list.removeMouseListener(mouseInputListener);
0722                list.removeMouseMotionListener(mouseInputListener);
0723                list.removePropertyChangeListener(propertyChangeListener);
0724                list.removeKeyListener(getHandler());
0725
0726                ListModel model = list.getModel();
0727                if (model != null) {
0728                    model.removeListDataListener(listDataListener);
0729                }
0730
0731                ListSelectionModel selectionModel = list.getSelectionModel();
0732                if (selectionModel != null) {
0733                    selectionModel
0734                            .removeListSelectionListener(listSelectionListener);
0735                }
0736
0737                focusListener = null;
0738                mouseInputListener = null;
0739                listSelectionListener = null;
0740                listDataListener = null;
0741                propertyChangeListener = null;
0742                handler = null;
0743            }
0744
0745            /**
0746             * Initialize JList properties, e.g. font, foreground, and background,
0747             * and add the CellRendererPane.  The font, foreground, and background
0748             * properties are only set if their current value is either null
0749             * or a UIResource, other properties are set if the current
0750             * value is null.
0751             *
0752             * @see #uninstallDefaults
0753             * @see #installUI
0754             * @see CellRendererPane
0755             */
0756            protected void installDefaults() {
0757                list.setLayout(null);
0758
0759                LookAndFeel.installBorder(list, "List.border");
0760
0761                LookAndFeel.installColorsAndFont(list, "List.background",
0762                        "List.foreground", "List.font");
0763
0764                LookAndFeel.installProperty(list, "opaque", Boolean.TRUE);
0765
0766                if (list.getCellRenderer() == null) {
0767                    list.setCellRenderer((ListCellRenderer) (UIManager
0768                            .get("List.cellRenderer")));
0769                }
0770
0771                Color sbg = list.getSelectionBackground();
0772                if (sbg == null || sbg instanceof  UIResource) {
0773                    list.setSelectionBackground(UIManager
0774                            .getColor("List.selectionBackground"));
0775                }
0776
0777                Color sfg = list.getSelectionForeground();
0778                if (sfg == null || sfg instanceof  UIResource) {
0779                    list.setSelectionForeground(UIManager
0780                            .getColor("List.selectionForeground"));
0781                }
0782
0783                Long l = (Long) UIManager.get("List.timeFactor");
0784                timeFactor = (l != null) ? l.longValue() : 1000L;
0785
0786                updateIsFileList();
0787            }
0788
0789            private void updateIsFileList() {
0790                boolean b = Boolean.TRUE.equals(list
0791                        .getClientProperty("List.isFileList"));
0792                if (b != isFileList) {
0793                    isFileList = b;
0794                    Font oldFont = list.getFont();
0795                    if (oldFont == null || oldFont instanceof  UIResource) {
0796                        Font newFont = UIManager
0797                                .getFont(b ? "FileChooser.listFont"
0798                                        : "List.font");
0799                        if (newFont != null && newFont != oldFont) {
0800                            list.setFont(newFont);
0801                        }
0802                    }
0803                }
0804            }
0805
0806            /**
0807             * Set the JList properties that haven't been explicitly overridden to
0808             * null.  A property is considered overridden if its current value
0809             * is not a UIResource.
0810             *
0811             * @see #installDefaults
0812             * @see #uninstallUI
0813             * @see CellRendererPane
0814             */
0815            protected void uninstallDefaults() {
0816                LookAndFeel.uninstallBorder(list);
0817                if (list.getFont() instanceof  UIResource) {
0818                    list.setFont(null);
0819                }
0820                if (list.getForeground() instanceof  UIResource) {
0821                    list.setForeground(null);
0822                }
0823                if (list.getBackground() instanceof  UIResource) {
0824                    list.setBackground(null);
0825                }
0826                if (list.getSelectionBackground() instanceof  UIResource) {
0827                    list.setSelectionBackground(null);
0828                }
0829                if (list.getSelectionForeground() instanceof  UIResource) {
0830                    list.setSelectionForeground(null);
0831                }
0832                if (list.getCellRenderer() instanceof  UIResource) {
0833                    list.setCellRenderer(null);
0834                }
0835                if (list.getTransferHandler() instanceof  UIResource) {
0836                    list.setTransferHandler(null);
0837                }
0838            }
0839
0840            /**
0841             * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
0842             * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
0843             * in order.
0844             *
0845             * @see #installDefaults
0846             * @see #installListeners
0847             * @see #installKeyboardActions
0848             */
0849            public void installUI(JComponent c) {
0850                list = (JList) c;
0851
0852                layoutOrientation = list.getLayoutOrientation();
0853
0854                rendererPane = new CellRendererPane();
0855                list.add(rendererPane);
0856
0857                columnCount = 1;
0858
0859                updateLayoutStateNeeded = modelChanged;
0860                isLeftToRight = list.getComponentOrientation().isLeftToRight();
0861
0862                installDefaults();
0863                installListeners();
0864                installKeyboardActions();
0865            }
0866
0867            /**
0868             * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
0869             * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>
0870             * in order.  Sets this.list to null.
0871             *
0872             * @see #uninstallListeners
0873             * @see #uninstallKeyboardActions
0874             * @see #uninstallDefaults
0875             */
0876            public void uninstallUI(JComponent c) {
0877                uninstallListeners();
0878                uninstallDefaults();
0879                uninstallKeyboardActions();
0880
0881                cellWidth = cellHeight = -1;
0882                cellHeights = null;
0883
0884                listWidth = listHeight = -1;
0885
0886                list.remove(rendererPane);
0887                rendererPane = null;
0888                list = null;
0889            }
0890
0891            /**
0892             * Returns a new instance of BasicListUI.  BasicListUI delegates are
0893             * allocated one per JList.
0894             *
0895             * @return A new ListUI implementation for the Windows look and feel.
0896             */
0897            public static ComponentUI createUI(JComponent list) {
0898                return new BasicListUI();
0899            }
0900
0901            /**
0902             * {@inheritDoc}
0903             * @throws NullPointerException {@inheritDoc}
0904             */
0905            public int locationToIndex(JList list, Point location) {
0906                maybeUpdateLayoutState();
0907                return convertLocationToModel(location.x, location.y);
0908            }
0909
0910            /**
0911             * {@inheritDoc}
0912             */
0913            public Point indexToLocation(JList list, int index) {
0914                maybeUpdateLayoutState();
0915                Rectangle rect = getCellBounds(list, index, index);
0916
0917                if (rect != null) {
0918                    return new Point(rect.x, rect.y);
0919                }
0920                return null;
0921            }
0922
0923            /**
0924             * {@inheritDoc}
0925             */
0926            public Rectangle getCellBounds(JList list, int index1, int index2) {
0927                maybeUpdateLayoutState();
0928
0929                int minIndex = Math.min(index1, index2);
0930                int maxIndex = Math.max(index1, index2);
0931
0932                if (minIndex >= list.getModel().getSize()) {
0933                    return null;
0934                }
0935
0936                Rectangle minBounds = getCellBounds(list, minIndex);
0937
0938                if (minBounds == null) {
0939                    return null;
0940                }
0941                if (minIndex == maxIndex) {
0942                    return minBounds;
0943                }
0944                Rectangle maxBounds = getCellBounds(list, maxIndex);
0945
0946                if (maxBounds != null) {
0947                    if (layoutOrientation == JList.HORIZONTAL_WRAP) {
0948                        int minRow = convertModelToRow(minIndex);
0949                        int maxRow = convertModelToRow(maxIndex);
0950
0951                        if (minRow != maxRow) {
0952                            minBounds.x = 0;
0953                            minBounds.width = list.getWidth();
0954                        }
0955                    } else if (minBounds.x != maxBounds.x) {
0956                        // Different columns
0957                        minBounds.y = 0;
0958                        minBounds.height = list.getHeight();
0959                    }
0960                    minBounds.add(maxBounds);
0961                }
0962                return minBounds;
0963            }
0964
0965            /**
0966             * Gets the bounds of the specified model index, returning the resulting
0967             * bounds, or null if <code>index</code> is not valid.
0968             */
0969            private Rectangle getCellBounds(JList list, int index) {
0970                maybeUpdateLayoutState();
0971
0972                int row = convertModelToRow(index);
0973                int column = convertModelToColumn(index);
0974
0975                if (row == -1 || column == -1) {
0976                    return null;
0977                }
0978
0979                Insets insets = list.getInsets();
0980                int x;
0981                int w = cellWidth;
0982                int y = insets.top;
0983                int h;
0984                switch (layoutOrientation) {
0985                case JList.VERTICAL_WRAP:
0986                case JList.HORIZONTAL_WRAP:
0987                    if (isLeftToRight) {
0988                        x = insets.left + column * cellWidth;
0989                    } else {
0990                        x = list.getWidth() - insets.right - (column + 1)
0991                                * cellWidth;
0992                    }
0993                    y += cellHeight * row;
0994                    h = cellHeight;
0995                    break;
0996                default:
0997                    x = insets.left;
0998                    if (cellHeights == null) {
0999                        y += (cellHeight * row);
1000                    } else if (row >= cellHeights.length) {
1001                        y = 0;
1002                    } else {
1003                        for (int i = 0; i < row; i++) {
1004                            y += cellHeights[i];
1005                        }
1006                    }
1007                    w = list.getWidth() - (insets.left + insets.right);
1008                    h = getRowHeight(index);
1009                    break;
1010                }
1011                return new Rectangle(x, y, w, h);
1012            }
1013
1014            /**
1015             * Returns the height of the specified row based on the current layout.
1016             *
1017             * @return The specified row height or -1 if row isn't valid.
1018             * @see #convertYToRow
1019             * @see #convertRowToY
1020             * @see #updateLayoutState
1021             */
1022            protected int getRowHeight(int row) {
1023                return getHeight(0, row);
1024            }
1025
1026            /**
1027             * Convert the JList relative coordinate to the row that contains it,
1028             * based on the current layout.  If y0 doesn't fall within any row,
1029             * return -1.
1030             *
1031             * @return The row that contains y0, or -1.
1032             * @see #getRowHeight
1033             * @see #updateLayoutState
1034             */
1035            protected int convertYToRow(int y0) {
1036                return convertLocationToRow(0, y0, false);
1037            }
1038
1039            /**
1040             * Return the JList relative Y coordinate of the origin of the specified
1041             * row or -1 if row isn't valid.
1042             *
1043             * @return The Y coordinate of the origin of row, or -1.
1044             * @see #getRowHeight
1045             * @see #updateLayoutState
1046             */
1047            protected int convertRowToY(int row) {
1048                if (row >= getRowCount(0) || row < 0) {
1049                    return -1;
1050                }
1051                Rectangle bounds = getCellBounds(list, row, row);
1052                return bounds.y;
1053            }
1054
1055            /**
1056             * Returns the height of the cell at the passed in location.
1057             */
1058            private int getHeight(int column, int row) {
1059                if (column < 0 || column > columnCount || row < 0) {
1060                    return -1;
1061                }
1062                if (layoutOrientation != JList.VERTICAL) {
1063                    return cellHeight;
1064                }
1065                if (row >= list.getModel().getSize()) {
1066                    return -1;
1067                }
1068                return (cellHeights == null) ? cellHeight
1069                        : ((row < cellHeights.length) ? cellHeights[row] : -1);
1070            }
1071
1072            /**
1073             * Returns the row at location x/y.
1074             *
1075             * @param closest If true and the location doesn't exactly match a
1076             *                particular location, this will return the closest row.
1077             */
1078            private int convertLocationToRow(int x, int y0, boolean closest) {
1079                int size = list.getModel().getSize();
1080
1081                if (size <= 0) {
1082                    return -1;
1083                }
1084                Insets insets = list.getInsets();
1085                if (cellHeights == null) {
1086                    int row = (cellHeight == 0) ? 0
1087                            : ((y0 - insets.top) / cellHeight);
1088                    if (closest) {
1089                        if (row < 0) {
1090                            row = 0;
1091                        } else if (row >= size) {
1092                            row = size - 1;
1093                        }
1094                    }
1095                    return row;
1096                } else if (size > cellHeights.length) {
1097                    return -1;
1098                } else {
1099                    int y = insets.top;
1100                    int row = 0;
1101
1102                    if (closest && y0 < y) {
1103                        return 0;
1104                    }
1105                    int i;
1106                    for (i = 0; i < size; i++) {
1107                        if ((y0 >= y) && (y0 < y + cellHeights[i])) {
1108                            return row;
1109                        }
1110                        y += cellHeights[i];
1111                        row += 1;
1112                    }
1113                    return i - 1;
1114                }
1115            }
1116
1117            /**
1118             * Returns the closest row that starts at the specified y-location
1119             * in the passed in column.
1120             */
1121            private int convertLocationToRowInColumn(int y, int column) {
1122                int x = 0;
1123
1124                if (layoutOrientation != JList.VERTICAL) {
1125                    if (isLeftToRight) {
1126                        x = column * cellWidth;
1127                    } else {
1128                        x = list.getWidth() - (column + 1) * cellWidth
1129                                - list.getInsets().right;
1130                    }
1131                }
1132                return convertLocationToRow(x, y, true);
1133            }
1134
1135            /**
1136             * Returns the closest location to the model index of the passed in
1137             * location.
1138             */
1139            private int convertLocationToModel(int x, int y) {
1140                int row = convertLocationToRow(x, y, true);
1141                int column = convertLocationToColumn(x, y);
1142
1143                if (row >= 0 && column >= 0) {
1144                    return getModelIndex(column, row);
1145                }
1146                return -1;
1147            }
1148
1149            /**
1150             * Returns the number of rows in the given column.
1151             */
1152            private int getRowCount(int column) {
1153                if (column < 0 || column >= columnCount) {
1154                    return -1;
1155                }
1156                if (layoutOrientation == JList.VERTICAL
1157                        || (column == 0 && columnCount == 1)) {
1158                    return list.getModel().getSize();
1159                }
1160                if (column >= columnCount) {
1161                    return -1;
1162                }
1163                if (layoutOrientation == JList.VERTICAL_WRAP) {
1164                    if (column < (columnCount - 1)) {
1165                        return rowsPerColumn;
1166                    }
1167                    return list.getModel().getSize() - (columnCount - 1)
1168                            * rowsPerColumn;
1169                }
1170                // JList.HORIZONTAL_WRAP
1171                int diff = columnCount
1172                        - (columnCount * rowsPerColumn - list.getModel()
1173                                .getSize());
1174
1175                if (column >= diff) {
1176                    return Math.max(0, rowsPerColumn - 1);
1177                }
1178                return rowsPerColumn;
1179            }
1180
1181            /**
1182             * Returns the model index for the specified display location.
1183             * If <code>column</code>x<code>row</code> is beyond the length of the
1184             * model, this will return the model size - 1.
1185             */
1186            private int getModelIndex(int column, int row) {
1187                switch (layoutOrientation) {
1188                case JList.VERTICAL_WRAP:
1189                    return Math.min(list.getModel().getSize() - 1,
1190                            rowsPerColumn * column
1191                                    + Math.min(row, rowsPerColumn - 1));
1192                case JList.HORIZONTAL_WRAP:
1193                    return Math.min(list.getModel().getSize() - 1, row
1194                            * columnCount + column);
1195                default:
1196                    return row;
1197                }
1198            }
1199
1200            /**
1201             * Returns the closest column to the passed in location.
1202             */
1203            private int convertLocationToColumn(int x, int y) {
1204                if (cellWidth > 0) {
1205                    if (layoutOrientation == JList.VERTICAL) {
1206                        return 0;
1207                    }
1208                    Insets insets = list.getInsets();
1209                    int col;
1210                    if (isLeftToRight) {
1211                        col = (x - insets.left) / cellWidth;
1212                    } else {
1213                        col = (list.getWidth() - x - insets.right - 1)
1214                                / cellWidth;
1215                    }
1216                    if (col < 0) {
1217                        return 0;
1218                    } else if (col >= columnCount) {
1219                        return columnCount - 1;
1220                    }
1221                    return col;
1222                }
1223                return 0;
1224            }
1225
1226            /**
1227             * Returns the row that the model index <code>index</code> will be
1228             * displayed in..
1229             */
1230            private int convertModelToRow(int index) {
1231                int size = list.getModel().getSize();
1232
1233                if ((index < 0) || (index >= size)) {
1234                    return -1;
1235                }
1236
1237                if (layoutOrientation != JList.VERTICAL && columnCount > 1
1238                        && rowsPerColumn > 0) {
1239                    if (layoutOrientation == JList.VERTICAL_WRAP) {
1240                        return index % rowsPerColumn;
1241                    }
1242                    return index / columnCount;
1243                }
1244                return index;
1245            }
1246
1247            /**
1248             * Returns the column that the model index <code>index</code> will be
1249             * displayed in.
1250             */
1251            private int convertModelToColumn(int index) {
1252                int size = list.getModel().getSize();
1253
1254                if ((index < 0) || (index >= size)) {
1255                    return -1;
1256                }
1257
1258                if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0
1259                        && columnCount > 1) {
1260                    if (layoutOrientation == JList.VERTICAL_WRAP) {
1261                        return index / rowsPerColumn;
1262                    }
1263                    return index % columnCount;
1264                }
1265                return 0;
1266            }
1267
1268            /**
1269             * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
1270             * updateLayoutStateNeeded.  This method should be called by methods
1271             * before doing any computation based on the geometry of the list.
1272             * For example it's the first call in paint() and getPreferredSize().
1273             *
1274             * @see #updateLayoutState
1275             */
1276            protected void maybeUpdateLayoutState() {
1277                if (updateLayoutStateNeeded != 0) {
1278                    updateLayoutState();
1279                    updateLayoutStateNeeded = 0;
1280                }
1281            }
1282
1283            /**
1284             * Recompute the value of cellHeight or cellHeights based
1285             * and cellWidth, based on the current font and the current
1286             * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
1287             *
1288             * @see #maybeUpdateLayoutState
1289             */
1290            protected void updateLayoutState() {
1291                /* If both JList fixedCellWidth and fixedCellHeight have been
1292                 * set, then initialize cellWidth and cellHeight, and set
1293                 * cellHeights to null.
1294                 */
1295
1296                int fixedCellHeight = list.getFixedCellHeight();
1297                int fixedCellWidth = list.getFixedCellWidth();
1298
1299                cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
1300
1301                if (fixedCellHeight != -1) {
1302                    cellHeight = fixedCellHeight;
1303                    cellHeights = null;
1304                } else {
1305                    cellHeight = -1;
1306                    cellHeights = new int[list.getModel().getSize()];
1307                }
1308
1309                /* If either of  JList fixedCellWidth and fixedCellHeight haven't
1310                 * been set, then initialize cellWidth and cellHeights by
1311                 * scanning through the entire model.  Note: if the renderer is
1312                 * null, we just set cellWidth and cellHeights[*] to zero,
1313                 * if they're not set already.
1314                 */
1315
1316                if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
1317
1318                    ListModel dataModel = list.getModel();
1319                    int dataModelSize = dataModel.getSize();
1320                    ListCellRenderer renderer = list.getCellRenderer();
1321
1322                    if (renderer != null) {
1323                        for (int index = 0; index < dataModelSize; index++) {
1324                            Object value = dataModel.getElementAt(index);
1325                            Component c = renderer
1326                                    .getListCellRendererComponent(list, value,
1327                                            index, false, false);
1328                            rendererPane.add(c);
1329                            Dimension cellSize = c.getPreferredSize();
1330                            if (fixedCellWidth == -1) {
1331                                cellWidth = Math.max(cellSize.width, cellWidth);
1332                            }
1333                            if (fixedCellHeight == -1) {
1334                                cellHeights[index] = cellSize.height;
1335                            }
1336                        }
1337                    } else {
1338                        if (cellWidth == -1) {
1339                            cellWidth = 0;
1340                        }
1341                        if (cellHeights == null) {
1342                            cellHeights = new int[dataModelSize];
1343                        }
1344                        for (int index = 0; index < dataModelSize; index++) {
1345                            cellHeights[index] = 0;
1346                        }
1347                    }
1348                }
1349
1350                columnCount = 1;
1351                if (layoutOrientation != JList.VERTICAL) {
1352                    updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight);
1353                }
1354            }
1355
1356            /**
1357             * Invoked when the list is layed out horizontally to determine how
1358             * many columns to create.
1359             * <p>
1360             * This updates the <code>rowsPerColumn, </code><code>columnCount</code>,
1361             * <code>preferredHeight</code> and potentially <code>cellHeight</code>
1362             * instance variables.
1363             */
1364            private void updateHorizontalLayoutState(int fixedCellWidth,
1365                    int fixedCellHeight) {
1366                int visRows = list.getVisibleRowCount();
1367                int dataModelSize = list.getModel().getSize();
1368                Insets insets = list.getInsets();
1369
1370                listHeight = list.getHeight();
1371                listWidth = list.getWidth();
1372
1373                if (dataModelSize == 0) {
1374                    rowsPerColumn = columnCount = 0;
1375                    preferredHeight = insets.top + insets.bottom;
1376                    return;
1377                }
1378
1379                int height;
1380
1381                if (fixedCellHeight != -1) {
1382                    height = fixedCellHeight;
1383                } else {
1384                    // Determine the max of the renderer heights.
1385                    int maxHeight = 0;
1386                    if (cellHeights.length > 0) {
1387                        maxHeight = cellHeights[cellHeights.length - 1];
1388                        for (int counter = cellHeights.length - 2; counter >= 0; counter--) {
1389                            maxHeight = Math.max(maxHeight,
1390                                    cellHeights[counter]);
1391                        }
1392                    }
1393                    height = cellHeight = maxHeight;
1394                    cellHeights = null;
1395                }
1396                // The number of rows is either determined by the visible row
1397                // count, or by the height of the list.
1398                rowsPerColumn = dataModelSize;
1399                if (visRows > 0) {
1400                    rowsPerColumn = visRows;
1401                    columnCount = Math.max(1, dataModelSize / rowsPerColumn);
1402                    if (dataModelSize > 0 && dataModelSize > rowsPerColumn
1403                            && dataModelSize % rowsPerColumn != 0) {
1404                        columnCount++;
1405                    }
1406                    if (layoutOrientation == JList.HORIZONTAL_WRAP) {
1407                        // Because HORIZONTAL_WRAP flows differently, the 
1408                        // rowsPerColumn needs to be adjusted.
1409                        rowsPerColumn = (dataModelSize / columnCount);
1410                        if (dataModelSize % columnCount > 0) {
1411                            rowsPerColumn++;
1412                        }
1413                    }
1414                } else if (layoutOrientation == JList.VERTICAL_WRAP
1415                        && height != 0) {
1416                    rowsPerColumn = Math.max(1,
1417                            (listHeight - insets.top - insets.bottom) / height);
1418                    columnCount = Math.max(1, dataModelSize / rowsPerColumn);
1419                    if (dataModelSize > 0 && dataModelSize > rowsPerColumn
1420                            && dataModelSize % rowsPerColumn != 0) {
1421                        columnCount++;
1422                    }
1423                } else if (layoutOrientation == JList.HORIZONTAL_WRAP
1424                        && cellWidth > 0 && listWidth > 0) {
1425                    columnCount = Math.max(1,
1426                            (listWidth - insets.left - insets.right)
1427                                    / cellWidth);
1428                    rowsPerColumn = dataModelSize / columnCount;
1429                    if (dataModelSize % columnCount > 0) {
1430                        rowsPerColumn++;
1431                    }
1432                }
1433                preferredHeight = rowsPerColumn * cellHeight + insets.top
1434                        + insets.bottom;
1435            }
1436
1437            private Handler getHandler() {
1438                if (handler == null) {
1439                    handler = new Handler();
1440                }
1441                return handler;
1442            }
1443
1444            /**
1445             * Mouse input, and focus handling for JList.  An instance of this
1446             * class is added to the appropriate java.awt.Component lists
1447             * at installUI() time.  Note keyboard input is handled with JComponent
1448             * KeyboardActions, see installKeyboardActions().
1449             * <p>
1450             * <strong>Warning:</strong>
1451             * Serialized objects of this class will not be compatible with
1452             * future Swing releases. The current serialization support is
1453             * appropriate for short term storage or RMI between applications running
1454             * the same version of Swing.  As of 1.4, support for long term storage
1455             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1456             * has been added to the <code>java.beans</code> package.
1457             * Please see {@link java.beans.XMLEncoder}.
1458             *
1459             * @see #createMouseInputListener
1460             * @see #installKeyboardActions
1461             * @see #installUI
1462             */
1463            public class MouseInputHandler implements  MouseInputListener {
1464                public void mouseClicked(MouseEvent e) {
1465                    getHandler().mouseClicked(e);
1466                }
1467
1468                public void mouseEntered(MouseEvent e) {
1469                    getHandler().mouseEntered(e);
1470                }
1471
1472                public void mouseExited(MouseEvent e) {
1473                    getHandler().mouseExited(e);
1474                }
1475
1476                public void mousePressed(MouseEvent e) {
1477                    getHandler().mousePressed(e);
1478                }
1479
1480                public void mouseDragged(MouseEvent e) {
1481                    getHandler().mouseDragged(e);
1482                }
1483
1484                public void mouseMoved(MouseEvent e) {
1485                    getHandler().mouseMoved(e);
1486                }
1487
1488                public void mouseReleased(MouseEvent e) {
1489                    getHandler().mouseReleased(e);
1490                }
1491            }
1492
1493            /**
1494             * Creates a delegate that implements MouseInputListener.
1495             * The delegate is added to the corresponding java.awt.Component listener 
1496             * lists at installUI() time. Subclasses can override this method to return 
1497             * a custom MouseInputListener, e.g.
1498             * <pre>
1499             * class MyListUI extends BasicListUI {
1500             *    protected MouseInputListener <b>createMouseInputListener</b>() {
1501             *        return new MyMouseInputHandler();
1502             *    }
1503             *    public class MyMouseInputHandler extends MouseInputHandler {
1504             *        public void mouseMoved(MouseEvent e) {
1505             *            // do some extra work when the mouse moves
1506             *            super.mouseMoved(e);
1507             *        }
1508             *    }
1509             * }
1510             * </pre>
1511             *
1512             * @see MouseInputHandler
1513             * @see #installUI
1514             */
1515            protected MouseInputListener createMouseInputListener() {
1516                return getHandler();
1517            }
1518
1519            /**
1520             * This inner class is marked &quot;public&quot; due to a compiler bug.
1521             * This class should be treated as a &quot;protected&quot; inner class.
1522             * Instantiate it only within subclasses of BasicTableUI.
1523             */
1524            public class FocusHandler implements  FocusListener {
1525                protected void repaintCellFocus() {
1526                    getHandler().repaintCellFocus();
1527                }
1528
1529                /* The focusGained() focusLost() methods run when the JList
1530                 * focus changes.
1531                 */
1532
1533                public void focusGained(FocusEvent e) {
1534                    getHandler().focusGained(e);
1535                }
1536
1537                public void focusLost(FocusEvent e) {
1538                    getHandler().focusLost(e);
1539                }
1540            }
1541
1542            protected FocusListener createFocusListener() {
1543                return getHandler();
1544            }
1545
1546            /**
1547             * The ListSelectionListener that's added to the JLists selection
1548             * model at installUI time, and whenever the JList.selectionModel property
1549             * changes.  When the selection changes we repaint the affected rows.
1550             * <p>
1551             * <strong>Warning:</strong>
1552             * Serialized objects of this class will not be compatible with
1553             * future Swing releases. The current serialization support is
1554             * appropriate for short term storage or RMI between applications running
1555             * the same version of Swing.  As of 1.4, support for long term storage
1556             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1557             * has been added to the <code>java.beans</code> package.
1558             * Please see {@link java.beans.XMLEncoder}.
1559             *
1560             * @see #createListSelectionListener
1561             * @see #getCellBounds
1562             * @see #installUI
1563             */
1564            public class ListSelectionHandler implements  ListSelectionListener {
1565                public void valueChanged(ListSelectionEvent e) {
1566                    getHandler().valueChanged(e);
1567                }
1568            }
1569
1570            /**
1571             * Creates an instance of ListSelectionHandler that's added to
1572             * the JLists by selectionModel as needed.  Subclasses can override
1573             * this method to return a custom ListSelectionListener, e.g.
1574             * <pre>
1575             * class MyListUI extends BasicListUI {
1576             *    protected ListSelectionListener <b>createListSelectionListener</b>() {
1577             *        return new MySelectionListener();
1578             *    }
1579             *    public class MySelectionListener extends ListSelectionHandler {
1580             *        public void valueChanged(ListSelectionEvent e) {
1581             *            // do some extra work when the selection changes
1582             *            super.valueChange(e);
1583             *        }
1584             *    }
1585             * }
1586             * </pre>
1587             *
1588             * @see ListSelectionHandler
1589             * @see #installUI
1590             */
1591            protected ListSelectionListener createListSelectionListener() {
1592                return getHandler();
1593            }
1594
1595            private void redrawList() {
1596                list.revalidate();
1597                list.repaint();
1598            }
1599
1600            /**
1601             * The ListDataListener that's added to the JLists model at
1602             * installUI time, and whenever the JList.model property changes.
1603             * <p>
1604             * <strong>Warning:</strong>
1605             * Serialized objects of this class will not be compatible with
1606             * future Swing releases. The current serialization support is
1607             * appropriate for short term storage or RMI between applications running
1608             * the same version of Swing.  As of 1.4, support for long term storage
1609             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1610             * has been added to the <code>java.beans</code> package.
1611             * Please see {@link java.beans.XMLEncoder}.
1612             *
1613             * @see JList#getModel
1614             * @see #maybeUpdateLayoutState
1615             * @see #createListDataListener
1616             * @see #installUI
1617             */
1618            public class ListDataHandler implements  ListDataListener {
1619                public void intervalAdded(ListDataEvent e) {
1620                    getHandler().intervalAdded(e);
1621                }
1622
1623                public void intervalRemoved(ListDataEvent e) {
1624                    getHandler().intervalRemoved(e);
1625                }
1626
1627                public void contentsChanged(ListDataEvent e) {
1628                    getHandler().contentsChanged(e);
1629                }
1630            }
1631
1632            /**
1633             * Creates an instance of ListDataListener that's added to
1634             * the JLists by model as needed.  Subclasses can override
1635             * this method to return a custom ListDataListener, e.g.
1636             * <pre>
1637             * class MyListUI extends BasicListUI {
1638             *    protected ListDataListener <b>createListDataListener</b>() {
1639             *        return new MyListDataListener();
1640             *    }
1641             *    public class MyListDataListener extends ListDataHandler {
1642             *        public void contentsChanged(ListDataEvent e) {
1643             *            // do some extra work when the models contents change
1644             *            super.contentsChange(e);
1645             *        }
1646             *    }
1647             * }
1648             * </pre>
1649             *
1650             * @see ListDataListener
1651             * @see JList#getModel
1652             * @see #installUI
1653             */
1654            protected ListDataListener createListDataListener() {
1655                return getHandler();
1656            }
1657
1658            /**
1659             * The PropertyChangeListener that's added to the JList at
1660             * installUI time.  When the value of a JList property that
1661             * affects layout changes, we set a bit in updateLayoutStateNeeded.
1662             * If the JLists model changes we additionally remove our listeners
1663             * from the old model.  Likewise for the JList selectionModel.
1664             * <p>
1665             * <strong>Warning:</strong>
1666             * Serialized objects of this class will not be compatible with
1667             * future Swing releases. The current serialization support is
1668             * appropriate for short term storage or RMI between applications running
1669             * the same version of Swing.  As of 1.4, support for long term storage
1670             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1671             * has been added to the <code>java.beans</code> package.
1672             * Please see {@link java.beans.XMLEncoder}.
1673             *
1674             * @see #maybeUpdateLayoutState
1675             * @see #createPropertyChangeListener
1676             * @see #installUI
1677             */
1678            public class PropertyChangeHandler implements 
1679                    PropertyChangeListener {
1680                public void propertyChange(PropertyChangeEvent e) {
1681                    getHandler().propertyChange(e);
1682                }
1683            }
1684
1685            /**
1686             * Creates an instance of PropertyChangeHandler that's added to
1687             * the JList by installUI().  Subclasses can override this method
1688             * to return a custom PropertyChangeListener, e.g.
1689             * <pre>
1690             * class MyListUI extends BasicListUI {
1691             *    protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
1692             *        return new MyPropertyChangeListener();
1693             *    }
1694             *    public class MyPropertyChangeListener extends PropertyChangeHandler {
1695             *        public void propertyChange(PropertyChangeEvent e) {
1696             *            if (e.getPropertyName().equals("model")) {
1697             *                // do some extra work when the model changes
1698             *            }
1699             *            super.propertyChange(e);
1700             *        }
1701             *    }
1702             * }
1703             * </pre>
1704             *
1705             * @see PropertyChangeListener
1706             * @see #installUI
1707             */
1708            protected PropertyChangeListener createPropertyChangeListener() {
1709                return getHandler();
1710            }
1711
1712            /** Used by IncrementLeadSelectionAction. Indicates the action should
1713             * change the lead, and not select it. */
1714            private static final int CHANGE_LEAD = 0;
1715            /** Used by IncrementLeadSelectionAction. Indicates the action should
1716             * change the selection and lead. */
1717            private static final int CHANGE_SELECTION = 1;
1718            /** Used by IncrementLeadSelectionAction. Indicates the action should
1719             * extend the selection from the anchor to the next index. */
1720            private static final int EXTEND_SELECTION = 2;
1721
1722            private static class Actions extends UIAction {
1723                private static final String SELECT_PREVIOUS_COLUMN = "selectPreviousColumn";
1724                private static final String SELECT_PREVIOUS_COLUMN_EXTEND = "selectPreviousColumnExtendSelection";
1725                private static final String SELECT_PREVIOUS_COLUMN_CHANGE_LEAD = "selectPreviousColumnChangeLead";
1726                private static final String SELECT_NEXT_COLUMN = "selectNextColumn";
1727                private static final String SELECT_NEXT_COLUMN_EXTEND = "selectNextColumnExtendSelection";
1728                private static final String SELECT_NEXT_COLUMN_CHANGE_LEAD = "selectNextColumnChangeLead";
1729                private static final String SELECT_PREVIOUS_ROW = "selectPreviousRow";
1730                private static final String SELECT_PREVIOUS_ROW_EXTEND = "selectPreviousRowExtendSelection";
1731                private static final String SELECT_PREVIOUS_ROW_CHANGE_LEAD = "selectPreviousRowChangeLead";
1732                private static final String SELECT_NEXT_ROW = "selectNextRow";
1733                private static final String SELECT_NEXT_ROW_EXTEND = "selectNextRowExtendSelection";
1734                private static final String SELECT_NEXT_ROW_CHANGE_LEAD = "selectNextRowChangeLead";
1735                private static final String SELECT_FIRST_ROW = "selectFirstRow";
1736                private static final String SELECT_FIRST_ROW_EXTEND = "selectFirstRowExtendSelection";
1737                private static final String SELECT_FIRST_ROW_CHANGE_LEAD = "selectFirstRowChangeLead";
1738                private static final String SELECT_LAST_ROW = "selectLastRow";
1739                private static final String SELECT_LAST_ROW_EXTEND = "selectLastRowExtendSelection";
1740                private static final String SELECT_LAST_ROW_CHANGE_LEAD = "selectLastRowChangeLead";
1741                private static final String SCROLL_UP = "scrollUp";
1742                private static final String SCROLL_UP_EXTEND = "scrollUpExtendSelection";
1743                private static final String SCROLL_UP_CHANGE_LEAD = "scrollUpChangeLead";
1744                private static final String SCROLL_DOWN = "scrollDown";
1745                private static final String SCROLL_DOWN_EXTEND = "scrollDownExtendSelection";
1746                private static final String SCROLL_DOWN_CHANGE_LEAD = "scrollDownChangeLead";
1747                private static final String SELECT_ALL = "selectAll";
1748                private static final String CLEAR_SELECTION = "clearSelection";
1749
1750                // add the lead item to the selection without changing lead or anchor
1751                private static final String ADD_TO_SELECTION = "addToSelection";
1752
1753                // toggle the selected state of the lead item and move the anchor to it
1754                private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
1755
1756                // extend the selection to the lead item
1757                private static final String EXTEND_TO = "extendTo";
1758
1759                // move the anchor to the lead and ensure only that item is selected
1760                private static final String MOVE_SELECTION_TO = "moveSelectionTo";
1761
1762                Actions(String name) {
1763                    super (name);
1764                }
1765
1766                public void actionPerformed(ActionEvent e) {
1767                    String name = getName();
1768                    JList list = (JList) e.getSource();
1769                    BasicListUI ui = (BasicListUI) BasicLookAndFeel
1770                            .getUIOfType(list.getUI(), BasicListUI.class);
1771
1772                    if (name == SELECT_PREVIOUS_COLUMN) {
1773                        changeSelection(list, CHANGE_SELECTION,
1774                                getNextColumnIndex(list, ui, -1), -1);
1775                    } else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) {
1776                        changeSelection(list, EXTEND_SELECTION,
1777                                getNextColumnIndex(list, ui, -1), -1);
1778                    } else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) {
1779                        changeSelection(list, CHANGE_LEAD, getNextColumnIndex(
1780                                list, ui, -1), -1);
1781                    } else if (name == SELECT_NEXT_COLUMN) {
1782                        changeSelection(list, CHANGE_SELECTION,
1783                                getNextColumnIndex(list, ui, 1), 1);
1784                    } else if (name == SELECT_NEXT_COLUMN_EXTEND) {
1785                        changeSelection(list, EXTEND_SELECTION,
1786                                getNextColumnIndex(list, ui, 1), 1);
1787                    } else if (name == SELECT_NEXT_COLUMN_CHANGE_LEAD) {
1788                        changeSelection(list, CHANGE_LEAD, getNextColumnIndex(
1789                                list, ui, 1), 1);
1790                    } else if (name == SELECT_PREVIOUS_ROW) {
1791                        changeSelection(list, CHANGE_SELECTION, getNextIndex(
1792                                list, ui, -1), -1);
1793                    } else if (name == SELECT_PREVIOUS_ROW_EXTEND) {
1794                        changeSelection(list, EXTEND_SELECTION, getNextIndex(
1795                                list, ui, -1), -1);
1796                    } else if (name == SELECT_PREVIOUS_ROW_CHANGE_LEAD) {
1797                        changeSelection(list, CHANGE_LEAD, getNextIndex(list,
1798                                ui, -1), -1);
1799                    } else if (name == SELECT_NEXT_ROW) {
1800                        changeSelection(list, CHANGE_SELECTION, getNextIndex(
1801                                list, ui, 1), 1);
1802                    } else if (name == SELECT_NEXT_ROW_EXTEND) {
1803                        changeSelection(list, EXTEND_SELECTION, getNextIndex(
1804                                list, ui, 1), 1);
1805                    } else if (name == SELECT_NEXT_ROW_CHANGE_LEAD) {
1806                        changeSelection(list, CHANGE_LEAD, getNextIndex(list,
1807                                ui, 1), 1);
1808                    } else if (name == SELECT_FIRST_ROW) {
1809                        changeSelection(list, CHANGE_SELECTION, 0, -1);
1810                    } else if (name == SELECT_FIRST_ROW_EXTEND) {
1811                        changeSelection(list, EXTEND_SELECTION, 0, -1);
1812                    } else if (name == SELECT_FIRST_ROW_CHANGE_LEAD) {
1813                        changeSelection(list, CHANGE_LEAD, 0, -1);
1814                    } else if (name == SELECT_LAST_ROW) {
1815                        changeSelection(list, CHANGE_SELECTION, list.getModel()
1816                                .getSize() - 1, 1);
1817                    } else if (name == SELECT_LAST_ROW_EXTEND) {
1818                        changeSelection(list, EXTEND_SELECTION, list.getModel()
1819                                .getSize() - 1, 1);
1820                    } else if (name == SELECT_LAST_ROW_CHANGE_LEAD) {
1821                        changeSelection(list, CHANGE_LEAD, list.getModel()
1822                                .getSize() - 1, 1);
1823                    } else if (name == SCROLL_UP) {
1824                        changeSelection(list, CHANGE_SELECTION,
1825                                getNextPageIndex(list, -1), -1);
1826                    } else if (name == SCROLL_UP_EXTEND) {
1827                        changeSelection(list, EXTEND_SELECTION,
1828                                getNextPageIndex(list, -1), -1);
1829                    } else if (name == SCROLL_UP_CHANGE_LEAD) {
1830                        changeSelection(list, CHANGE_LEAD, getNextPageIndex(
1831                                list, -1), -1);
1832                    } else if (name == SCROLL_DOWN) {
1833                        changeSelection(list, CHANGE_SELECTION,
1834                                getNextPageIndex(list, 1), 1);
1835                    } else if (name == SCROLL_DOWN_EXTEND) {
1836                        changeSelection(list, EXTEND_SELECTION,
1837                                getNextPageIndex(list, 1), 1);
1838                    } else if (name == SCROLL_DOWN_CHANGE_LEAD) {
1839                        changeSelection(list, CHANGE_LEAD, getNextPageIndex(
1840                                list, 1), 1);
1841                    } else if (name == SELECT_ALL) {
1842                        selectAll(list);
1843                    } else if (name == CLEAR_SELECTION) {
1844                        clearSelection(list);
1845                    } else if (name == ADD_TO_SELECTION) {
1846                        int index = adjustIndex(list.getSelectionModel()
1847                                .getLeadSelectionIndex(), list);
1848
1849                        if (!list.isSelectedIndex(index)) {
1850                            int oldAnchor = list.getSelectionModel()
1851                                    .getAnchorSelectionIndex();
1852                            list.setValueIsAdjusting(true);
1853                            list.addSelectionInterval(index, index);
1854                            list.getSelectionModel().setAnchorSelectionIndex(
1855                                    oldAnchor);
1856                            list.setValueIsAdjusting(false);
1857                        }
1858                    } else if (name == TOGGLE_AND_ANCHOR) {
1859                        int index = adjustIndex(list.getSelectionModel()
1860                                .getLeadSelectionIndex(), list);
1861
1862                        if (list.isSelectedIndex(index)) {
1863                            list.removeSelectionInterval(index, index);
1864                        } else {
1865                            list.addSelectionInterval(index, index);
1866                        }
1867                    } else if (name == EXTEND_TO) {
1868                        changeSelection(list, EXTEND_SELECTION, adjustIndex(
1869                                list.getSelectionModel()
1870                                        .getLeadSelectionIndex(), list), 0);
1871                    } else if (name == MOVE_SELECTION_TO) {
1872                        changeSelection(list, CHANGE_SELECTION, adjustIndex(
1873                                list.getSelectionModel()
1874                                        .getLeadSelectionIndex(), list), 0);
1875                    }
1876                }
1877
1878                public boolean isEnabled(Object c) {
1879                    Object name = getName();
1880                    if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD
1881                            || name == SELECT_NEXT_COLUMN_CHANGE_LEAD
1882                            || name == SELECT_PREVIOUS_ROW_CHANGE_LEAD
1883                            || name == SELECT_NEXT_ROW_CHANGE_LEAD
1884                            || name == SELECT_FIRST_ROW_CHANGE_LEAD
1885                            || name == SELECT_LAST_ROW_CHANGE_LEAD
1886                            || name == SCROLL_UP_CHANGE_LEAD
1887                            || name == SCROLL_DOWN_CHANGE_LEAD) {
1888
1889                        // discontinuous selection actions are only enabled for
1890                        // DefaultListSelectionModel
1891                        return c != null
1892                                && ((JList) c).getSelectionModel() instanceof  DefaultListSelectionModel;
1893                    }
1894
1895                    return true;
1896                }
1897
1898                private void clearSelection(JList list) {
1899                    list.clearSelection();
1900                }
1901
1902                private void selectAll(JList list) {
1903                    int size = list.getModel().getSize();
1904                    if (size > 0) {
1905                        ListSelectionModel lsm = list.getSelectionModel();
1906                        int lead = adjustIndex(lsm.getLeadSelectionIndex(),
1907                                list);
1908
1909                        if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
1910                            if (lead == -1) {
1911                                int min = adjustIndex(list
1912                                        .getMinSelectionIndex(), list);
1913                                lead = (min == -1 ? 0 : min);
1914                            }
1915
1916                            list.setSelectionInterval(lead, lead);
1917                            list.ensureIndexIsVisible(lead);
1918                        } else {
1919                            list.setValueIsAdjusting(true);
1920
1921                            int anchor = adjustIndex(lsm
1922                                    .getAnchorSelectionIndex(), list);
1923
1924                            list.setSelectionInterval(0, size - 1);
1925
1926                            // this is done to restore the anchor and lead
1927                            SwingUtilities2.setLeadAnchorWithoutSelection(lsm,
1928                                    anchor, lead);
1929
1930                            list.setValueIsAdjusting(false);
1931                        }
1932                    }
1933                }
1934
1935                private int getNextPageIndex(JList list, int direction) {
1936                    if (list.getModel().getSize() == 0) {
1937                        return -1;
1938                    }
1939
1940                    int index = -1;
1941                    Rectangle visRect = list.getVisibleRect();
1942                    ListSelectionModel lsm = list.getSelectionModel();
1943                    int lead = adjustIndex(lsm.getLeadSelectionIndex(), list);
1944                    Rectangle leadRect = (lead == -1) ? new Rectangle() : list
1945                            .getCellBounds(lead, lead);
1946
1947                    if (list.getLayoutOrientation() == JList.VERTICAL_WRAP
1948                            && list.getVisibleRowCount() <= 0) {
1949                        if (!list.getComponentOrientation().isLeftToRight()) {
1950                            direction = -direction;
1951                        }
1952                        // apply for horizontal scrolling: the step for next
1953                        // page index is number of visible columns
1954                        if (direction < 0) {
1955                            // left
1956                            visRect.x = leadRect.x + leadRect.width
1957                                    - visRect.width;
1958                            Point p = new Point(visRect.x - 1, leadRect.y);
1959                            index = list.locationToIndex(p);
1960                            Rectangle cellBounds = list.getCellBounds(index,
1961                                    index);
1962                            if (visRect.intersects(cellBounds)) {
1963                                p.x = cellBounds.x - 1;
1964                                index = list.locationToIndex(p);
1965                                cellBounds = list.getCellBounds(index, index);
1966                            }
1967                            // this is necessary for right-to-left orientation only
1968                            if (cellBounds.y != leadRect.y) {
1969                                p.x = cellBounds.x + cellBounds.width;
1970                                index = list.locationToIndex(p);
1971                            }
1972                        } else {
1973                            // right
1974                            visRect.x = leadRect.x;
1975                            Point p = new Point(visRect.x + visRect.width,
1976                                    leadRect.y);
1977                            index = list.locationToIndex(p);
1978                            Rectangle cellBounds = list.getCellBounds(index,
1979                                    index);
1980                            if (visRect.intersects(cellBounds)) {
1981                                p.x = cellBounds.x + cellBounds.width;
1982                                index = list.locationToIndex(p);
1983                                cellBounds = list.getCellBounds(index, index);
1984                            }
1985                            if (cellBounds.y != leadRect.y) {
1986                                p.x = cellBounds.x - 1;
1987                                index = list.locationToIndex(p);
1988                            }
1989                        }
1990                    } else {
1991                        if (direction < 0) {
1992                            // up
1993                            // go to the first visible cell
1994                            Point p = new Point(leadRect.x, visRect.y);
1995                            index = list.locationToIndex(p);
1996                            if (lead <= index) {
1997                                // if lead is the first visible cell (or above it)
1998                                // adjust the visible rect up
1999                                visRect.y = leadRect.y + leadRect.height
2000                                        - visRect.height;
2001                                p.y = visRect.y;
2002                                index = list.locationToIndex(p);
2003                                Rectangle cellBounds = list.getCellBounds(
2004                                        index, index);
2005                                // go one cell down if first visible cell doesn't fit
2006                                // into adjasted visible rectangle
2007                                if (cellBounds.y < visRect.y) {
2008                                    p.y = cellBounds.y + cellBounds.height;
2009                                    index = list.locationToIndex(p);
2010                                    cellBounds = list.getCellBounds(index,
2011                                            index);
2012                                }
2013                                // if index isn't less then lead
2014                                // try to go to cell previous to lead
2015                                if (cellBounds.y >= leadRect.y) {
2016                                    p.y = leadRect.y - 1;
2017                                    index = list.locationToIndex(p);
2018                                }
2019                            }
2020                        } else {
2021                            // down
2022                            // go to the last completely visible cell
2023                            Point p = new Point(leadRect.x, visRect.y
2024                                    + visRect.height - 1);
2025                            index = list.locationToIndex(p);
2026                            Rectangle cellBounds = list.getCellBounds(index,
2027                                    index);
2028                            // go up one cell if last visible cell doesn't fit
2029                            // into visible rectangle
2030                            if (cellBounds.y + cellBounds.height > visRect.y
2031                                    + visRect.height) {
2032                                p.y = cellBounds.y - 1;
2033                                index = list.locationToIndex(p);
2034                                cellBounds = list.getCellBounds(index, index);
2035                                index = Math.max(index, lead);
2036                            }
2037
2038                            if (lead >= index) {
2039                                // if lead is the last completely visible index
2040                                // (or below it) adjust the visible rect down
2041                                visRect.y = leadRect.y;
2042                                p.y = visRect.y + visRect.height - 1;
2043                                index = list.locationToIndex(p);
2044                                cellBounds = list.getCellBounds(index, index);
2045                                // go one cell up if last visible cell doesn't fit
2046                                // into adjasted visible rectangle
2047                                if (cellBounds.y + cellBounds.height > visRect.y
2048                                        + visRect.height) {
2049                                    p.y = cellBounds.y - 1;
2050                                    index = list.locationToIndex(p);
2051                                    cellBounds = list.getCellBounds(index,
2052                                            index);
2053                                }
2054                                // if index isn't greater then lead
2055                                // try to go to cell next after lead
2056                                if (cellBounds.y <= leadRect.y) {
2057                                    p.y = leadRect.y + leadRect.height;
2058                                    index = list.locationToIndex(p);
2059                                }
2060                            }
2061                        }
2062                    }
2063                    return index;
2064                }
2065
2066                private void changeSelection(JList list, int type, int index,
2067                        int direction) {
2068                    if (index >= 0 && index < list.getModel().getSize()) {
2069                        ListSelectionModel lsm = list.getSelectionModel();
2070
2071                        // CHANGE_LEAD is only valid with multiple interval selection
2072                        if (type == CHANGE_LEAD
2073                                && list.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) {
2074
2075                            type = CHANGE_SELECTION;
2076                        }
2077
2078                        // IMPORTANT - This needs to happen before the index is changed.
2079                        // This is because JFileChooser, which uses JList, also scrolls
2080                        // the selected item into view. If that happens first, then
2081                        // this method becomes a no-op.
2082                        adjustScrollPositionIfNecessary(list, index, direction);
2083
2084                        if (type == EXTEND_SELECTION) {
2085                            int anchor = adjustIndex(lsm
2086                                    .getAnchorSelectionIndex(), list);
2087                            if (anchor == -1) {
2088                                anchor = 0;
2089                            }
2090
2091                            list.setSelectionInterval(anchor, index);
2092                        } else if (type == CHANGE_SELECTION) {
2093                            list.setSelectedIndex(index);
2094                        } else {
2095                            // casting should be safe since the action is only enabled
2096                            // for DefaultListSelectionModel
2097                            ((DefaultListSelectionModel) lsm)
2098                                    .moveLeadSelectionIndex(index);
2099                        }
2100                    }
2101                }
2102
2103                /**
2104                 * When scroll down makes selected index the last completely visible
2105                 * index. When scroll up makes selected index the first visible index.
2106                 * Adjust visible rectangle respect to list's component orientation.
2107                 */
2108                private void adjustScrollPositionIfNecessary(JList list,
2109                        int index, int direction) {
2110                    if (direction == 0) {
2111                        return;
2112                    }
2113                    Rectangle cellBounds = list.getCellBounds(index, index);
2114                    Rectangle visRect = list.getVisibleRect();
2115                    if (cellBounds != null && !visRect.contains(cellBounds)) {
2116                        if (list.getLayoutOrientation() == JList.VERTICAL_WRAP
2117                                && list.getVisibleRowCount() <= 0) {
2118                            // horizontal
2119                            if (list.getComponentOrientation().isLeftToRight()) {
2120                                if (direction > 0) {
2121                                    // right for left-to-right
2122                                    int x = Math.max(0, cellBounds.x
2123                                            + cellBounds.width - visRect.width);
2124                                    int startIndex = list
2125                                            .locationToIndex(new Point(x,
2126                                                    cellBounds.y));
2127                                    Rectangle startRect = list.getCellBounds(
2128                                            startIndex, startIndex);
2129                                    if (startRect.x < x
2130                                            && startRect.x < cellBounds.x) {
2131                                        startRect.x += startRect.width;
2132                                        startIndex = list
2133                                                .locationToIndex(startRect
2134                                                        .getLocation());
2135                                        startRect = list.getCellBounds(
2136                                                startIndex, startIndex);
2137                                    }
2138                                    cellBounds = startRect;
2139                                }
2140                                cellBounds.width = visRect.width;
2141                            } else {
2142                                if (direction > 0) {
2143                                    // left for right-to-left
2144                                    int x = cellBounds.x + visRect.width;
2145                                    int rightIndex = list
2146                                            .locationToIndex(new Point(x,
2147                                                    cellBounds.y));
2148                                    Rectangle rightRect = list.getCellBounds(
2149                                            rightIndex, rightIndex);
2150                                    if (rightRect.x + rightRect.width > x
2151                                            && rightRect.x > cellBounds.x) {
2152                                        rightRect.width = 0;
2153                                    }
2154                                    cellBounds.x = Math.max(0, rightRect.x
2155                                            + rightRect.width - visRect.width);
2156                                    cellBounds.width = visRect.width;
2157                                } else {
2158                                    cellBounds.x += Math.max(0,
2159                                            cellBounds.width - visRect.width);
2160                                    // adjust width to fit into visible rectangle
2161                                    cellBounds.width = Math.min(
2162                                            cellBounds.width, visRect.width);
2163                                }
2164                            }
2165                        } else {
2166                            // vertical
2167                            if (direction > 0) {
2168                                //down
2169                                int y = Math.max(0, cellBounds.y
2170                                        + cellBounds.height - visRect.height);
2171                                int startIndex = list
2172                                        .locationToIndex(new Point(
2173                                                cellBounds.x, y));
2174                                Rectangle startRect = list.getCellBounds(
2175                                        startIndex, startIndex);
2176                                if (startRect.y < y
2177                                        && startRect.y < cellBounds.y) {
2178                                    startRect.y += startRect.height;
2179                                    startIndex = list.locationToIndex(startRect
2180                                            .getLocation());
2181                                    startRect = list.getCellBounds(startIndex,
2182                                            startIndex);
2183                                }
2184                                cellBounds = startRect;
2185                                cellBounds.height = visRect.height;
2186                            } else {
2187                                // adjust height to fit into visible rectangle
2188                                cellBounds.height = Math.min(cellBounds.height,
2189                                        visRect.height);
2190                            }
2191                        }
2192                        list.scrollRectToVisible(cellBounds);
2193                    }
2194                }
2195
2196                private int getNextColumnIndex(JList list, BasicListUI ui,
2197                        int amount) {
2198                    if (list.getLayoutOrientation() != JList.VERTICAL) {
2199                        int index = adjustIndex(list.getLeadSelectionIndex(),
2200                                list);
2201                        int size = list.getModel().getSize();
2202
2203                        if (index == -1) {
2204                            return 0;
2205                        } else if (size == 1) {
2206                            // there's only one item so we should select it
2207                            return 0;
2208                        } else if (ui == null || ui.columnCount <= 1) {
2209                            return -1;
2210                        }
2211
2212                        int column = ui.convertModelToColumn(index);
2213                        int row = ui.convertModelToRow(index);
2214
2215                        column += amount;
2216                        if (column >= ui.columnCount || column < 0) {
2217                            // No wrapping.
2218                            return -1;
2219                        }
2220                        int maxRowCount = ui.getRowCount(column);
2221                        if (row >= maxRowCount) {
2222                            return -1;
2223                        }
2224                        return ui.getModelIndex(column, row);
2225                    }
2226                    // Won't change the selection.
2227                    return -1;
2228                }
2229
2230                private int getNextIndex(JList list, BasicListUI ui, int amount) {
2231                    int index = adjustIndex(list.getLeadSelectionIndex(), list);
2232                    int size = list.getModel().getSize();
2233
2234                    if (index == -1) {
2235                        if (size > 0) {
2236                            if (amount > 0) {
2237                                index = 0;
2238                            } else {
2239                                index = size - 1;
2240                            }
2241                        }
2242                    } else if (size == 1) {
2243                        // there's only one item so we should select it
2244                        index = 0;
2245                    } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
2246                        if (ui != null) {
2247                            index += ui.columnCount * amount;
2248                        }
2249                    } else {
2250                        index += amount;
2251                    }
2252
2253                    return index;
2254                }
2255            }
2256
2257            private class Handler implements  FocusListener, KeyListener,
2258                    ListDataListener, ListSelectionListener,
2259                    MouseInputListener, PropertyChangeListener, BeforeDrag {
2260                //
2261                // KeyListener
2262                //
2263                private String prefix = "";
2264                private String typedString = "";
2265                private long lastTime = 0L;
2266
2267                /**
2268                 * Invoked when a key has been typed.
2269                 * 
2270                 * Moves the keyboard focus to the first element whose prefix matches the
2271                 * sequence of alphanumeric keys pressed by the user with delay less
2272                 * than value of <code>timeFactor</code> property (or 1000 milliseconds
2273                 * if it is not defined). Subsequent same key presses move the keyboard
2274                 * focus to the next object that starts with the same letter until another
2275                 * key is pressed, then it is treated as the prefix with appropriate number
2276                 * of the same letters followed by first typed another letter.
2277                 */
2278                public void keyTyped(KeyEvent e) {
2279                    JList src = (JList) e.getSource();
2280                    ListModel model = src.getModel();
2281
2282                    if (model.getSize() == 0 || e.isAltDown()
2283                            || e.isControlDown() || e.isMetaDown()
2284                            || isNavigationKey(e)) {
2285                        // Nothing to select
2286                        return;
2287                    }
2288                    boolean startingFromSelection = true;
2289
2290                    char c = e.getKeyChar();
2291
2292                    long time = e.getWhen();
2293                    int startIndex = adjustIndex(src.getLeadSelectionIndex(),
2294                            list);
2295                    if (time - lastTime < timeFactor) {
2296                        typedString += c;
2297                        if ((prefix.length() == 1) && (c == prefix.charAt(0))) {
2298                            // Subsequent same key presses move the keyboard focus to the next 
2299                            // object that starts with the same letter.
2300                            startIndex++;
2301                        } else {
2302                            prefix = typedString;
2303                        }
2304                    } else {
2305                        startIndex++;
2306                        typedString = "" + c;
2307                        prefix = typedString;
2308                    }
2309                    lastTime = time;
2310
2311                    if (startIndex < 0 || startIndex >= model.getSize()) {
2312                        startingFromSelection = false;
2313                        startIndex = 0;
2314                    }
2315                    int index = src.getNextMatch(prefix, startIndex,
2316                            Position.Bias.Forward);
2317                    if (index >= 0) {
2318                        src.setSelectedIndex(index);
2319                        src.ensureIndexIsVisible(index);
2320                    } else if (startingFromSelection) { // wrap
2321                        index = src.getNextMatch(prefix, 0,
2322                                Position.Bias.Forward);
2323                        if (index >= 0) {
2324                            src.setSelectedIndex(index);
2325                            src.ensureIndexIsVisible(index);
2326                        }
2327                    }
2328                }
2329
2330                /**
2331                 * Invoked when a key has been pressed.
2332                 *
2333                 * Checks to see if the key event is a navigation key to prevent
2334                 * dispatching these keys for the first letter navigation.
2335                 */
2336                public void keyPressed(KeyEvent e) {
2337                    if (isNavigationKey(e)) {
2338                        prefix = "";
2339                        typedString = "";
2340                        lastTime = 0L;
2341                    }
2342                }
2343
2344                /**
2345                 * Invoked when a key has been released.
2346                 * See the class description for {@link KeyEvent} for a definition of 
2347                 * a key released event.
2348                 */
2349                public void keyReleased(KeyEvent e) {
2350                }
2351
2352                /**
2353                 * Returns whether or not the supplied key event maps to a key that is used for
2354                 * navigation.  This is used for optimizing key input by only passing non-
2355                 * navigation keys to the first letter navigation mechanism.
2356                 */
2357                private boolean isNavigationKey(KeyEvent event) {
2358                    InputMap inputMap = list
2359                            .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
2360                    KeyStroke key = KeyStroke.getKeyStrokeForEvent(event);
2361
2362                    if (inputMap != null && inputMap.get(key) != null) {
2363                        return true;
2364                    }
2365                    return false;
2366                }
2367
2368                // 
2369                // PropertyChangeListener
2370                //
2371                public void propertyChange(PropertyChangeEvent e) {
2372                    String propertyName = e.getPropertyName();
2373
2374                    /* If the JList.model property changes, remove our listener,
2375                     * listDataListener from the old model and add it to the new one.
2376                     */
2377                    if (propertyName == "model") {
2378                        ListModel oldModel = (ListModel) e.getOldValue();
2379                        ListModel newModel = (ListModel) e.getNewValue();
2380                        if (oldModel != null) {
2381                            oldModel.removeListDataListener(listDataListener);
2382                        }
2383                        if (newModel != null) {
2384                            newModel.addListDataListener(listDataListener);
2385                        }
2386                        updateLayoutStateNeeded |= modelChanged;
2387                        redrawList();
2388                    }
2389
2390                    /* If the JList.selectionModel property changes, remove our listener,
2391                     * listSelectionListener from the old selectionModel and add it to the new one.
2392                     */
2393                    else if (propertyName == "selectionModel") {
2394                        ListSelectionModel oldModel = (ListSelectionModel) e
2395                                .getOldValue();
2396                        ListSelectionModel newModel = (ListSelectionModel) e
2397                                .getNewValue();
2398                        if (oldModel != null) {
2399                            oldModel
2400                                    .removeListSelectionListener(listSelectionListener);
2401                        }
2402                        if (newModel != null) {
2403                            newModel
2404                                    .addListSelectionListener(listSelectionListener);
2405                        }
2406                        updateLayoutStateNeeded |= modelChanged;
2407                        redrawList();
2408                    } else if (propertyName == "cellRenderer") {
2409                        updateLayoutStateNeeded |= cellRendererChanged;
2410                        redrawList();
2411                    } else if (propertyName == "font") {
2412                        updateLayoutStateNeeded |= fontChanged;
2413                        redrawList();
2414                    } else if (propertyName == "prototypeCellValue") {
2415                        updateLayoutStateNeeded |= prototypeCellValueChanged;
2416                        redrawList();
2417                    } else if (propertyName == "fixedCellHeight") {
2418                        updateLayoutStateNeeded |= fixedCellHeightChanged;
2419                        redrawList();
2420                    } else if (propertyName == "fixedCellWidth") {
2421                        updateLayoutStateNeeded |= fixedCellWidthChanged;
2422                        redrawList();
2423                    } else if (propertyName == "selectionForeground") {
2424                        list.repaint();
2425                    } else if (propertyName == "selectionBackground") {
2426                        list.repaint();
2427                    } else if ("layoutOrientation" == propertyName) {
2428                        updateLayoutStateNeeded |= layoutOrientationChanged;
2429                        layoutOrientation = list.getLayoutOrientation();
2430                        redrawList();
2431                    } else if ("visibleRowCount" == propertyName) {
2432                        if (layoutOrientation != JList.VERTICAL) {
2433                            updateLayoutStateNeeded |= layoutOrientationChanged;
2434                            redrawList();
2435                        }
2436                    } else if ("componentOrientation" == propertyName) {
2437                        isLeftToRight = list.getComponentOrientation()
2438                                .isLeftToRight();
2439                        updateLayoutStateNeeded |= componentOrientationChanged;
2440                        redrawList();
2441
2442                        InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
2443                        SwingUtilities.replaceUIInputMap(list,
2444                                JComponent.WHEN_FOCUSED, inputMap);
2445                    } else if ("List.isFileList" == propertyName) {
2446                        updateIsFileList();
2447                        redrawList();
2448                    } else if ("dropLocation" == propertyName) {
2449                        JList.DropLocation oldValue = (JList.DropLocation) e
2450                                .getOldValue();
2451                        repaintDropLocation(oldValue);
2452                        repaintDropLocation(list.getDropLocation());
2453                    }
2454                }
2455
2456                private void repaintDropLocation(JList.DropLocation loc) {
2457                    if (loc == null) {
2458                        return;
2459                    }
2460
2461                    Rectangle r;
2462
2463                    if (loc.isInsert()) {
2464                        r = getDropLineRect(loc);
2465                    } else {
2466                        r = getCellBounds(list, loc.getIndex());
2467                    }
2468
2469                    if (r != null) {
2470                        list.repaint(r);
2471                    }
2472                }
2473
2474                //
2475                // ListDataListener
2476                //
2477                public void intervalAdded(ListDataEvent e) {
2478                    updateLayoutStateNeeded = modelChanged;
2479
2480                    int minIndex = Math.min(e.getIndex0(), e.getIndex1());
2481                    int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
2482
2483                    /* Sync the SelectionModel with the DataModel.
2484                     */
2485
2486                    ListSelectionModel sm = list.getSelectionModel();
2487                    if (sm != null) {
2488                        sm.insertIndexInterval(minIndex, maxIndex - minIndex
2489                                + 1, true);
2490                    }
2491
2492                    /* Repaint the entire list, from the origin of
2493                     * the first added cell, to the bottom of the
2494                     * component.
2495                     */
2496                    redrawList();
2497                }
2498
2499                public void intervalRemoved(ListDataEvent e) {
2500                    updateLayoutStateNeeded = modelChanged;
2501
2502                    /* Sync the SelectionModel with the DataModel.
2503                     */
2504
2505                    ListSelectionModel sm = list.getSelectionModel();
2506                    if (sm != null) {
2507                        sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
2508                    }
2509
2510                    /* Repaint the entire list, from the origin of
2511                     * the first removed cell, to the bottom of the
2512                     * component.
2513                     */
2514
2515                    redrawList();
2516                }
2517
2518                public void contentsChanged(ListDataEvent e) {
2519                    updateLayoutStateNeeded = modelChanged;
2520                    redrawList();
2521                }
2522
2523                //
2524                // ListSelectionListener
2525                //
2526                public void valueChanged(ListSelectionEvent e) {
2527                    maybeUpdateLayoutState();
2528
2529                    int size = list.getModel().getSize();
2530                    int firstIndex = Math.min(size - 1, Math.max(e
2531                            .getFirstIndex(), 0));
2532                    int lastIndex = Math.min(size - 1, Math.max(e
2533                            .getLastIndex(), 0));
2534
2535                    Rectangle bounds = getCellBounds(list, firstIndex,
2536                            lastIndex);
2537
2538                    if (bounds != null) {
2539                        list.repaint(bounds.x, bounds.y, bounds.width,
2540                                bounds.height);
2541                    }
2542                }
2543
2544                //
2545                // MouseListener
2546                //
2547                public void mouseClicked(MouseEvent e) {
2548                }
2549
2550                public void mouseEntered(MouseEvent e) {
2551                }
2552
2553                public void mouseExited(MouseEvent e) {
2554                }
2555
2556                // Whether or not the mouse press (which is being considered as part
2557                // of a drag sequence) also caused the selection change to be fully
2558                // processed.
2559                private boolean dragPressDidSelection;
2560
2561                public void mousePressed(MouseEvent e) {
2562                    if (SwingUtilities2.shouldIgnore(e, list)) {
2563                        return;
2564                    }
2565
2566                    boolean dragEnabled = list.getDragEnabled();
2567                    boolean grabFocus = true;
2568
2569                    // different behavior if drag is enabled
2570                    if (dragEnabled) {
2571                        int row = SwingUtilities2.loc2IndexFileList(list, e
2572                                .getPoint());
2573                        // if we have a valid row and this is a drag initiating event
2574                        if (row != -1 && DragRecognitionSupport.mousePressed(e)) {
2575                            dragPressDidSelection = false;
2576
2577                            if (e.isControlDown()) {
2578                                // do nothing for control - will be handled on release
2579                                // or when drag starts
2580                                return;
2581                            } else if (!e.isShiftDown()
2582                                    && list.isSelectedIndex(row)) {
2583                                // clicking on something that's already selected
2584                                // and need to make it the lead now
2585                                list.addSelectionInterval(row, row);
2586                                return;
2587                            }
2588
2589                            // could be a drag initiating event - don't grab focus
2590                            grabFocus = false;
2591
2592                            dragPressDidSelection = true;
2593                        }
2594                    } else {
2595                        // When drag is enabled mouse drags won't change the selection
2596                        // in the list, so we only set the isAdjusting flag when it's
2597                        // not enabled
2598                        list.setValueIsAdjusting(true);
2599                    }
2600
2601                    if (grabFocus) {
2602                        SwingUtilities2.adjustFocus(list);
2603                    }
2604
2605                    adjustSelection(e);
2606                }
2607
2608                private void adjustSelection(MouseEvent e) {
2609                    int row = SwingUtilities2.loc2IndexFileList(list, e
2610                            .getPoint());
2611                    if (row < 0) {
2612                        // If shift is down in multi-select, we should do nothing.
2613                        // For single select or non-shift-click, clear the selection
2614                        if (isFileList
2615                                && e.getID() == MouseEvent.MOUSE_PRESSED
2616                                && (!e.isShiftDown() || list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
2617                            list.clearSelection();
2618                        }
2619                    } else {
2620                        int anchorIndex = adjustIndex(list
2621                                .getAnchorSelectionIndex(), list);
2622                        boolean anchorSelected;
2623                        if (anchorIndex == -1) {
2624                            anchorIndex = 0;
2625                            anchorSelected = false;
2626                        } else {
2627                            anchorSelected = list.isSelectedIndex(anchorIndex);
2628                        }
2629
2630                        if (e.isControlDown()) {
2631                            if (e.isShiftDown()) {
2632                                if (anchorSelected) {
2633                                    list.addSelectionInterval(anchorIndex, row);
2634                                } else {
2635                                    list.removeSelectionInterval(anchorIndex,
2636                                            row);
2637                                    if (isFileList) {
2638                                        list.addSelectionInterval(row, row);
2639                                        list.getSelectionModel()
2640                                                .setAnchorSelectionIndex(
2641                                                        anchorIndex);
2642                                    }
2643                                }
2644                            } else if (list.isSelectedIndex(row)) {
2645                                list.removeSelectionInterval(row, row);
2646                            } else {
2647                                list.addSelectionInterval(row, row);
2648                            }
2649                        } else if (e.isShiftDown()) {
2650                            list.setSelectionInterval(anchorIndex, row);
2651                        } else {
2652                            list.setSelectionInterval(row, row);
2653                        }
2654                    }
2655                }
2656
2657                public void dragStarting(MouseEvent me) {
2658                    if (me.isControlDown()) {
2659                        int row = SwingUtilities2.loc2IndexFileList(list, me
2660                                .getPoint());
2661                        list.addSelectionInterval(row, row);
2662                    }
2663                }
2664
2665                public void mouseDragged(MouseEvent e) {
2666                    if (SwingUtilities2.shouldIgnore(e, list)) {
2667                        return;
2668                    }
2669
2670                    if (list.getDragEnabled()) {
2671                        DragRecognitionSupport.mouseDragged(e, this );
2672                        return;
2673                    }
2674
2675                    if (e.isShiftDown() || e.isControlDown()) {
2676                        return;
2677                    }
2678
2679                    int row = locationToIndex(list, e.getPoint());
2680                    if (row != -1) {
2681                        // 4835633.  Dragging onto a File should not select it.
2682                        if (isFileList) {
2683                            return;
2684                        }
2685                        Rectangle cellBounds = getCellBounds(list, row, row);
2686                        if (cellBounds != null) {
2687                            list.scrollRectToVisible(cellBounds);
2688                            list.setSelectionInterval(row, row);
2689                        }
2690                    }
2691                }
2692
2693                public void mouseMoved(MouseEvent e) {
2694                }
2695
2696                public void mouseReleased(MouseEvent e) {
2697                    if (SwingUtilities2.shouldIgnore(e, list)) {
2698                        return;
2699                    }
2700
2701                    if (list.getDragEnabled()) {
2702                        MouseEvent me = DragRecognitionSupport.mouseReleased(e);
2703                        if (me != null) {
2704                            SwingUtilities2.adjustFocus(list);
2705                            if (!dragPressDidSelection) {
2706                                adjustSelection(me);
2707                            }
2708                        }
2709                    } else {
2710                        list.setValueIsAdjusting(false);
2711                    }
2712                }
2713
2714                //
2715                // FocusListener
2716                //
2717                protected void repaintCellFocus() {
2718                    int leadIndex = adjustIndex(list.getLeadSelectionIndex(),
2719                            list);
2720                    if (leadIndex != -1) {
2721                        Rectangle r = getCellBounds(list, leadIndex, leadIndex);
2722                        if (r != null) {
2723                            list.repaint(r.x, r.y, r.width, r.height);
2724                        }
2725                    }
2726                }
2727
2728                /* The focusGained() focusLost() methods run when the JList
2729                 * focus changes.
2730                 */
2731
2732                public void focusGained(FocusEvent e) {
2733                    repaintCellFocus();
2734                }
2735
2736                public void focusLost(FocusEvent e) {
2737                    repaintCellFocus();
2738                }
2739            }
2740
2741            private static int adjustIndex(int index, JList list) {
2742                return index < list.getModel().getSize() ? index : -1;
2743            }
2744
2745            private static final TransferHandler defaultTransferHandler = new ListTransferHandler();
2746
2747            static class ListTransferHandler extends TransferHandler implements 
2748                    UIResource {
2749
2750                /**
2751                 * Create a Transferable to use as the source for a data transfer.
2752                 *
2753                 * @param c  The component holding the data to be transfered.  This
2754                 *  argument is provided to enable sharing of TransferHandlers by
2755                 *  multiple components.
2756                 * @return  The representation of the data to be transfered. 
2757                 *  
2758                 */
2759                protected Transferable createTransferable(JComponent c) {
2760                    if (c instanceof  JList) {
2761                        JList list = (JList) c;
2762                        Object[] values = list.getSelectedValues();
2763
2764                        if (values == null || values.length == 0) {
2765                            return null;
2766                        }
2767
2768                        StringBuffer plainBuf = new StringBuffer();
2769                        StringBuffer htmlBuf = new StringBuffer();
2770
2771                        htmlBuf.append("<html>\n<body>\n<ul>\n");
2772
2773                        for (int i = 0; i < values.length; i++) {
2774                            Object obj = values[i];
2775                            String val = ((obj == null) ? "" : obj.toString());
2776                            plainBuf.append(val + "\n");
2777                            htmlBuf.append("  <li>" + val + "\n");
2778                        }
2779
2780                        // remove the last newline
2781                        plainBuf.deleteCharAt(plainBuf.length() - 1);
2782                        htmlBuf.append("</ul>\n</body>\n</html>");
2783
2784                        return new BasicTransferable(plainBuf.toString(),
2785                                htmlBuf.toString());
2786                    }
2787
2788                    return null;
2789                }
2790
2791                public int getSourceActions(JComponent c) {
2792                    return COPY;
2793                }
2794
2795            }
2796        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.