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)
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
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
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
;
69 * The Basic Look and Feel UI delegate for the
72 public class BasicListUI
extends ListUI
75 * A helper class which listens for {@link FocusEvents}
78 class FocusHandler
implements FocusListener
81 * Called when the JList acquires focus.
83 * @param e The FocusEvent representing focus acquisition
85 public void focusGained(FocusEvent e
)
91 * Called when the JList loses focus.
93 * @param e The FocusEvent representing focus loss
95 public void focusLost(FocusEvent e
)
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
185 * @param event The event representing the mouse press
187 public void mousePressed(MouseEvent event
)
189 int row
= BasicListUI
.this.convertYToRow(event
.getY());
193 BasicListUI
.this.list
.setSelectedIndex(row
);
197 * Called when a mouse button is released on
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
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. */
302 /** The height of a single cell in the list. */
305 /** The width of a single cell in the list. */
309 * An array of varying heights of cells in the list, in cases where each
310 * cell might have a different height.
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
)
335 else if (cellHeight
!= -1)
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)
359 int lo
= Math
.min(index1
, index2
);
360 int hi
= Math
.max(index1
, index2
);
361 Rectangle lobounds
= new Rectangle(0, convertRowToY(lo
), cellWidth
,
363 Rectangle hibounds
= new Rectangle(0, convertRowToY(hi
), cellWidth
,
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
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
)
382 for (int i
= 0; i
< row
; ++i
)
384 int h
= getRowHeight(i
);
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
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
);
416 * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
417 * #cellWidth} properties by examining the variouis properties of the
420 void updateLayoutState()
422 int nrows
= list
.getModel().getSize();
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
,
437 Dimension dim
= flyweight
.getPreferredSize();
438 cellHeights
[i
] = dim
.height
;
439 cellWidth
= Math
.max(cellWidth
, dim
.width
);
444 cellHeight
= list
.getFixedCellHeight();
445 cellWidth
= list
.getFixedCellWidth();
450 * Marks the current layout as damaged and requests revalidation from the
453 * @see #updateLayoutStateNeeded
457 updateLayoutStateNeeded
= 1;
462 * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
463 * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
465 void maybeUpdateLayoutState()
467 if (updateLayoutStateNeeded
!= 0)
470 updateLayoutStateNeeded
= 0;
475 * Creates a new BasicListUI object.
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
)
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
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();
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
);
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
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
));
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();
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
));