Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / javax / swing / plaf / basic / BasicListUI.java
blobb39421798fad7ca363a24634dca91c8551ad449a
1 /* BasicListUI.java --
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.plaf.basic;
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Dimension;
44 import java.awt.Graphics;
45 import java.awt.Point;
46 import java.awt.Rectangle;
47 import java.awt.event.FocusEvent;
48 import java.awt.event.FocusListener;
49 import java.awt.event.MouseEvent;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
53 import javax.swing.JComponent;
54 import javax.swing.JList;
55 import javax.swing.ListCellRenderer;
56 import javax.swing.ListModel;
57 import javax.swing.ListSelectionModel;
58 import javax.swing.UIDefaults;
59 import javax.swing.UIManager;
60 import javax.swing.event.ListDataEvent;
61 import javax.swing.event.ListDataListener;
62 import javax.swing.event.ListSelectionEvent;
63 import javax.swing.event.ListSelectionListener;
64 import javax.swing.event.MouseInputListener;
65 import javax.swing.plaf.ComponentUI;
66 import javax.swing.plaf.ListUI;
68 /**
69 * The Basic Look and Feel UI delegate for the
70 * JList.
72 public class BasicListUI extends ListUI
74 /**
75 * A helper class which listens for {@link FocusEvents}
76 * from the JList.
78 class FocusHandler implements FocusListener
80 /**
81 * Called when the JList acquires focus.
83 * @param e The FocusEvent representing focus acquisition
85 public void focusGained(FocusEvent e)
87 repaintCellFocus();
90 /**
91 * Called when the JList loses focus.
93 * @param e The FocusEvent representing focus loss
95 public void focusLost(FocusEvent e)
97 repaintCellFocus();
101 * Helper method to repaint the focused cell's
102 * lost or acquired focus state.
104 void repaintCellFocus()
110 * A helper class which listens for {@link ListDataEvent}s generated by
111 * the {@link JList}'s {@link ListModel}.
113 * @see javax.swing.JList#model
115 class ListDataHandler implements ListDataListener
118 * Called when a general change has happened in the model which cannot
119 * be represented in terms of a simple addition or deletion.
121 * @param e The event representing the change
123 public void contentsChanged(ListDataEvent e)
125 BasicListUI.this.damageLayout();
129 * Called when an interval of objects has been added to the model.
131 * @param e The event representing the addition
133 public void intervalAdded(ListDataEvent e)
135 BasicListUI.this.damageLayout();
139 * Called when an inteval of objects has been removed from the model.
141 * @param e The event representing the removal
143 public void intervalRemoved(ListDataEvent e)
145 BasicListUI.this.damageLayout();
150 * A helper class which listens for {@link ListSelectionEvent}s
151 * from the {@link JList}'s {@link ListSelectionModel}.
153 class ListSelectionHandler implements ListSelectionListener
156 * Called when the list selection changes.
158 * @param e The event representing the change
160 public void valueChanged(ListSelectionEvent e)
166 * A helper class which listens for {@link MouseEvent}s
167 * from the {@link JList}.
169 class MouseInputHandler implements MouseInputListener
172 * Called when a mouse button press/release cycle completes
173 * on the {@link JList}
175 * @param event The event representing the mouse click
177 public void mouseClicked(MouseEvent event)
182 * Called when a mouse button is pressed down on the
183 * {@link JList}.
185 * @param event The event representing the mouse press
187 public void mousePressed(MouseEvent event)
189 int row = BasicListUI.this.convertYToRow(event.getY());
190 if (row == -1)
191 return;
193 BasicListUI.this.list.setSelectedIndex(row);
197 * Called when a mouse button is released on
198 * the {@link JList}
200 * @param event The event representing the mouse press
202 public void mouseReleased(MouseEvent event)
207 * Called when the mouse pointer enters the area bounded
208 * by the {@link JList}
210 * @param event The event representing the mouse entry
212 public void mouseEntered(MouseEvent event)
217 * Called when the mouse pointer leaves the area bounded
218 * by the {@link JList}
220 * @param event The event representing the mouse exit
222 public void mouseExited(MouseEvent event)
227 * Called when the mouse pointer moves over the area bounded
228 * by the {@link JList} while a button is held down.
230 * @param event The event representing the mouse drag
232 public void mouseDragged(MouseEvent event)
237 * Called when the mouse pointer moves over the area bounded
238 * by the {@link JList}.
240 * @param event The event representing the mouse move
242 public void mouseMoved(MouseEvent event)
248 * Helper class which listens to {@link PropertyChangeEvent}s
249 * from the {@link JList}.
251 class PropertyChangeHandler implements PropertyChangeListener
254 * Called when the {@link JList} changes one of its bound properties.
256 * @param e The event representing the property change
258 public void propertyChange(PropertyChangeEvent e)
260 if (e.getSource() == BasicListUI.this.list)
262 if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
263 ((ListModel) e.getOldValue()).removeListDataListener(BasicListUI.this.listDataListener);
265 if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
266 ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener);
268 BasicListUI.this.damageLayout();
273 * Creates a new BasicListUI for the component.
275 * @param c The component to create a UI for
277 * @return A new UI
279 public static ComponentUI createUI(final JComponent c)
281 return new BasicListUI();
284 /** The current focus listener. */
285 FocusHandler focusListener;
287 /** The data listener listening to the model. */
288 ListDataHandler listDataListener;
290 /** The selection listener listening to the selection model. */
291 ListSelectionHandler listSelectionListener;
293 /** The mouse listener listening to the list. */
294 MouseInputHandler mouseInputListener;
296 /** The property change listener listening to the list. */
297 PropertyChangeHandler propertyChangeListener;
299 /** Saved reference to the list this UI was created for. */
300 JList list;
302 /** The height of a single cell in the list. */
303 int cellHeight;
305 /** The width of a single cell in the list. */
306 int cellWidth;
308 /**
309 * An array of varying heights of cells in the list, in cases where each
310 * cell might have a different height.
312 int[] cellHeights;
315 * A simple counter. When nonzero, indicates that the UI class is out of
316 * date with respect to the underlying list, and must recalculate the
317 * list layout before painting or performing size calculations.
319 int updateLayoutStateNeeded;
322 * Calculate the height of a particular row. If there is a fixed {@link
323 * #cellHeight}, return it; otherwise return the specific row height
324 * requested from the {@link #cellHeights} array. If the requested row
325 * is invalid, return <code>-1</code>.
327 * @param row The row to get the height of
329 * @return The height, in pixels, of the specified row
331 int getRowHeight(int row)
333 if (row < 0 || row >= cellHeights.length)
334 return -1;
335 else if (cellHeight != -1)
336 return cellHeight;
337 else
338 return cellHeights[row];
342 * Calculate the bounds of a particular cell, considering the upper left
343 * corner of the list as the origin position <code>(0,0)</code>.
345 * @param l Ignored; calculates over <code>this.list</code>
346 * @param index1 The first row to include in the bounds
347 * @param index2 The last row to incude in the bounds
349 * @return A rectangle encompassing the range of rows between
350 * <code>index1</code> and <code>index2</code> inclusive
352 public Rectangle getCellBounds(JList l, int index1, int index2)
354 maybeUpdateLayoutState();
356 if (l != list || cellWidth == -1)
357 return null;
359 int lo = Math.min(index1, index2);
360 int hi = Math.max(index1, index2);
361 Rectangle lobounds = new Rectangle(0, convertRowToY(lo), cellWidth,
362 getRowHeight(lo));
363 Rectangle hibounds = new Rectangle(0, convertRowToY(hi), cellWidth,
364 getRowHeight(hi));
366 return lobounds.union(hibounds);
370 * Calculate the Y coordinate of the upper edge of a particular row,
371 * considering the Y coordinate <code>0</code> to occur at the top of the
372 * list.
374 * @param row The row to calculate the Y coordinate of
376 * @return The Y coordinate of the specified row, or <code>-1</code> if
377 * the specified row number is invalid
379 int convertRowToY(int row)
381 int y = 0;
382 for (int i = 0; i < row; ++i)
384 int h = getRowHeight(i);
385 if (h == -1)
386 return -1;
387 y += h;
389 return y;
393 * Calculate the row number containing a particular Y coordinate,
394 * considering the Y coodrinate <code>0</code> to occur at the top of the
395 * list.
397 * @param y0 The Y coordinate to calculate the row number for
399 * @return The row number containing the specified Y value, or <code>-1</code>
400 * if the specified Y coordinate is invalid
402 int convertYToRow(int y0)
404 for (int row = 0; row < cellHeights.length; ++row)
406 int h = getRowHeight(row);
408 if (y0 < h)
409 return row;
410 y0 -= h;
412 return -1;
416 * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
417 * #cellWidth} properties by examining the variouis properties of the
418 * {@link JList}.
420 void updateLayoutState()
422 int nrows = list.getModel().getSize();
423 cellHeight = -1;
424 cellWidth = -1;
425 if (cellHeights == null || cellHeights.length != nrows)
426 cellHeights = new int[nrows];
427 if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1)
429 ListCellRenderer rend = list.getCellRenderer();
430 for (int i = 0; i < nrows; ++i)
432 Component flyweight = rend.getListCellRendererComponent(list,
433 list.getModel()
434 .getElementAt(i),
435 0, false,
436 false);
437 Dimension dim = flyweight.getPreferredSize();
438 cellHeights[i] = dim.height;
439 cellWidth = Math.max(cellWidth, dim.width);
442 else
444 cellHeight = list.getFixedCellHeight();
445 cellWidth = list.getFixedCellWidth();
450 * Marks the current layout as damaged and requests revalidation from the
451 * JList.
453 * @see #updateLayoutStateNeeded
455 void damageLayout()
457 updateLayoutStateNeeded = 1;
458 list.revalidate();
462 * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
463 * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
465 void maybeUpdateLayoutState()
467 if (updateLayoutStateNeeded != 0)
469 updateLayoutState();
470 updateLayoutStateNeeded = 0;
475 * Creates a new BasicListUI object.
477 public BasicListUI()
479 focusListener = new FocusHandler();
480 listDataListener = new ListDataHandler();
481 listSelectionListener = new ListSelectionHandler();
482 mouseInputListener = new MouseInputHandler();
483 propertyChangeListener = new PropertyChangeHandler();
484 updateLayoutStateNeeded = 1;
488 * Installs various default settings (mostly colors) from the {@link
489 * UIDefaults} into the {@link JList}
491 * @see #uninstallDefaults
493 void installDefaults()
495 UIDefaults defaults = UIManager.getLookAndFeelDefaults();
496 list.setForeground(defaults.getColor("List.foreground"));
497 list.setBackground(defaults.getColor("List.background"));
498 list.setSelectionForeground(defaults.getColor("List.selectionForeground"));
499 list.setSelectionBackground(defaults.getColor("List.selectionBackground"));
500 list.setOpaque(true);
504 * Resets to <code>null</code> those defaults which were installed in
505 * {@link #installDefaults}
507 void uninstallDefaults()
509 UIDefaults defaults = UIManager.getLookAndFeelDefaults();
510 list.setForeground(null);
511 list.setBackground(null);
512 list.setSelectionForeground(null);
513 list.setSelectionBackground(null);
517 * Attaches all the listeners we have in the UI class to the {@link
518 * JList}, its model and its selection model.
520 * @see #uninstallListeners
522 void installListeners()
524 list.addFocusListener(focusListener);
525 list.getModel().addListDataListener(listDataListener);
526 list.addListSelectionListener(listSelectionListener);
527 list.addMouseListener(mouseInputListener);
528 list.addMouseMotionListener(mouseInputListener);
529 list.addPropertyChangeListener(propertyChangeListener);
533 * Detaches all the listeners we attached in {@link #installListeners}.
535 void uninstallListeners()
537 list.removeFocusListener(focusListener);
538 list.getModel().removeListDataListener(listDataListener);
539 list.removeListSelectionListener(listSelectionListener);
540 list.removeMouseListener(mouseInputListener);
541 list.removeMouseMotionListener(mouseInputListener);
542 list.removePropertyChangeListener(propertyChangeListener);
546 * Installs keyboard actions for this UI in the {@link JList}.
548 void installKeyboardActions()
553 * Uninstalls keyboard actions for this UI in the {@link JList}.
555 void uninstallKeyboardActions()
560 * Installs the various aspects of the UI in the {@link JList}. In
561 * particular, calls {@link #installDefaults}, {@link #installListeners}
562 * and {@link #installKeyboardActions}. Also saves a reference to the
563 * provided component, cast to a {@link JList}.
565 * @param c The {@link JList} to install the UI into
567 public void installUI(final JComponent c)
569 super.installUI(c);
570 list = (JList) c;
571 installDefaults();
572 installListeners();
573 installKeyboardActions();
574 maybeUpdateLayoutState();
578 * Uninstalls all the aspects of the UI which were installed in {@link
579 * #installUI}. When finished uninstalling, drops the saved reference to
580 * the {@link JList}.
582 * @param c Ignored; the UI is uninstalled from the {@link JList}
583 * reference saved during the call to {@link #installUI}
585 public void uninstallUI(final JComponent c)
587 uninstallKeyboardActions();
588 uninstallListeners();
589 uninstallDefaults();
590 list = null;
594 * Gets the maximum size this list can assume.
596 * @param c The component to measure the size of
598 * @return A new Dimension representing the component's maximum size
600 public Dimension getMaximumSize(JComponent c)
602 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
606 * Gets the size this list would prefer to assume. This is calculated by
607 * calling {@link #getCellBounds} over the entire list.
609 * @param c Ignored; uses the saved {@link JList} reference
611 * @return DOCUMENT ME!
613 public Dimension getPreferredSize(JComponent c)
615 if (list.getModel().getSize() == 0)
616 return new Dimension(0, 0);
617 Rectangle bounds = getCellBounds(list, 0, list.getModel().getSize() - 1);
618 return bounds.getSize();
622 * Paints the packground of the list using the background color
623 * of the specified component.
625 * @param g The graphics context to paint in
626 * @param c The component to paint the background of
628 public void paintBackground(Graphics g, JComponent c)
630 Dimension size = getPreferredSize(c);
631 Color save = g.getColor();
632 g.setColor(c.getBackground());
633 g.fillRect(0, 0, size.width, size.height);
634 g.setColor(save);
638 * Paints a single cell in the list.
640 * @param g The graphics context to paint in
641 * @param row The row number to paint
642 * @param bounds The bounds of the cell to paint, assuming a coordinate
643 * system beginning at <code>(0,0)</code> in the upper left corner of the
644 * list
645 * @param rend A cell renderer to paint with
646 * @param data The data to provide to the cell renderer
647 * @param sel A selection model to provide to the cell renderer
648 * @param lead The lead selection index of the list
650 void paintCell(Graphics g, int row, Rectangle bounds, ListCellRenderer rend,
651 ListModel data, ListSelectionModel sel, int lead)
653 boolean is_sel = list.isSelectedIndex(row);
654 boolean has_focus = false;
655 Component comp = rend.getListCellRendererComponent(list,
656 data.getElementAt(row),
657 0, is_sel, has_focus);
658 g.translate(bounds.x, bounds.y);
659 comp.setBounds(new Rectangle(0, 0, bounds.width, bounds.height));
660 comp.paint(g);
661 g.translate(-bounds.x, -bounds.y);
665 * Paints the list by calling {@link #paintBackground} and then repeatedly
666 * calling {@link #paintCell} for each visible cell in the list.
668 * @param g The graphics context to paint with
669 * @param c Ignored; uses the saved {@link JList} reference
671 public void paint(Graphics g, JComponent c)
673 int nrows = list.getModel().getSize();
674 if (nrows == 0)
675 return;
677 maybeUpdateLayoutState();
678 ListCellRenderer render = list.getCellRenderer();
679 ListModel model = list.getModel();
680 ListSelectionModel sel = list.getSelectionModel();
681 int lead = sel.getLeadSelectionIndex();
682 Rectangle clip = g.getClipBounds();
683 paintBackground(g, list);
685 for (int row = 0; row < nrows; ++row)
687 Rectangle bounds = getCellBounds(list, row, row);
688 if (bounds.intersects(clip))
689 paintCell(g, row, bounds, render, model, sel, lead);
693 public int locationToIndex(JList list, Point location)
695 return convertYToRow(location.y);
698 public Point indexToLocation(JList list, int index)
700 return new Point(0, convertRowToY(index));