2880 lines · 105.5 KB · Java
RawDownload
/* * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.plaf.basic; import sun.swing.DefaultLookup;import sun.swing.UIAction; import javax.swing.*;import javax.swing.event.*;import javax.swing.plaf.*;import javax.swing.text.Position; import java.awt.*;import java.awt.event.*;import java.awt.datatransfer.Transferable;import java.awt.geom.Point2D; import java.beans.PropertyChangeListener;import java.beans.PropertyChangeEvent; import sun.swing.SwingUtilities2;import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag; /** * An extensible implementation of {@code ListUI}. * <p> * {@code BasicListUI} instances cannot be shared between multiple * lists. * * @author Hans Muller * @author Philip Milne * @author Shannon Hickey (drag and drop) */public class BasicListUI extends ListUI{    private static final StringBuilder BASELINE_COMPONENT_KEY =        new StringBuilder("List.baselineComponent");     protected JList list = null;    protected CellRendererPane rendererPane;     // Listeners that this UI attaches to the JList    protected FocusListener focusListener;    protected MouseInputListener mouseInputListener;    protected ListSelectionListener listSelectionListener;    protected ListDataListener listDataListener;    protected PropertyChangeListener propertyChangeListener;    private Handler handler;     protected int[] cellHeights = null;    protected int cellHeight = -1;    protected int cellWidth = -1;    protected int updateLayoutStateNeeded = modelChanged;    /**     * Height of the list. When asked to paint, if the current size of     * the list differs, this will update the layout state.     */    private int listHeight;     /**     * Width of the list. When asked to paint, if the current size of     * the list differs, this will update the layout state.     */    private int listWidth;     /**     * The layout orientation of the list.     */    private int layoutOrientation;     // Following ivars are used if the list is laying out horizontally     /**     * Number of columns to create.     */    private int columnCount;    /**     * Preferred height to make the list, this is only used if the     * the list is layed out horizontally.     */    private int preferredHeight;    /**     * Number of rows per column. This is only used if the row height is     * fixed.     */    private int rowsPerColumn;     /**     * The time factor to treate the series of typed alphanumeric key     * as prefix for first letter navigation.     */    private long timeFactor = 1000L;     /**     * Local cache of JList's client property "List.isFileList"     */    private boolean isFileList = false;     /**     * Local cache of JList's component orientation property     */    private boolean isLeftToRight = true;     /* The bits below define JList property changes that affect layout.     * When one of these properties changes we set a bit in     * updateLayoutStateNeeded.  The change is dealt with lazily, see     * maybeUpdateLayoutState.  Changes to the JLists model, e.g. the     * models length changed, are handled similarly, see DataListener.     */     protected final static int modelChanged = 1 << 0;    protected final static int selectionModelChanged = 1 << 1;    protected final static int fontChanged = 1 << 2;    protected final static int fixedCellWidthChanged = 1 << 3;    protected final static int fixedCellHeightChanged = 1 << 4;    protected final static int prototypeCellValueChanged = 1 << 5;    protected final static int cellRendererChanged = 1 << 6;    private final static int layoutOrientationChanged = 1 << 7;    private final static int heightChanged = 1 << 8;    private final static int widthChanged = 1 << 9;    private final static int componentOrientationChanged = 1 << 10;     private static final int DROP_LINE_THICKNESS = 2;     static void loadActionMap(LazyActionMap map) {        map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN));        map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND));        map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD));        map.put(new Actions(Actions.SELECT_NEXT_COLUMN));        map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND));        map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD));        map.put(new Actions(Actions.SELECT_PREVIOUS_ROW));        map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND));        map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD));        map.put(new Actions(Actions.SELECT_NEXT_ROW));        map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND));        map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD));        map.put(new Actions(Actions.SELECT_FIRST_ROW));        map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND));        map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD));        map.put(new Actions(Actions.SELECT_LAST_ROW));        map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND));        map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD));        map.put(new Actions(Actions.SCROLL_UP));        map.put(new Actions(Actions.SCROLL_UP_EXTEND));        map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD));        map.put(new Actions(Actions.SCROLL_DOWN));        map.put(new Actions(Actions.SCROLL_DOWN_EXTEND));        map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD));        map.put(new Actions(Actions.SELECT_ALL));        map.put(new Actions(Actions.CLEAR_SELECTION));        map.put(new Actions(Actions.ADD_TO_SELECTION));        map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));        map.put(new Actions(Actions.EXTEND_TO));        map.put(new Actions(Actions.MOVE_SELECTION_TO));         map.put(TransferHandler.getCutAction().getValue(Action.NAME),                TransferHandler.getCutAction());        map.put(TransferHandler.getCopyAction().getValue(Action.NAME),                TransferHandler.getCopyAction());        map.put(TransferHandler.getPasteAction().getValue(Action.NAME),                TransferHandler.getPasteAction());    }     /**     * Paint one List cell: compute the relevant state, get the "rubber stamp"     * cell renderer component, and then use the CellRendererPane to paint it.     * Subclasses may want to override this method rather than paint().     *     * @see #paint     */    protected void paintCell(        Graphics g,        int row,        Rectangle rowBounds,        ListCellRenderer cellRenderer,        ListModel dataModel,        ListSelectionModel selModel,        int leadIndex)    {        Object value = dataModel.getElementAt(row);        boolean cellHasFocus = list.hasFocus() && (row == leadIndex);        boolean isSelected = selModel.isSelectedIndex(row);         Component rendererComponent =            cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);         int cx = rowBounds.x;        int cy = rowBounds.y;        int cw = rowBounds.width;        int ch = rowBounds.height;         if (isFileList) {            // Shrink renderer to preferred size. This is mostly used on Windows            // where selection is only shown around the file name, instead of            // across the whole list cell.            int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4);            if (!isLeftToRight) {                cx += (cw - w);            }            cw = w;        }         rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);    }      /**     * Paint the rows that intersect the Graphics objects clipRect.  This     * method calls paintCell as necessary.  Subclasses     * may want to override these methods.     *     * @see #paintCell     */    public void paint(Graphics g, JComponent c) {        Shape clip = g.getClip();        paintImpl(g, c);        g.setClip(clip);         paintDropLine(g);    }     private void paintImpl(Graphics g, JComponent c)    {        switch (layoutOrientation) {        case JList.VERTICAL_WRAP:            if (list.getHeight() != listHeight) {                updateLayoutStateNeeded |= heightChanged;                redrawList();            }            break;        case JList.HORIZONTAL_WRAP:            if (list.getWidth() != listWidth) {                updateLayoutStateNeeded |= widthChanged;                redrawList();            }            break;        default:            break;        }        maybeUpdateLayoutState();         ListCellRenderer renderer = list.getCellRenderer();        ListModel dataModel = list.getModel();        ListSelectionModel selModel = list.getSelectionModel();        int size;         if ((renderer == null) || (size = dataModel.getSize()) == 0) {            return;        }         // Determine how many columns we need to paint        Rectangle paintBounds = g.getClipBounds();         int startColumn, endColumn;        if (c.getComponentOrientation().isLeftToRight()) {            startColumn = convertLocationToColumn(paintBounds.x,                                                  paintBounds.y);            endColumn = convertLocationToColumn(paintBounds.x +                                                paintBounds.width,                                                paintBounds.y);        } else {            startColumn = convertLocationToColumn(paintBounds.x +                                                paintBounds.width,                                                paintBounds.y);            endColumn = convertLocationToColumn(paintBounds.x,                                                  paintBounds.y);        }        int maxY = paintBounds.y + paintBounds.height;        int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list);        int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ?                           columnCount : 1;          for (int colCounter = startColumn; colCounter <= endColumn;             colCounter++) {            // And then how many rows in this columnn            int row = convertLocationToRowInColumn(paintBounds.y, colCounter);            int rowCount = getRowCount(colCounter);            int index = getModelIndex(colCounter, row);            Rectangle rowBounds = getCellBounds(list, index, index);             if (rowBounds == null) {                // Not valid, bail!                return;            }            while (row < rowCount && rowBounds.y < maxY &&                   index < size) {                rowBounds.height = getHeight(colCounter, row);                g.setClip(rowBounds.x, rowBounds.y, rowBounds.width,                          rowBounds.height);                g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width,                           paintBounds.height);                paintCell(g, index, rowBounds, renderer, dataModel, selModel,                          leadIndex);                rowBounds.y += rowBounds.height;                index += rowIncrement;                row++;            }        }        // Empty out the renderer pane, allowing renderers to be gc'ed.        rendererPane.removeAll();    }     private void paintDropLine(Graphics g) {        JList.DropLocation loc = list.getDropLocation();        if (loc == null || !loc.isInsert()) {            return;        }         Color c = DefaultLookup.getColor(list, this, "List.dropLineColor", null);        if (c != null) {            g.setColor(c);            Rectangle rect = getDropLineRect(loc);            g.fillRect(rect.x, rect.y, rect.width, rect.height);        }    }     private Rectangle getDropLineRect(JList.DropLocation loc) {        int size = list.getModel().getSize();         if (size == 0) {            Insets insets = list.getInsets();            if (layoutOrientation == JList.HORIZONTAL_WRAP) {                if (isLeftToRight) {                    return new Rectangle(insets.left, insets.top, DROP_LINE_THICKNESS, 20);                } else {                    return new Rectangle(list.getWidth() - DROP_LINE_THICKNESS - insets.right,                                         insets.top, DROP_LINE_THICKNESS, 20);                }            } else {                return new Rectangle(insets.left, insets.top,                                     list.getWidth() - insets.left - insets.right,                                     DROP_LINE_THICKNESS);            }        }         Rectangle rect = null;        int index = loc.getIndex();        boolean decr = false;         if (layoutOrientation == JList.HORIZONTAL_WRAP) {            if (index == size) {                decr = true;            } else if (index != 0 && convertModelToRow(index)                                         != convertModelToRow(index - 1)) {                 Rectangle prev = getCellBounds(list, index - 1);                Rectangle me = getCellBounds(list, index);                Point p = loc.getDropPoint();                 if (isLeftToRight) {                    decr = Point2D.distance(prev.x + prev.width,                                            prev.y + (int)(prev.height / 2.0),                                            p.x, p.y)                           < Point2D.distance(me.x,                                              me.y + (int)(me.height / 2.0),                                              p.x, p.y);                } else {                    decr = Point2D.distance(prev.x,                                            prev.y + (int)(prev.height / 2.0),                                            p.x, p.y)                           < Point2D.distance(me.x + me.width,                                              me.y + (int)(prev.height / 2.0),                                              p.x, p.y);                }            }             if (decr) {                index--;                rect = getCellBounds(list, index);                if (isLeftToRight) {                    rect.x += rect.width;                } else {                    rect.x -= DROP_LINE_THICKNESS;                }            } else {                rect = getCellBounds(list, index);                if (!isLeftToRight) {                    rect.x += rect.width - DROP_LINE_THICKNESS;                }            }             if (rect.x >= list.getWidth()) {                rect.x = list.getWidth() - DROP_LINE_THICKNESS;            } else if (rect.x < 0) {                rect.x = 0;            }             rect.width = DROP_LINE_THICKNESS;        } else if (layoutOrientation == JList.VERTICAL_WRAP) {            if (index == size) {                index--;                rect = getCellBounds(list, index);                rect.y += rect.height;            } else if (index != 0 && convertModelToColumn(index)                                         != convertModelToColumn(index - 1)) {                 Rectangle prev = getCellBounds(list, index - 1);                Rectangle me = getCellBounds(list, index);                Point p = loc.getDropPoint();                if (Point2D.distance(prev.x + (int)(prev.width / 2.0),                                     prev.y + prev.height,                                     p.x, p.y)                        < Point2D.distance(me.x + (int)(me.width / 2.0),                                           me.y,                                           p.x, p.y)) {                     index--;                    rect = getCellBounds(list, index);                    rect.y += rect.height;                } else {                    rect = getCellBounds(list, index);                }            } else {                rect = getCellBounds(list, index);            }             if (rect.y >= list.getHeight()) {                rect.y = list.getHeight() - DROP_LINE_THICKNESS;            }             rect.height = DROP_LINE_THICKNESS;        } else {            if (index == size) {                index--;                rect = getCellBounds(list, index);                rect.y += rect.height;            } else {                rect = getCellBounds(list, index);            }             if (rect.y >= list.getHeight()) {                rect.y = list.getHeight() - DROP_LINE_THICKNESS;            }             rect.height = DROP_LINE_THICKNESS;        }         return rect;    }     /**     * Returns the baseline.     *     * @throws NullPointerException {@inheritDoc}     * @throws IllegalArgumentException {@inheritDoc}     * @see javax.swing.JComponent#getBaseline(int, int)     * @since 1.6     */    public int getBaseline(JComponent c, int width, int height) {        super.getBaseline(c, width, height);        int rowHeight = list.getFixedCellHeight();        UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();        Component renderer = (Component)lafDefaults.get(                BASELINE_COMPONENT_KEY);        if (renderer == null) {            ListCellRenderer lcr = (ListCellRenderer)UIManager.get(                    "List.cellRenderer");             // fix for 6711072 some LAFs like Nimbus do not provide this            // UIManager key and we should not through a NPE here because of it            if (lcr == null) {                lcr = new DefaultListCellRenderer();            }            renderer = lcr.getListCellRendererComponent(                    list, "a", -1, false, false);            lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);        }        renderer.setFont(list.getFont());        // JList actually has much more complex behavior here.        // If rowHeight != -1 the rowHeight is either the max of all cell        // heights (layout orientation != VERTICAL), or is variable depending        // upon the cell.  We assume a default size.        // We could theoretically query the real renderer, but that would        // not work for an empty model and the results may vary with        // the content.

Lines 1–500 of 2880.