diff options
author | tromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-07-16 00:30:23 +0000 |
---|---|---|
committer | tromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-07-16 00:30:23 +0000 |
commit | c8875fb97fc03779a5bba09872227b1d08e5d52a (patch) | |
tree | a0b991cf5866ae1d616639b906ac001811d74508 /libjava/classpath/javax/swing/plaf/basic/BasicListUI.java | |
parent | c40c1730800ed292b6db39a83d592476fa59623c (diff) | |
download | ppe42-gcc-c8875fb97fc03779a5bba09872227b1d08e5d52a.tar.gz ppe42-gcc-c8875fb97fc03779a5bba09872227b1d08e5d52a.zip |
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@102074 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicListUI.java')
-rw-r--r-- | libjava/classpath/javax/swing/plaf/basic/BasicListUI.java | 1004 |
1 files changed, 1004 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java new file mode 100644 index 00000000000..24c6cd20b61 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java @@ -0,0 +1,1004 @@ +/* BasicListUI.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.CellRendererPane; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JViewport; +import javax.swing.ListCellRenderer; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.ListUI; + +/** + * The Basic Look and Feel UI delegate for the + * JList. + */ +public class BasicListUI extends ListUI +{ + + /** + * A helper class which listens for {@link ComponentEvent}s from + * the JList. + */ + private class ComponentHandler extends ComponentAdapter { + + /** + * Called when the component is hidden. Invalidates the internal + * layout. + */ + public void componentResized(ComponentEvent ev) { + BasicListUI.this.damageLayout(); + } + } + + /** + * A helper class which listens for {@link FocusEvent}s + * from the JList. + */ + public class FocusHandler implements FocusListener + { + /** + * Called when the JList acquires focus. + * + * @param e The FocusEvent representing focus acquisition + */ + public void focusGained(FocusEvent e) + { + repaintCellFocus(); + } + + /** + * Called when the JList loses focus. + * + * @param e The FocusEvent representing focus loss + */ + public void focusLost(FocusEvent e) + { + repaintCellFocus(); + } + + /** + * Helper method to repaint the focused cell's + * lost or acquired focus state. + */ + void repaintCellFocus() + { + } + } + + /** + * A helper class which listens for {@link ListDataEvent}s generated by + * the {@link JList}'s {@link ListModel}. + * + * @see javax.swing.JList#getModel() + */ + public class ListDataHandler implements ListDataListener + { + /** + * Called when a general change has happened in the model which cannot + * be represented in terms of a simple addition or deletion. + * + * @param e The event representing the change + */ + public void contentsChanged(ListDataEvent e) + { + BasicListUI.this.damageLayout(); + } + + /** + * Called when an interval of objects has been added to the model. + * + * @param e The event representing the addition + */ + public void intervalAdded(ListDataEvent e) + { + BasicListUI.this.damageLayout(); + } + + /** + * Called when an inteval of objects has been removed from the model. + * + * @param e The event representing the removal + */ + public void intervalRemoved(ListDataEvent e) + { + BasicListUI.this.damageLayout(); + } + } + + /** + * A helper class which listens for {@link ListSelectionEvent}s + * from the {@link JList}'s {@link ListSelectionModel}. + */ + public class ListSelectionHandler implements ListSelectionListener + { + /** + * Called when the list selection changes. + * + * @param e The event representing the change + */ + public void valueChanged(ListSelectionEvent e) + { + } + } + + /** + * A helper class which listens for {@link KeyEvents}s + * from the {@link JList}. + */ + private class KeyHandler extends KeyAdapter + { + public KeyHandler() + { + } + + public void keyPressed( KeyEvent evt ) + { + int lead = BasicListUI.this.list.getLeadSelectionIndex(); + int max = BasicListUI.this.list.getModel().getSize() - 1; + // Do nothing if list is empty + if (max == -1) + return; + + // Process the key event. Bindings can be found in + // javax.swing.plaf.basic.BasicLookAndFeel.java + if ((evt.getKeyCode() == KeyEvent.VK_DOWN) + || (evt.getKeyCode() == KeyEvent.VK_KP_DOWN)) + { + if (!evt.isShiftDown()) + { + BasicListUI.this.list.clearSelection(); + BasicListUI.this.list.setSelectedIndex(Math.min(lead+1,max)); + } + else + { + BasicListUI.this.list.getSelectionModel(). + setLeadSelectionIndex(Math.min(lead+1,max)); + } + } + else if ((evt.getKeyCode() == KeyEvent.VK_UP) + || (evt.getKeyCode() == KeyEvent.VK_KP_UP)) + { + if (!evt.isShiftDown()) + { + BasicListUI.this.list.clearSelection(); + BasicListUI.this.list.setSelectedIndex(Math.max(lead-1,0)); + } + else + { + BasicListUI.this.list.getSelectionModel(). + setLeadSelectionIndex(Math.max(lead-1,0)); + } + } + else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP) + { + // FIXME: implement, need JList.ensureIndexIsVisible to work + } + else if (evt.getKeyCode() == KeyEvent.VK_PAGE_DOWN) + { + // FIXME: implement, need JList.ensureIndexIsVisible to work + } + else if (evt.getKeyCode() == KeyEvent.VK_BACK_SLASH + && evt.isControlDown()) + { + BasicListUI.this.list.clearSelection(); + } + else if ((evt.getKeyCode() == KeyEvent.VK_HOME) + || evt.getKeyCode() == KeyEvent.VK_END) + { + // index is either 0 for HOME, or last cell for END + int index = (evt.getKeyCode() == KeyEvent.VK_HOME) ? 0 : max; + + if (!evt.isShiftDown() ||(BasicListUI.this.list.getSelectionMode() + == ListSelectionModel.SINGLE_SELECTION)) + BasicListUI.this.list.setSelectedIndex(index); + else if (BasicListUI.this.list.getSelectionMode() == + ListSelectionModel.SINGLE_INTERVAL_SELECTION) + BasicListUI.this.list.setSelectionInterval + (BasicListUI.this.list.getAnchorSelectionIndex(), index); + else + BasicListUI.this.list.getSelectionModel(). + setLeadSelectionIndex(index); + } + else if ((evt.getKeyCode() == KeyEvent.VK_A || evt.getKeyCode() + == KeyEvent.VK_SLASH) && evt.isControlDown()) + { + BasicListUI.this.list.setSelectionInterval(0, max); + } + else if (evt.getKeyCode() == KeyEvent.VK_SPACE && evt.isControlDown()) + { + BasicListUI.this.list.getSelectionModel(). + setLeadSelectionIndex(Math.min(lead+1,max)); + } + + } + } + + /** + * A helper class which listens for {@link MouseEvent}s + * from the {@link JList}. + */ + public class MouseInputHandler implements MouseInputListener + { + /** + * Called when a mouse button press/release cycle completes + * on the {@link JList} + * + * @param event The event representing the mouse click + */ + public void mouseClicked(MouseEvent event) + { + Point click = event.getPoint(); + int index = BasicListUI.this.locationToIndex(list, click); + if (index == -1) + return; + if (event.isControlDown()) + { + if (BasicListUI.this.list.getSelectionMode() == + ListSelectionModel.SINGLE_SELECTION) + BasicListUI.this.list.setSelectedIndex(index); + else if (BasicListUI.this.list.isSelectedIndex(index)) + BasicListUI.this.list.removeSelectionInterval(index,index); + else + BasicListUI.this.list.addSelectionInterval(index,index); + } + else if (event.isShiftDown()) + { + if (BasicListUI.this.list.getSelectionMode() == + ListSelectionModel.SINGLE_SELECTION) + BasicListUI.this.list.setSelectedIndex(index); + else if (BasicListUI.this.list.getSelectionMode() == + ListSelectionModel.SINGLE_INTERVAL_SELECTION) + // COMPAT: the IBM VM is compatible with the following line of code. + // However, compliance with Sun's VM would correspond to replacing + // getAnchorSelectionIndex() with getLeadSelectionIndex().This is + // both unnatural and contradictory to the way they handle other + // similar UI interactions. + BasicListUI.this.list.setSelectionInterval + (BasicListUI.this.list.getAnchorSelectionIndex(), index); + else + // COMPAT: both Sun and IBM are compatible instead with: + // BasicListUI.this.list.setSelectionInterval + // (BasicListUI.this.list.getLeadSelectionIndex(),index); + // Note that for IBM this is contradictory to what they did in + // the above situation for SINGLE_INTERVAL_SELECTION. + // The most natural thing to do is the following: + BasicListUI.this.list.getSelectionModel(). + setLeadSelectionIndex(index); + } + else + BasicListUI.this.list.setSelectedIndex(index); + } + + /** + * Called when a mouse button is pressed down on the + * {@link JList}. + * + * @param event The event representing the mouse press + */ + public void mousePressed(MouseEvent event) + { + } + + /** + * Called when a mouse button is released on + * the {@link JList} + * + * @param event The event representing the mouse press + */ + public void mouseReleased(MouseEvent event) + { + } + + /** + * Called when the mouse pointer enters the area bounded + * by the {@link JList} + * + * @param event The event representing the mouse entry + */ + public void mouseEntered(MouseEvent event) + { + } + + /** + * Called when the mouse pointer leaves the area bounded + * by the {@link JList} + * + * @param event The event representing the mouse exit + */ + public void mouseExited(MouseEvent event) + { + } + + /** + * Called when the mouse pointer moves over the area bounded + * by the {@link JList} while a button is held down. + * + * @param event The event representing the mouse drag + */ + public void mouseDragged(MouseEvent event) + { + } + + /** + * Called when the mouse pointer moves over the area bounded + * by the {@link JList}. + * + * @param event The event representing the mouse move + */ + public void mouseMoved(MouseEvent event) + { + } + } + + /** + * Helper class which listens to {@link PropertyChangeEvent}s + * from the {@link JList}. + */ + public class PropertyChangeHandler implements PropertyChangeListener + { + /** + * Called when the {@link JList} changes one of its bound properties. + * + * @param e The event representing the property change + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getSource() == BasicListUI.this.list) + { + if (e.getOldValue() != null && e.getOldValue() instanceof ListModel) + ((ListModel) e.getOldValue()).removeListDataListener(BasicListUI.this.listDataListener); + + if (e.getNewValue() != null && e.getNewValue() instanceof ListModel) + ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener); + } + BasicListUI.this.damageLayout(); + } + } + + /** + * Creates a new BasicListUI for the component. + * + * @param c The component to create a UI for + * + * @return A new UI + */ + public static ComponentUI createUI(final JComponent c) + { + return new BasicListUI(); + } + + /** The current focus listener. */ + protected FocusListener focusListener; + + /** The data listener listening to the model. */ + protected ListDataListener listDataListener; + + /** The selection listener listening to the selection model. */ + protected ListSelectionListener listSelectionListener; + + /** The mouse listener listening to the list. */ + protected MouseInputListener mouseInputListener; + + /** The key listener listening to the list */ + private KeyHandler keyListener; + + /** The property change listener listening to the list. */ + protected PropertyChangeListener propertyChangeListener; + + + /** The component listener that receives notification for resizing the + * JList component.*/ + private ComponentListener componentListener; + + /** Saved reference to the list this UI was created for. */ + protected JList list; + + /** The height of a single cell in the list. */ + protected int cellHeight; + + /** The width of a single cell in the list. */ + protected int cellWidth; + + /** + * An array of varying heights of cells in the list, in cases where each + * cell might have a different height. + */ + protected int[] cellHeights; + + /** + * A simple counter. When nonzero, indicates that the UI class is out of + * date with respect to the underlying list, and must recalculate the + * list layout before painting or performing size calculations. + */ + protected int updateLayoutStateNeeded; + + /** + * The {@link CellRendererPane} that is used for painting. + */ + protected CellRendererPane rendererPane; + + /** + * Calculate the height of a particular row. If there is a fixed {@link + * #cellHeight}, return it; otherwise return the specific row height + * requested from the {@link #cellHeights} array. If the requested row + * is invalid, return <code>-1</code>. + * + * @param row The row to get the height of + * + * @return The height, in pixels, of the specified row + */ + protected int getRowHeight(int row) + { + if (row < 0 || row >= cellHeights.length) + return -1; + else if (cellHeight != -1) + return cellHeight; + else + return cellHeights[row]; + } + + /** + * Calculate the bounds of a particular cell, considering the upper left + * corner of the list as the origin position <code>(0,0)</code>. + * + * @param l Ignored; calculates over <code>this.list</code> + * @param index1 The first row to include in the bounds + * @param index2 The last row to incude in the bounds + * + * @return A rectangle encompassing the range of rows between + * <code>index1</code> and <code>index2</code> inclusive + */ + public Rectangle getCellBounds(JList l, int index1, int index2) + { + maybeUpdateLayoutState(); + + if (l != list || cellWidth == -1) + return null; + + int minIndex = Math.min(index1, index2); + int maxIndex = Math.max(index1, index2); + Point loc = indexToLocation(list, minIndex); + Rectangle bounds = new Rectangle(loc.x, loc.y, cellWidth, + getRowHeight(minIndex)); + + for (int i = minIndex + 1; i <= maxIndex; i++) + { + Point hiLoc = indexToLocation(list, i); + Rectangle hibounds = new Rectangle(hiLoc.x, hiLoc.y, cellWidth, + getRowHeight(i)); + bounds = bounds.union(hibounds); + } + + return bounds; + } + + /** + * Calculate the Y coordinate of the upper edge of a particular row, + * considering the Y coordinate <code>0</code> to occur at the top of the + * list. + * + * @param row The row to calculate the Y coordinate of + * + * @return The Y coordinate of the specified row, or <code>-1</code> if + * the specified row number is invalid + */ + protected int convertRowToY(int row) + { + int y = 0; + for (int i = 0; i < row; ++i) + { + int h = getRowHeight(i); + if (h == -1) + return -1; + y += h; + } + return y; + } + + /** + * Calculate the row number containing a particular Y coordinate, + * considering the Y coodrinate <code>0</code> to occur at the top of the + * list. + * + * @param y0 The Y coordinate to calculate the row number for + * + * @return The row number containing the specified Y value, or <code>-1</code> + * if the specified Y coordinate is invalid + */ + protected int convertYToRow(int y0) + { + for (int row = 0; row < cellHeights.length; ++row) + { + int h = getRowHeight(row); + + if (y0 < h) + return row; + y0 -= h; + } + return -1; + } + + /** + * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link + * #cellWidth} properties by examining the variouis properties of the + * {@link JList}. + */ + protected void updateLayoutState() + { + int nrows = list.getModel().getSize(); + cellHeight = -1; + cellWidth = -1; + if (cellHeights == null || cellHeights.length != nrows) + cellHeights = new int[nrows]; + if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1) + { + ListCellRenderer rend = list.getCellRenderer(); + for (int i = 0; i < nrows; ++i) + { + Component flyweight = rend.getListCellRendererComponent(list, + list.getModel() + .getElementAt(i), + 0, false, + false); + Dimension dim = flyweight.getPreferredSize(); + cellHeights[i] = dim.height; + // compute average cell height (little hack here) + cellHeight = (cellHeight * i + cellHeights[i]) / (i + 1); + cellWidth = Math.max(cellWidth, dim.width); + if (list.getLayoutOrientation() == JList.VERTICAL) + cellWidth = Math.max(cellWidth, list.getSize().width); + } + } + else + { + cellHeight = list.getFixedCellHeight(); + cellWidth = list.getFixedCellWidth(); + } + } + + /** + * Marks the current layout as damaged and requests revalidation from the + * JList. + * This is package-private to avoid an accessor method. + * + * @see #updateLayoutStateNeeded + */ + void damageLayout() + { + updateLayoutStateNeeded = 1; + } + + /** + * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded} + * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero. + */ + protected void maybeUpdateLayoutState() + { + if (updateLayoutStateNeeded != 0) + { + updateLayoutState(); + updateLayoutStateNeeded = 0; + } + } + + /** + * Creates a new BasicListUI object. + */ + public BasicListUI() + { + focusListener = new FocusHandler(); + listDataListener = new ListDataHandler(); + listSelectionListener = new ListSelectionHandler(); + mouseInputListener = new MouseInputHandler(); + keyListener = new KeyHandler(); + propertyChangeListener = new PropertyChangeHandler(); + componentListener = new ComponentHandler(); + updateLayoutStateNeeded = 1; + rendererPane = new CellRendererPane(); + } + + /** + * Installs various default settings (mostly colors) from the {@link + * UIDefaults} into the {@link JList} + * + * @see #uninstallDefaults + */ + protected void installDefaults() + { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + list.setForeground(defaults.getColor("List.foreground")); + list.setBackground(defaults.getColor("List.background")); + list.setSelectionForeground(defaults.getColor("List.selectionForeground")); + list.setSelectionBackground(defaults.getColor("List.selectionBackground")); + list.setOpaque(true); + } + + /** + * Resets to <code>null</code> those defaults which were installed in + * {@link #installDefaults} + */ + protected void uninstallDefaults() + { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + list.setForeground(null); + list.setBackground(null); + list.setSelectionForeground(null); + list.setSelectionBackground(null); + } + + /** + * Attaches all the listeners we have in the UI class to the {@link + * JList}, its model and its selection model. + * + * @see #uninstallListeners + */ + protected void installListeners() + { + list.addFocusListener(focusListener); + list.getModel().addListDataListener(listDataListener); + list.addListSelectionListener(listSelectionListener); + list.addMouseListener(mouseInputListener); + list.addKeyListener(keyListener); + list.addMouseMotionListener(mouseInputListener); + list.addPropertyChangeListener(propertyChangeListener); + list.addComponentListener(componentListener); + } + + /** + * Detaches all the listeners we attached in {@link #installListeners}. + */ + protected void uninstallListeners() + { + list.removeFocusListener(focusListener); + list.getModel().removeListDataListener(listDataListener); + list.removeListSelectionListener(listSelectionListener); + list.removeMouseListener(mouseInputListener); + list.removeKeyListener(keyListener); + list.removeMouseMotionListener(mouseInputListener); + list.removePropertyChangeListener(propertyChangeListener); + } + + /** + * Installs keyboard actions for this UI in the {@link JList}. + */ + protected void installKeyboardActions() + { + } + + /** + * Uninstalls keyboard actions for this UI in the {@link JList}. + */ + protected void uninstallKeyboardActions() + { + } + + /** + * Installs the various aspects of the UI in the {@link JList}. In + * particular, calls {@link #installDefaults}, {@link #installListeners} + * and {@link #installKeyboardActions}. Also saves a reference to the + * provided component, cast to a {@link JList}. + * + * @param c The {@link JList} to install the UI into + */ + public void installUI(final JComponent c) + { + super.installUI(c); + list = (JList) c; + installDefaults(); + installListeners(); + installKeyboardActions(); + maybeUpdateLayoutState(); + } + + /** + * Uninstalls all the aspects of the UI which were installed in {@link + * #installUI}. When finished uninstalling, drops the saved reference to + * the {@link JList}. + * + * @param c Ignored; the UI is uninstalled from the {@link JList} + * reference saved during the call to {@link #installUI} + */ + public void uninstallUI(final JComponent c) + { + uninstallKeyboardActions(); + uninstallListeners(); + uninstallDefaults(); + list = null; + } + + /** + * Gets the size this list would prefer to assume. This is calculated by + * calling {@link #getCellBounds} over the entire list. + * + * @param c Ignored; uses the saved {@link JList} reference + * + * @return DOCUMENT ME! + */ + public Dimension getPreferredSize(JComponent c) + { + int size = list.getModel().getSize(); + if (size == 0) + return new Dimension(0, 0); + int visibleRows = list.getVisibleRowCount(); + int layoutOrientation = list.getLayoutOrientation(); + Rectangle bounds = getCellBounds(list, 0, list.getModel().getSize() - 1); + Dimension retVal = bounds.getSize(); + Component parent = list.getParent(); + if ((visibleRows == -1) && (parent instanceof JViewport)) + { + JViewport viewport = (JViewport) parent; + + if (layoutOrientation == JList.HORIZONTAL_WRAP) + { + int h = viewport.getSize().height; + int cellsPerCol = h / cellHeight; + int w = size / cellsPerCol * cellWidth; + retVal = new Dimension(w, h); + } + else if (layoutOrientation == JList.VERTICAL_WRAP) + { + int w = viewport.getSize().width; + int cellsPerRow = Math.max(w / cellWidth, 1); + int h = size / cellsPerRow * cellHeight; + retVal = new Dimension(w, h); + } + } + return retVal; + } + + /** + * Paints the packground of the list using the background color + * of the specified component. + * + * @param g The graphics context to paint in + * @param c The component to paint the background of + */ + private void paintBackground(Graphics g, JComponent c) + { + Dimension size = getPreferredSize(c); + Color save = g.getColor(); + g.setColor(c.getBackground()); + g.fillRect(0, 0, size.width, size.height); + g.setColor(save); + } + + /** + * Paints a single cell in the list. + * + * @param g The graphics context to paint in + * @param row The row number to paint + * @param bounds The bounds of the cell to paint, assuming a coordinate + * system beginning at <code>(0,0)</code> in the upper left corner of the + * list + * @param rend A cell renderer to paint with + * @param data The data to provide to the cell renderer + * @param sel A selection model to provide to the cell renderer + * @param lead The lead selection index of the list + */ + protected void paintCell(Graphics g, int row, Rectangle bounds, + ListCellRenderer rend, ListModel data, + ListSelectionModel sel, int lead) + { + boolean is_sel = list.isSelectedIndex(row); + boolean has_focus = false; + Component comp = rend.getListCellRendererComponent(list, + data.getElementAt(row), + 0, is_sel, has_focus); + //comp.setBounds(new Rectangle(0, 0, bounds.width, bounds.height)); + //comp.paint(g); + rendererPane.paintComponent(g, comp, list, bounds); + } + + /** + * Paints the list by calling {@link #paintBackground} and then repeatedly + * calling {@link #paintCell} for each visible cell in the list. + * + * @param g The graphics context to paint with + * @param c Ignored; uses the saved {@link JList} reference + */ + public void paint(Graphics g, JComponent c) + { + int nrows = list.getModel().getSize(); + if (nrows == 0) + return; + + maybeUpdateLayoutState(); + ListCellRenderer render = list.getCellRenderer(); + ListModel model = list.getModel(); + ListSelectionModel sel = list.getSelectionModel(); + int lead = sel.getLeadSelectionIndex(); + Rectangle clip = g.getClipBounds(); + paintBackground(g, list); + + for (int row = 0; row < nrows; ++row) + { + Rectangle bounds = getCellBounds(list, row, row); + if (bounds.intersects(clip)) + paintCell(g, row, bounds, render, model, sel, lead); + } + } + + /** + * Computes the index of a list cell given a point within the list. + * + * @param list the list which on which the computation is based on + * @param location the coordinates + * + * @return the index of the list item that is located at the given + * coordinates or <code>null</code> if the location is invalid + */ + public int locationToIndex(JList list, Point location) + { + int layoutOrientation = list.getLayoutOrientation(); + int index = -1; + switch (layoutOrientation) + { + case JList.VERTICAL: + index = convertYToRow(location.y); + break; + case JList.HORIZONTAL_WRAP: + // determine visible rows and cells per row + int visibleRows = list.getVisibleRowCount(); + int cellsPerRow = -1; + int numberOfItems = list.getModel().getSize(); + Dimension listDim = list.getSize(); + if (visibleRows <= 0) + { + try + { + cellsPerRow = listDim.width / cellWidth; + } + catch (ArithmeticException ex) + { + cellsPerRow = 1; + } + } + else + { + cellsPerRow = numberOfItems / visibleRows + 1; + } + + // determine index for the given location + int cellsPerColumn = numberOfItems / cellsPerRow + 1; + int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1); + int gridY = Math.min(location.y / cellHeight, cellsPerColumn); + index = gridX + gridY * cellsPerRow; + break; + case JList.VERTICAL_WRAP: + // determine visible rows and cells per column + int visibleRows2 = list.getVisibleRowCount(); + if (visibleRows2 <= 0) + { + Dimension listDim2 = list.getSize(); + visibleRows2 = listDim2.height / cellHeight; + } + int numberOfItems2 = list.getModel().getSize(); + int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1; + + Dimension listDim2 = list.getSize(); + int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1); + int gridY2 = Math.min(location.y / cellHeight, visibleRows2); + index = gridY2 + gridX2 * visibleRows2; + break; + } + return index; + } + + public Point indexToLocation(JList list, int index) + { + int layoutOrientation = list.getLayoutOrientation(); + Point loc = null; + switch (layoutOrientation) + { + case JList.VERTICAL: + loc = new Point(0, convertRowToY(index)); + break; + case JList.HORIZONTAL_WRAP: + // determine visible rows and cells per row + int visibleRows = list.getVisibleRowCount(); + int numberOfCellsPerRow = -1; + if (visibleRows <= 0) + { + Dimension listDim = list.getSize(); + numberOfCellsPerRow = Math.max(listDim.width / cellWidth, 1); + } + else + { + int numberOfItems = list.getModel().getSize(); + numberOfCellsPerRow = numberOfItems / visibleRows + 1; + } + // compute coordinates inside the grid + int gridX = index % numberOfCellsPerRow; + int gridY = index / numberOfCellsPerRow; + int locX = gridX * cellWidth; + int locY = gridY * cellHeight; + loc = new Point(locX, locY); + break; + case JList.VERTICAL_WRAP: + // determine visible rows and cells per column + int visibleRows2 = list.getVisibleRowCount(); + if (visibleRows2 <= 0) + { + Dimension listDim2 = list.getSize(); + visibleRows2 = listDim2.height / cellHeight; + } + // compute coordinates inside the grid + if (visibleRows2 > 0) + { + int gridY2 = index % visibleRows2; + int gridX2 = index / visibleRows2; + int locX2 = gridX2 * cellWidth; + int locY2 = gridY2 * cellHeight; + loc = new Point(locX2, locY2); + } + else + loc = new Point(0, convertRowToY(index)); + break; + } + return loc; + } +} |