2 Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, 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
.Component
;
42 import java
.awt
.Dimension
;
43 import java
.awt
.Graphics
;
44 import java
.awt
.Insets
;
45 import java
.awt
.Point
;
46 import java
.awt
.Rectangle
;
47 import java
.awt
.event
.ActionEvent
;
48 import java
.awt
.event
.ActionListener
;
49 import java
.awt
.event
.FocusEvent
;
50 import java
.awt
.event
.FocusListener
;
51 import java
.awt
.event
.MouseEvent
;
52 import java
.beans
.PropertyChangeEvent
;
53 import java
.beans
.PropertyChangeListener
;
55 import javax
.swing
.AbstractAction
;
56 import javax
.swing
.ActionMap
;
57 import javax
.swing
.CellRendererPane
;
58 import javax
.swing
.DefaultListSelectionModel
;
59 import javax
.swing
.InputMap
;
60 import javax
.swing
.JComponent
;
61 import javax
.swing
.JList
;
62 import javax
.swing
.KeyStroke
;
63 import javax
.swing
.ListCellRenderer
;
64 import javax
.swing
.ListModel
;
65 import javax
.swing
.ListSelectionModel
;
66 import javax
.swing
.LookAndFeel
;
67 import javax
.swing
.UIDefaults
;
68 import javax
.swing
.UIManager
;
69 import javax
.swing
.event
.ListDataEvent
;
70 import javax
.swing
.event
.ListDataListener
;
71 import javax
.swing
.event
.ListSelectionEvent
;
72 import javax
.swing
.event
.ListSelectionListener
;
73 import javax
.swing
.event
.MouseInputListener
;
74 import javax
.swing
.plaf
.ActionMapUIResource
;
75 import javax
.swing
.plaf
.ComponentUI
;
76 import javax
.swing
.plaf
.InputMapUIResource
;
77 import javax
.swing
.plaf
.ListUI
;
80 * The Basic Look and Feel UI delegate for the
83 public class BasicListUI
extends ListUI
87 * A helper class which listens for {@link FocusEvent}s
90 public class FocusHandler
implements FocusListener
93 * Called when the JList acquires focus.
95 * @param e The FocusEvent representing focus acquisition
97 public void focusGained(FocusEvent e
)
103 * Called when the JList loses focus.
105 * @param e The FocusEvent representing focus loss
107 public void focusLost(FocusEvent e
)
113 * Helper method to repaint the focused cell's
114 * lost or acquired focus state.
116 protected void repaintCellFocus()
118 // TODO: Implement this properly.
123 * A helper class which listens for {@link ListDataEvent}s generated by
124 * the {@link JList}'s {@link ListModel}.
126 * @see javax.swing.JList#getModel()
128 public class ListDataHandler
implements ListDataListener
131 * Called when a general change has happened in the model which cannot
132 * be represented in terms of a simple addition or deletion.
134 * @param e The event representing the change
136 public void contentsChanged(ListDataEvent e
)
142 * Called when an interval of objects has been added to the model.
144 * @param e The event representing the addition
146 public void intervalAdded(ListDataEvent e
)
152 * Called when an inteval of objects has been removed from the model.
154 * @param e The event representing the removal
156 public void intervalRemoved(ListDataEvent e
)
163 * A helper class which listens for {@link ListSelectionEvent}s
164 * from the {@link JList}'s {@link ListSelectionModel}.
166 public class ListSelectionHandler
implements ListSelectionListener
169 * Called when the list selection changes.
171 * @param e The event representing the change
173 public void valueChanged(ListSelectionEvent e
)
175 int index1
= e
.getFirstIndex();
176 int index2
= e
.getLastIndex();
177 Rectangle damaged
= getCellBounds(list
, index1
, index2
);
178 list
.repaint(damaged
);
183 * This class is used to mimmic the behaviour of the JDK when registering
184 * keyboard actions. It is the same as the private class used in JComponent
185 * for the same reason. This class receives an action event and dispatches
186 * it to the true receiver after altering the actionCommand property of the
189 private static class ActionListenerProxy
190 extends AbstractAction
192 ActionListener target
;
193 String bindingCommandName
;
195 public ActionListenerProxy(ActionListener li
,
199 bindingCommandName
= cmd
;
202 public void actionPerformed(ActionEvent e
)
204 ActionEvent derivedEvent
= new ActionEvent(e
.getSource(),
208 target
.actionPerformed(derivedEvent
);
212 class ListAction
extends AbstractAction
214 public void actionPerformed (ActionEvent e
)
216 int lead
= list
.getLeadSelectionIndex();
217 int max
= list
.getModel().getSize() - 1;
218 DefaultListSelectionModel selModel
= (DefaultListSelectionModel
)list
.getSelectionModel();
219 String command
= e
.getActionCommand();
220 // Do nothing if list is empty
224 if (command
.equals("selectNextRow"))
228 else if (command
.equals("selectPreviousRow"))
230 selectPreviousIndex();
232 else if (command
.equals("clearSelection"))
234 list
.clearSelection();
236 else if (command
.equals("selectAll"))
238 list
.setSelectionInterval(0, max
);
239 // this next line is to restore the lead selection index to the old
240 // position, because select-all should not change the lead index
241 list
.addSelectionInterval(lead
, lead
);
243 else if (command
.equals("selectLastRow"))
245 list
.setSelectedIndex(list
.getModel().getSize() - 1);
247 else if (command
.equals("selectLastRowChangeLead"))
249 selModel
.moveLeadSelectionIndex(list
.getModel().getSize() - 1);
251 else if (command
.equals("scrollDownExtendSelection"))
254 if (lead
== list
.getLastVisibleIndex())
257 (max
, lead
+ (list
.getLastVisibleIndex() -
258 list
.getFirstVisibleIndex() + 1));
261 target
= list
.getLastVisibleIndex();
262 selModel
.setLeadSelectionIndex(target
);
264 else if (command
.equals("scrollDownChangeLead"))
267 if (lead
== list
.getLastVisibleIndex())
270 (max
, lead
+ (list
.getLastVisibleIndex() -
271 list
.getFirstVisibleIndex() + 1));
274 target
= list
.getLastVisibleIndex();
275 selModel
.moveLeadSelectionIndex(target
);
277 else if (command
.equals("scrollUpExtendSelection"))
280 if (lead
== list
.getFirstVisibleIndex())
283 (0, lead
- (list
.getLastVisibleIndex() -
284 list
.getFirstVisibleIndex() + 1));
287 target
= list
.getFirstVisibleIndex();
288 selModel
.setLeadSelectionIndex(target
);
290 else if (command
.equals("scrollUpChangeLead"))
293 if (lead
== list
.getFirstVisibleIndex())
296 (0, lead
- (list
.getLastVisibleIndex() -
297 list
.getFirstVisibleIndex() + 1));
300 target
= list
.getFirstVisibleIndex();
301 selModel
.moveLeadSelectionIndex(target
);
303 else if (command
.equals("selectNextRowExtendSelection"))
305 selModel
.setLeadSelectionIndex(Math
.min(lead
+ 1,max
));
307 else if (command
.equals("selectFirstRow"))
309 list
.setSelectedIndex(0);
311 else if (command
.equals("selectFirstRowChangeLead"))
313 selModel
.moveLeadSelectionIndex(0);
315 else if (command
.equals("selectFirstRowExtendSelection"))
317 selModel
.setLeadSelectionIndex(0);
319 else if (command
.equals("selectPreviousRowExtendSelection"))
321 selModel
.setLeadSelectionIndex(Math
.max(0,lead
- 1));
323 else if (command
.equals("scrollUp"))
326 if (lead
== list
.getFirstVisibleIndex())
329 (0, lead
- (list
.getLastVisibleIndex() -
330 list
.getFirstVisibleIndex() + 1));
333 target
= list
.getFirstVisibleIndex();
334 list
.setSelectedIndex(target
);
336 else if (command
.equals("selectLastRowExtendSelection"))
338 selModel
.setLeadSelectionIndex(list
.getModel().getSize() - 1);
340 else if (command
.equals("scrollDown"))
343 if (lead
== list
.getLastVisibleIndex())
346 (max
, lead
+ (list
.getLastVisibleIndex() -
347 list
.getFirstVisibleIndex() + 1));
350 target
= list
.getLastVisibleIndex();
351 list
.setSelectedIndex(target
);
353 else if (command
.equals("selectNextRowChangeLead"))
355 if (selModel
.getSelectionMode() != ListSelectionModel
.MULTIPLE_INTERVAL_SELECTION
)
359 selModel
.moveLeadSelectionIndex(Math
.min(max
, lead
+ 1));
362 else if (command
.equals("selectPreviousRowChangeLead"))
364 if (selModel
.getSelectionMode() != ListSelectionModel
.MULTIPLE_INTERVAL_SELECTION
)
365 selectPreviousIndex();
368 selModel
.moveLeadSelectionIndex(Math
.max(0, lead
- 1));
371 else if (command
.equals("addToSelection"))
373 list
.addSelectionInterval(lead
, lead
);
375 else if (command
.equals("extendTo"))
377 selModel
.setSelectionInterval(selModel
.getAnchorSelectionIndex(),
380 else if (command
.equals("toggleAndAnchor"))
382 if (!list
.isSelectedIndex(lead
))
383 list
.addSelectionInterval(lead
, lead
);
385 list
.removeSelectionInterval(lead
, lead
);
386 selModel
.setAnchorSelectionIndex(lead
);
390 // DEBUG: uncomment the following line to print out
391 // key bindings that aren't implemented yet
393 // System.out.println ("not implemented: "+e.getActionCommand());
396 list
.ensureIndexIsVisible(list
.getLeadSelectionIndex());
401 * A helper class which listens for {@link MouseEvent}s
402 * from the {@link JList}.
404 public class MouseInputHandler
implements MouseInputListener
407 * Called when a mouse button press/release cycle completes
408 * on the {@link JList}
410 * @param event The event representing the mouse click
412 public void mouseClicked(MouseEvent event
)
414 Point click
= event
.getPoint();
415 int index
= locationToIndex(list
, click
);
418 if (event
.isShiftDown())
420 if (list
.getSelectionMode() == ListSelectionModel
.SINGLE_SELECTION
)
421 list
.setSelectedIndex(index
);
422 else if (list
.getSelectionMode() ==
423 ListSelectionModel
.SINGLE_INTERVAL_SELECTION
)
424 // COMPAT: the IBM VM is compatible with the following line of code.
425 // However, compliance with Sun's VM would correspond to replacing
426 // getAnchorSelectionIndex() with getLeadSelectionIndex().This is
427 // both unnatural and contradictory to the way they handle other
428 // similar UI interactions.
429 list
.setSelectionInterval(list
.getAnchorSelectionIndex(), index
);
431 // COMPAT: both Sun and IBM are compatible instead with:
432 // list.setSelectionInterval
433 // (list.getLeadSelectionIndex(),index);
434 // Note that for IBM this is contradictory to what they did in
435 // the above situation for SINGLE_INTERVAL_SELECTION.
436 // The most natural thing to do is the following:
437 if (list
.isSelectedIndex(list
.getAnchorSelectionIndex()))
438 list
.getSelectionModel().setLeadSelectionIndex(index
);
440 list
.addSelectionInterval(list
.getAnchorSelectionIndex(), index
);
442 else if (event
.isControlDown())
444 if (list
.getSelectionMode() == ListSelectionModel
.SINGLE_SELECTION
)
445 list
.setSelectedIndex(index
);
446 else if (list
.isSelectedIndex(index
))
447 list
.removeSelectionInterval(index
,index
);
449 list
.addSelectionInterval(index
,index
);
452 list
.setSelectedIndex(index
);
454 list
.ensureIndexIsVisible(list
.getLeadSelectionIndex());
458 * Called when a mouse button is pressed down on the
461 * @param event The event representing the mouse press
463 public void mousePressed(MouseEvent event
)
465 // TODO: What should be done here, if anything?
469 * Called when a mouse button is released on
472 * @param event The event representing the mouse press
474 public void mouseReleased(MouseEvent event
)
476 // TODO: What should be done here, if anything?
480 * Called when the mouse pointer enters the area bounded
481 * by the {@link JList}
483 * @param event The event representing the mouse entry
485 public void mouseEntered(MouseEvent event
)
487 // TODO: What should be done here, if anything?
491 * Called when the mouse pointer leaves the area bounded
492 * by the {@link JList}
494 * @param event The event representing the mouse exit
496 public void mouseExited(MouseEvent event
)
498 // TODO: What should be done here, if anything?
502 * Called when the mouse pointer moves over the area bounded
503 * by the {@link JList} while a button is held down.
505 * @param event The event representing the mouse drag
507 public void mouseDragged(MouseEvent event
)
509 Point click
= event
.getPoint();
510 int index
= locationToIndex(list
, click
);
513 if (!event
.isShiftDown() && !event
.isControlDown())
514 list
.setSelectedIndex(index
);
516 list
.ensureIndexIsVisible(list
.getLeadSelectionIndex());
520 * Called when the mouse pointer moves over the area bounded
521 * by the {@link JList}.
523 * @param event The event representing the mouse move
525 public void mouseMoved(MouseEvent event
)
527 // TODO: What should be done here, if anything?
532 * Helper class which listens to {@link PropertyChangeEvent}s
533 * from the {@link JList}.
535 public class PropertyChangeHandler
implements PropertyChangeListener
538 * Called when the {@link JList} changes one of its bound properties.
540 * @param e The event representing the property change
542 public void propertyChange(PropertyChangeEvent e
)
544 if (e
.getSource() == BasicListUI
.this.list
)
546 if (e
.getOldValue() != null && e
.getOldValue() instanceof ListModel
)
547 ((ListModel
) e
.getOldValue()).removeListDataListener(BasicListUI
.this.listDataListener
);
549 if (e
.getNewValue() != null && e
.getNewValue() instanceof ListModel
)
550 ((ListModel
) e
.getNewValue()).addListDataListener(BasicListUI
.this.listDataListener
);
552 // Update the updateLayoutStateNeeded flag.
553 if (e
.getPropertyName().equals("model"))
554 updateLayoutStateNeeded
|= modelChanged
;
555 else if (e
.getPropertyName().equals("selectionModel"))
556 updateLayoutStateNeeded
|= selectionModelChanged
;
557 else if (e
.getPropertyName().equals("font"))
558 updateLayoutStateNeeded
|= fontChanged
;
559 else if (e
.getPropertyName().equals("fixedCellWidth"))
560 updateLayoutStateNeeded
|= fixedCellWidthChanged
;
561 else if (e
.getPropertyName().equals("fixedCellHeight"))
562 updateLayoutStateNeeded
|= fixedCellHeightChanged
;
563 else if (e
.getPropertyName().equals("prototypeCellValue"))
564 updateLayoutStateNeeded
|= prototypeCellValueChanged
;
565 else if (e
.getPropertyName().equals("cellRenderer"))
566 updateLayoutStateNeeded
|= cellRendererChanged
;
571 * A constant to indicate that the model has changed.
573 protected static final int modelChanged
= 1;
576 * A constant to indicate that the selection model has changed.
578 protected static final int selectionModelChanged
= 2;
581 * A constant to indicate that the font has changed.
583 protected static final int fontChanged
= 4;
586 * A constant to indicate that the fixedCellWidth has changed.
588 protected static final int fixedCellWidthChanged
= 8;
591 * A constant to indicate that the fixedCellHeight has changed.
593 protected static final int fixedCellHeightChanged
= 16;
596 * A constant to indicate that the prototypeCellValue has changed.
598 protected static final int prototypeCellValueChanged
= 32;
601 * A constant to indicate that the cellRenderer has changed.
603 protected static final int cellRendererChanged
= 64;
606 * Creates a new BasicListUI for the component.
608 * @param c The component to create a UI for
612 public static ComponentUI
createUI(final JComponent c
)
614 return new BasicListUI();
617 /** The current focus listener. */
618 protected FocusListener focusListener
;
620 /** The data listener listening to the model. */
621 protected ListDataListener listDataListener
;
623 /** The selection listener listening to the selection model. */
624 protected ListSelectionListener listSelectionListener
;
626 /** The mouse listener listening to the list. */
627 protected MouseInputListener mouseInputListener
;
629 /** The property change listener listening to the list. */
630 protected PropertyChangeListener propertyChangeListener
;
632 /** Saved reference to the list this UI was created for. */
633 protected JList list
;
636 * The height of a single cell in the list. This field is used when the
637 * fixedCellHeight property of the list is set. Otherwise this field is
638 * set to <code>-1</code> and {@link #cellHeights} is used instead.
640 protected int cellHeight
;
642 /** The width of a single cell in the list. */
643 protected int cellWidth
;
646 * An array of varying heights of cells in the list, in cases where each
647 * cell might have a different height. This field is used when the
648 * <code>fixedCellHeight</code> property of the list is not set. Otherwise
649 * this field is <code>null</code> and {@link #cellHeight} is used.
651 protected int[] cellHeights
;
654 * A bitmask that indicates which properties of the JList have changed.
655 * When nonzero, indicates that the UI class is out of
656 * date with respect to the underlying list, and must recalculate the
657 * list layout before painting or performing size calculations.
660 * @see #selectionModelChanged
662 * @see #fixedCellWidthChanged
663 * @see #fixedCellHeightChanged
664 * @see #prototypeCellValueChanged
665 * @see #cellRendererChanged
667 protected int updateLayoutStateNeeded
;
670 * The {@link CellRendererPane} that is used for painting.
672 protected CellRendererPane rendererPane
;
674 /** The action bound to KeyStrokes. */
678 * Calculate the height of a particular row. If there is a fixed {@link
679 * #cellHeight}, return it; otherwise return the specific row height
680 * requested from the {@link #cellHeights} array. If the requested row
681 * is invalid, return <code>-1</code>.
683 * @param row The row to get the height of
685 * @return The height, in pixels, of the specified row
687 protected int getRowHeight(int row
)
690 if (cellHeights
== null)
694 if (row
< 0 || row
>= cellHeights
.length
)
697 height
= cellHeights
[row
];
703 * Calculate the bounds of a particular cell, considering the upper left
704 * corner of the list as the origin position <code>(0,0)</code>.
706 * @param l Ignored; calculates over <code>this.list</code>
707 * @param index1 The first row to include in the bounds
708 * @param index2 The last row to incude in the bounds
710 * @return A rectangle encompassing the range of rows between
711 * <code>index1</code> and <code>index2</code> inclusive
713 public Rectangle
getCellBounds(JList l
, int index1
, int index2
)
715 maybeUpdateLayoutState();
717 if (l
!= list
|| cellWidth
== -1)
720 int minIndex
= Math
.min(index1
, index2
);
721 int maxIndex
= Math
.max(index1
, index2
);
722 Point loc
= indexToLocation(list
, minIndex
);
723 Rectangle bounds
= new Rectangle(loc
.x
, loc
.y
, cellWidth
,
724 getCellHeight(minIndex
));
725 for (int i
= minIndex
+ 1; i
<= maxIndex
; i
++)
727 Point hiLoc
= indexToLocation(list
, i
);
728 Rectangle hibounds
= new Rectangle(hiLoc
.x
, hiLoc
.y
, cellWidth
,
730 bounds
= bounds
.union(hibounds
);
737 * Calculates the maximum cell height.
739 * @param index the index of the cell
741 * @return the maximum cell height
743 private int getCellHeight(int index
)
745 int height
= cellHeight
;
748 if (list
.getLayoutOrientation() == JList
.VERTICAL
)
749 height
= getRowHeight(index
);
752 for (int j
= 0; j
< cellHeights
.length
; j
++)
753 height
= Math
.max(height
, cellHeights
[j
]);
760 * Calculate the Y coordinate of the upper edge of a particular row,
761 * considering the Y coordinate <code>0</code> to occur at the top of the
764 * @param row The row to calculate the Y coordinate of
766 * @return The Y coordinate of the specified row, or <code>-1</code> if
767 * the specified row number is invalid
769 protected int convertRowToY(int row
)
772 for (int i
= 0; i
< row
; ++i
)
774 int h
= getRowHeight(i
);
783 * Calculate the row number containing a particular Y coordinate,
784 * considering the Y coodrinate <code>0</code> to occur at the top of the
787 * @param y0 The Y coordinate to calculate the row number for
789 * @return The row number containing the specified Y value, or <code>-1</code>
790 * if the list model is empty
792 * @specnote This method is specified to return -1 for an invalid Y
793 * coordinate. However, some simple tests show that the behaviour
794 * is to return the index of the last list element for an Y
795 * coordinate that lies outside of the list bounds (even for
796 * negative indices). <code>-1</code>
797 * is only returned if the list model is empty.
799 protected int convertYToRow(int y0
)
801 if (list
.getModel().getSize() == 0)
804 // When y0 < 0, then the JDK returns the maximum row index of the list. So
807 return list
.getModel().getSize() - 1;
809 // Update the layout if necessary.
810 maybeUpdateLayoutState();
812 int index
= list
.getModel().getSize() - 1;
814 // If a fixed cell height is set, then we can work more efficient.
816 index
= Math
.min(y0
/ cellHeight
, index
);
817 // If we have no fixed cell height, we must add up each cell height up
822 for (int row
= 0; row
< cellHeights
.length
; ++row
)
824 h
+= cellHeights
[row
];
836 * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
837 * #cellWidth} properties by examining the variouis properties of the
840 protected void updateLayoutState()
842 int nrows
= list
.getModel().getSize();
845 if (cellHeights
== null || cellHeights
.length
!= nrows
)
846 cellHeights
= new int[nrows
];
847 ListCellRenderer rend
= list
.getCellRenderer();
848 // Update the cellHeight(s) fields.
849 int fixedCellHeight
= list
.getFixedCellHeight();
850 if (fixedCellHeight
> 0)
852 cellHeight
= fixedCellHeight
;
858 for (int i
= 0; i
< nrows
; ++i
)
860 Component flyweight
=
861 rend
.getListCellRendererComponent(list
,
862 list
.getModel().getElementAt(i
),
863 i
, list
.isSelectedIndex(i
),
864 list
.getSelectionModel().getAnchorSelectionIndex() == i
);
865 Dimension dim
= flyweight
.getPreferredSize();
866 cellHeights
[i
] = dim
.height
;
870 // Update the cellWidth field.
871 int fixedCellWidth
= list
.getFixedCellWidth();
872 if (fixedCellWidth
> 0)
873 cellWidth
= fixedCellWidth
;
876 for (int i
= 0; i
< nrows
; ++i
)
878 Component flyweight
=
879 rend
.getListCellRendererComponent(list
,
880 list
.getModel().getElementAt(i
),
881 i
, list
.isSelectedIndex(i
),
882 list
.getSelectionModel().getAnchorSelectionIndex() == i
);
883 Dimension dim
= flyweight
.getPreferredSize();
884 cellWidth
= Math
.max(cellWidth
, dim
.width
);
886 if (list
.getLayoutOrientation() == JList
.VERTICAL
)
887 cellWidth
= Math
.max(cellWidth
, list
.getSize().width
);
892 * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
893 * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
895 protected void maybeUpdateLayoutState()
897 if (updateLayoutStateNeeded
!= 0 || !list
.isValid())
900 updateLayoutStateNeeded
= 0;
905 * Creates a new BasicListUI object.
909 updateLayoutStateNeeded
= 1;
910 rendererPane
= new CellRendererPane();
914 * Installs various default settings (mostly colors) from the {@link
915 * UIDefaults} into the {@link JList}
917 * @see #uninstallDefaults
919 protected void installDefaults()
921 LookAndFeel
.installColorsAndFont(list
, "List.background",
922 "List.foreground", "List.font");
923 list
.setSelectionForeground(UIManager
.getColor("List.selectionForeground"));
924 list
.setSelectionBackground(UIManager
.getColor("List.selectionBackground"));
925 list
.setOpaque(true);
929 * Resets to <code>null</code> those defaults which were installed in
930 * {@link #installDefaults}
932 protected void uninstallDefaults()
934 list
.setForeground(null);
935 list
.setBackground(null);
936 list
.setSelectionForeground(null);
937 list
.setSelectionBackground(null);
941 * Attaches all the listeners we have in the UI class to the {@link
942 * JList}, its model and its selection model.
944 * @see #uninstallListeners
946 protected void installListeners()
948 if (focusListener
== null)
949 focusListener
= createFocusListener();
950 list
.addFocusListener(focusListener
);
951 if (listDataListener
== null)
952 listDataListener
= createListDataListener();
953 list
.getModel().addListDataListener(listDataListener
);
954 if (listSelectionListener
== null)
955 listSelectionListener
= createListSelectionListener();
956 list
.addListSelectionListener(listSelectionListener
);
957 if (mouseInputListener
== null)
958 mouseInputListener
= createMouseInputListener();
959 list
.addMouseListener(mouseInputListener
);
960 list
.addMouseMotionListener(mouseInputListener
);
961 if (propertyChangeListener
== null)
962 propertyChangeListener
= createPropertyChangeListener();
963 list
.addPropertyChangeListener(propertyChangeListener
);
967 * Detaches all the listeners we attached in {@link #installListeners}.
969 protected void uninstallListeners()
971 list
.removeFocusListener(focusListener
);
972 list
.getModel().removeListDataListener(listDataListener
);
973 list
.removeListSelectionListener(listSelectionListener
);
974 list
.removeMouseListener(mouseInputListener
);
975 list
.removeMouseMotionListener(mouseInputListener
);
976 list
.removePropertyChangeListener(propertyChangeListener
);
980 * Installs keyboard actions for this UI in the {@link JList}.
982 protected void installKeyboardActions()
984 InputMap focusInputMap
= (InputMap
) UIManager
.get("List.focusInputMap");
985 InputMapUIResource parentInputMap
= new InputMapUIResource();
986 // FIXME: The JDK uses a LazyActionMap for parentActionMap
987 ActionMap parentActionMap
= new ActionMapUIResource();
988 action
= new ListAction();
989 Object keys
[] = focusInputMap
.allKeys();
990 // Register key bindings in the UI InputMap-ActionMap pair
991 for (int i
= 0; i
< keys
.length
; i
++)
993 KeyStroke stroke
= (KeyStroke
)keys
[i
];
994 String actionString
= (String
) focusInputMap
.get(stroke
);
995 parentInputMap
.put(KeyStroke
.getKeyStroke(stroke
.getKeyCode(),
996 stroke
.getModifiers()),
999 parentActionMap
.put (actionString
,
1000 new ActionListenerProxy(action
, actionString
));
1002 // Register the new InputMap-ActionMap as the parents of the list's
1003 // InputMap and ActionMap
1004 parentInputMap
.setParent(list
.getInputMap().getParent());
1005 parentActionMap
.setParent(list
.getActionMap().getParent());
1006 list
.getInputMap().setParent(parentInputMap
);
1007 list
.getActionMap().setParent(parentActionMap
);
1011 * Uninstalls keyboard actions for this UI in the {@link JList}.
1013 protected void uninstallKeyboardActions()
1015 // TODO: Implement this properly.
1019 * Installs the various aspects of the UI in the {@link JList}. In
1020 * particular, calls {@link #installDefaults}, {@link #installListeners}
1021 * and {@link #installKeyboardActions}. Also saves a reference to the
1022 * provided component, cast to a {@link JList}.
1024 * @param c The {@link JList} to install the UI into
1026 public void installUI(final JComponent c
)
1032 installKeyboardActions();
1033 maybeUpdateLayoutState();
1037 * Uninstalls all the aspects of the UI which were installed in {@link
1038 * #installUI}. When finished uninstalling, drops the saved reference to
1039 * the {@link JList}.
1041 * @param c Ignored; the UI is uninstalled from the {@link JList}
1042 * reference saved during the call to {@link #installUI}
1044 public void uninstallUI(final JComponent c
)
1046 uninstallKeyboardActions();
1047 uninstallListeners();
1048 uninstallDefaults();
1053 * Gets the size this list would prefer to assume. This is calculated by
1054 * calling {@link #getCellBounds} over the entire list.
1056 * @param c Ignored; uses the saved {@link JList} reference
1058 * @return DOCUMENT ME!
1060 public Dimension
getPreferredSize(JComponent c
)
1062 maybeUpdateLayoutState();
1063 int size
= list
.getModel().getSize();
1064 int visibleRows
= list
.getVisibleRowCount();
1065 int layoutOrientation
= list
.getLayoutOrientation();
1069 int maxCellHeight
= cellHeight
;
1070 if (maxCellHeight
<= 0)
1072 for (int i
= 0; i
< cellHeights
.length
; i
++)
1073 maxCellHeight
= Math
.max(maxCellHeight
, cellHeights
[i
]);
1075 if (layoutOrientation
== JList
.HORIZONTAL_WRAP
)
1077 if (visibleRows
> 0)
1079 // We cast to double here to force double divisions.
1080 double modelSize
= size
;
1081 int neededColumns
= (int) Math
.ceil(modelSize
/ visibleRows
);
1082 int adjustedRows
= (int) Math
.ceil(modelSize
/ neededColumns
);
1083 h
= maxCellHeight
* adjustedRows
;
1084 w
= cellWidth
* neededColumns
;
1088 int neededColumns
= Math
.min(1, list
.getWidth() / cellWidth
);
1089 h
= size
/ neededColumns
* maxCellHeight
;
1090 w
= neededColumns
* cellWidth
;
1093 else if (layoutOrientation
== JList
.VERTICAL_WRAP
)
1095 if (visibleRows
> 0)
1096 h
= visibleRows
* maxCellHeight
;
1098 h
= Math
.max(list
.getHeight(), maxCellHeight
);
1099 int neededColumns
= h
/ maxCellHeight
;
1100 w
= cellWidth
* neededColumns
;
1104 if (list
.getFixedCellWidth() > 0)
1105 w
= list
.getFixedCellWidth();
1108 if (list
.getFixedCellHeight() > 0)
1109 // FIXME: We need to add some cellVerticalMargins here, according
1111 h
= list
.getFixedCellHeight() * size
;
1113 h
= maxCellHeight
* size
;
1115 Insets insets
= list
.getInsets();
1116 Dimension retVal
= new Dimension(w
+ insets
.left
+ insets
.right
,
1117 h
+ insets
.top
+ insets
.bottom
);
1122 * Paints a single cell in the list.
1124 * @param g The graphics context to paint in
1125 * @param row The row number to paint
1126 * @param bounds The bounds of the cell to paint, assuming a coordinate
1127 * system beginning at <code>(0,0)</code> in the upper left corner of the
1129 * @param rend A cell renderer to paint with
1130 * @param data The data to provide to the cell renderer
1131 * @param sel A selection model to provide to the cell renderer
1132 * @param lead The lead selection index of the list
1134 protected void paintCell(Graphics g
, int row
, Rectangle bounds
,
1135 ListCellRenderer rend
, ListModel data
,
1136 ListSelectionModel sel
, int lead
)
1138 boolean isSel
= list
.isSelectedIndex(row
);
1139 boolean hasFocus
= (list
.getLeadSelectionIndex() == row
) && BasicListUI
.this.list
.hasFocus();
1140 Component comp
= rend
.getListCellRendererComponent(list
,
1141 data
.getElementAt(row
),
1142 0, isSel
, hasFocus
);
1143 rendererPane
.paintComponent(g
, comp
, list
, bounds
);
1147 * Paints the list by repeatedly calling {@link #paintCell} for each visible
1150 * @param g The graphics context to paint with
1151 * @param c Ignored; uses the saved {@link JList} reference
1153 public void paint(Graphics g
, JComponent c
)
1155 int nrows
= list
.getModel().getSize();
1159 maybeUpdateLayoutState();
1160 ListCellRenderer render
= list
.getCellRenderer();
1161 ListModel model
= list
.getModel();
1162 ListSelectionModel sel
= list
.getSelectionModel();
1163 int lead
= sel
.getLeadSelectionIndex();
1164 Rectangle clip
= g
.getClipBounds();
1166 int startIndex
= locationToIndex(list
, new Point(clip
.x
, clip
.y
));
1167 int endIndex
= locationToIndex(list
, new Point(clip
.x
+ clip
.width
,
1168 clip
.y
+ clip
.height
));
1170 for (int row
= startIndex
; row
<= endIndex
; ++row
)
1172 Rectangle bounds
= getCellBounds(list
, row
, row
);
1173 if (bounds
.intersects(clip
))
1174 paintCell(g
, row
, bounds
, render
, model
, sel
, lead
);
1179 * Computes the index of a list cell given a point within the list. If the
1180 * location lies outside the bounds of the list, the greatest index in the
1181 * list model is returned.
1183 * @param l the list which on which the computation is based on
1184 * @param location the coordinates
1186 * @return the index of the list item that is located at the given
1187 * coordinates or <code>-1</code> if the list model is empty
1189 public int locationToIndex(JList l
, Point location
)
1191 int layoutOrientation
= list
.getLayoutOrientation();
1193 switch (layoutOrientation
)
1195 case JList
.VERTICAL
:
1196 index
= convertYToRow(location
.y
);
1198 case JList
.HORIZONTAL_WRAP
:
1199 // determine visible rows and cells per row
1200 int maxCellHeight
= getCellHeight(0);
1201 int visibleRows
= list
.getHeight() / maxCellHeight
;
1202 int cellsPerRow
= -1;
1203 int numberOfItems
= list
.getModel().getSize();
1204 cellsPerRow
= numberOfItems
/ visibleRows
+ 1;
1206 // determine index for the given location
1207 int cellsPerColumn
= numberOfItems
/ cellsPerRow
+ 1;
1208 int gridX
= Math
.min(location
.x
/ cellWidth
, cellsPerRow
- 1);
1209 int gridY
= Math
.min(location
.y
/ maxCellHeight
, cellsPerColumn
);
1210 index
= gridX
+ gridY
* cellsPerRow
;
1212 case JList
.VERTICAL_WRAP
:
1213 // determine visible rows and cells per column
1214 int maxCellHeight2
= getCellHeight(0);
1215 int visibleRows2
= list
.getHeight() / maxCellHeight2
;
1216 int numberOfItems2
= list
.getModel().getSize();
1217 int cellsPerRow2
= numberOfItems2
/ visibleRows2
+ 1;
1219 int gridX2
= Math
.min(location
.x
/ cellWidth
, cellsPerRow2
- 1);
1220 int gridY2
= Math
.min(location
.y
/ maxCellHeight2
, visibleRows2
);
1221 index
= gridY2
+ gridX2
* visibleRows2
;
1227 public Point
indexToLocation(JList l
, int index
)
1229 int layoutOrientation
= list
.getLayoutOrientation();
1231 switch (layoutOrientation
)
1233 case JList
.VERTICAL
:
1234 loc
= new Point(0, convertRowToY(index
));
1236 case JList
.HORIZONTAL_WRAP
:
1237 // determine visible rows and cells per row
1238 int maxCellHeight
= getCellHeight(0);
1239 int visibleRows
= list
.getHeight() / maxCellHeight
;
1240 int numberOfCellsPerRow
= -1;
1241 int numberOfItems
= list
.getModel().getSize();
1242 numberOfCellsPerRow
= numberOfItems
/ visibleRows
+ 1;
1244 // compute coordinates inside the grid
1245 int gridX
= index
% numberOfCellsPerRow
;
1246 int gridY
= index
/ numberOfCellsPerRow
;
1247 int locX
= gridX
* cellWidth
;
1249 locY
= gridY
* maxCellHeight
;
1250 loc
= new Point(locX
, locY
);
1252 case JList
.VERTICAL_WRAP
:
1253 // determine visible rows and cells per column
1254 int maxCellHeight2
= getCellHeight(0);
1255 int visibleRows2
= list
.getHeight() / maxCellHeight2
;
1256 // compute coordinates inside the grid
1257 if (visibleRows2
> 0)
1259 int gridY2
= index
% visibleRows2
;
1260 int gridX2
= index
/ visibleRows2
;
1261 int locX2
= gridX2
* cellWidth
;
1262 int locY2
= gridY2
* maxCellHeight2
;
1263 loc
= new Point(locX2
, locY2
);
1266 loc
= new Point(0, convertRowToY(index
));
1273 * Creates and returns the focus listener for this UI.
1275 * @return the focus listener for this UI
1277 protected FocusListener
createFocusListener()
1279 return new FocusHandler();
1283 * Creates and returns the list data listener for this UI.
1285 * @return the list data listener for this UI
1287 protected ListDataListener
createListDataListener()
1289 return new ListDataHandler();
1293 * Creates and returns the list selection listener for this UI.
1295 * @return the list selection listener for this UI
1297 protected ListSelectionListener
createListSelectionListener()
1299 return new ListSelectionHandler();
1303 * Creates and returns the mouse input listener for this UI.
1305 * @return the mouse input listener for this UI
1307 protected MouseInputListener
createMouseInputListener()
1309 return new MouseInputHandler();
1313 * Creates and returns the property change listener for this UI.
1315 * @return the property change listener for this UI
1317 protected PropertyChangeListener
createPropertyChangeListener()
1319 return new PropertyChangeHandler();
1323 * Selects the next list item and force it to be visible.
1325 protected void selectNextIndex()
1327 int index
= list
.getSelectionModel().getLeadSelectionIndex();
1328 if (index
< list
.getModel().getSize() - 1)
1331 list
.setSelectedIndex(index
);
1333 list
.ensureIndexIsVisible(index
);
1337 * Selects the previous list item and force it to be visible.
1339 protected void selectPreviousIndex()
1341 int index
= list
.getSelectionModel().getLeadSelectionIndex();
1345 list
.setSelectedIndex(index
);
1347 list
.ensureIndexIsVisible(index
);