38 |
|
|
39 |
package javax.swing.plaf.basic; |
package javax.swing.plaf.basic; |
40 |
|
|
|
import java.awt.Color; |
|
41 |
import java.awt.Component; |
import java.awt.Component; |
42 |
import java.awt.Dimension; |
import java.awt.Dimension; |
43 |
import java.awt.Graphics; |
import java.awt.Graphics; |
50 |
import java.awt.event.ComponentListener; |
import java.awt.event.ComponentListener; |
51 |
import java.awt.event.FocusEvent; |
import java.awt.event.FocusEvent; |
52 |
import java.awt.event.FocusListener; |
import java.awt.event.FocusListener; |
|
import java.awt.event.KeyEvent; |
|
53 |
import java.awt.event.MouseEvent; |
import java.awt.event.MouseEvent; |
54 |
import java.beans.PropertyChangeEvent; |
import java.beans.PropertyChangeEvent; |
55 |
import java.beans.PropertyChangeListener; |
import java.beans.PropertyChangeListener; |
74 |
import javax.swing.event.ListSelectionEvent; |
import javax.swing.event.ListSelectionEvent; |
75 |
import javax.swing.event.ListSelectionListener; |
import javax.swing.event.ListSelectionListener; |
76 |
import javax.swing.event.MouseInputListener; |
import javax.swing.event.MouseInputListener; |
77 |
|
import javax.swing.plaf.ActionMapUIResource; |
78 |
import javax.swing.plaf.ComponentUI; |
import javax.swing.plaf.ComponentUI; |
79 |
import javax.swing.plaf.InputMapUIResource; |
import javax.swing.plaf.InputMapUIResource; |
80 |
import javax.swing.plaf.ListUI; |
import javax.swing.plaf.ListUI; |
190 |
*/ |
*/ |
191 |
public void valueChanged(ListSelectionEvent e) |
public void valueChanged(ListSelectionEvent e) |
192 |
{ |
{ |
193 |
// TODO: Implement this properly. |
int index1 = e.getFirstIndex(); |
194 |
|
int index2 = e.getLastIndex(); |
195 |
|
Rectangle damaged = getCellBounds(list, index1, index2); |
196 |
|
list.repaint(damaged); |
197 |
} |
} |
198 |
} |
} |
199 |
|
|
524 |
*/ |
*/ |
525 |
public void mouseDragged(MouseEvent event) |
public void mouseDragged(MouseEvent event) |
526 |
{ |
{ |
527 |
// TODO: What should be done here, if anything? |
Point click = event.getPoint(); |
528 |
|
int index = locationToIndex(list, click); |
529 |
|
if (index == -1) |
530 |
|
return; |
531 |
|
if (!event.isShiftDown() && !event.isControlDown()) |
532 |
|
list.setSelectedIndex(index); |
533 |
|
|
534 |
|
list.ensureIndexIsVisible(list.getLeadSelectionIndex()); |
535 |
} |
} |
536 |
|
|
537 |
/** |
/** |
582 |
updateLayoutStateNeeded += prototypeCellValueChanged; |
updateLayoutStateNeeded += prototypeCellValueChanged; |
583 |
else if (e.getPropertyName().equals("cellRenderer")) |
else if (e.getPropertyName().equals("cellRenderer")) |
584 |
updateLayoutStateNeeded += cellRendererChanged; |
updateLayoutStateNeeded += cellRendererChanged; |
|
|
|
585 |
BasicListUI.this.damageLayout(); |
BasicListUI.this.damageLayout(); |
586 |
} |
} |
587 |
} |
} |
656 |
/** Saved reference to the list this UI was created for. */ |
/** Saved reference to the list this UI was created for. */ |
657 |
protected JList list; |
protected JList list; |
658 |
|
|
659 |
/** The height of a single cell in the list. */ |
/** |
660 |
|
* The height of a single cell in the list. This field is used when the |
661 |
|
* fixedCellHeight property of the list is set. Otherwise this field is |
662 |
|
* set to <code>-1</code> and {@link #cellHeights} is used instead. |
663 |
|
*/ |
664 |
protected int cellHeight; |
protected int cellHeight; |
665 |
|
|
666 |
/** The width of a single cell in the list. */ |
/** The width of a single cell in the list. */ |
668 |
|
|
669 |
/** |
/** |
670 |
* An array of varying heights of cells in the list, in cases where each |
* An array of varying heights of cells in the list, in cases where each |
671 |
* cell might have a different height. |
* cell might have a different height. This field is used when the |
672 |
|
* <code>fixedCellHeight</code> property of the list is not set. Otherwise |
673 |
|
* this field is <code>null</code> and {@link #cellHeight} is used. |
674 |
*/ |
*/ |
675 |
protected int[] cellHeights; |
protected int[] cellHeights; |
676 |
|
|
710 |
*/ |
*/ |
711 |
protected int getRowHeight(int row) |
protected int getRowHeight(int row) |
712 |
{ |
{ |
713 |
if (row < 0 || row >= cellHeights.length) |
int height; |
714 |
return -1; |
if (cellHeights == null) |
715 |
else if (cellHeight != -1) |
height = cellHeight; |
|
return cellHeight; |
|
716 |
else |
else |
717 |
return cellHeights[row]; |
{ |
718 |
|
if (row < 0 || row >= cellHeights.length) |
719 |
|
height = -1; |
720 |
|
else |
721 |
|
height = cellHeights[row]; |
722 |
|
} |
723 |
|
return height; |
724 |
} |
} |
725 |
|
|
726 |
/** |
/** |
789 |
* @param y0 The Y coordinate to calculate the row number for |
* @param y0 The Y coordinate to calculate the row number for |
790 |
* |
* |
791 |
* @return The row number containing the specified Y value, or <code>-1</code> |
* @return The row number containing the specified Y value, or <code>-1</code> |
792 |
* if the specified Y coordinate is invalid |
* if the list model is empty |
793 |
|
* |
794 |
|
* @specnote This method is specified to return -1 for an invalid Y |
795 |
|
* coordinate. However, some simple tests show that the behaviour |
796 |
|
* is to return the index of the last list element for an Y |
797 |
|
* coordinate that lies outside of the list bounds (even for |
798 |
|
* negative indices). <code>-1</code> |
799 |
|
* is only returned if the list model is empty. |
800 |
*/ |
*/ |
801 |
protected int convertYToRow(int y0) |
protected int convertYToRow(int y0) |
802 |
{ |
{ |
803 |
for (int row = 0; row < cellHeights.length; ++row) |
if (list.getModel().getSize() == 0) |
804 |
{ |
return -1; |
805 |
int h = getRowHeight(row); |
|
806 |
|
// When y0 < 0, then the JDK returns the maximum row index of the list. So |
807 |
|
// do we. |
808 |
|
if (y0 < 0) |
809 |
|
return list.getModel().getSize() - 1; |
810 |
|
|
811 |
|
// Update the layout if necessary. |
812 |
|
maybeUpdateLayoutState(); |
813 |
|
|
814 |
|
int index = list.getModel().getSize() - 1;; |
815 |
|
|
816 |
if (y0 < h) |
// If a fixed cell height is set, then we can work more efficient. |
817 |
return row; |
if (cellHeight > 0) |
818 |
y0 -= h; |
index = Math.min(y0 / cellHeight, index); |
819 |
|
// If we have no fixed cell height, we must add up each cell height up |
820 |
|
// to y0. |
821 |
|
else |
822 |
|
{ |
823 |
|
int h = 0; |
824 |
|
for (int row = 0; row < cellHeights.length; ++row) |
825 |
|
{ |
826 |
|
h += cellHeights[row]; |
827 |
|
if (y0 < h) |
828 |
|
{ |
829 |
|
index = row; |
830 |
|
break; |
831 |
|
} |
832 |
|
} |
833 |
} |
} |
834 |
return -1; |
return index; |
835 |
} |
} |
836 |
|
|
837 |
/** |
/** |
846 |
cellWidth = -1; |
cellWidth = -1; |
847 |
if (cellHeights == null || cellHeights.length != nrows) |
if (cellHeights == null || cellHeights.length != nrows) |
848 |
cellHeights = new int[nrows]; |
cellHeights = new int[nrows]; |
849 |
if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1) |
ListCellRenderer rend = list.getCellRenderer(); |
850 |
|
// Update the cellHeight(s) fields. |
851 |
|
int fixedCellHeight = list.getFixedCellHeight(); |
852 |
|
if (fixedCellHeight > 0) |
853 |
|
{ |
854 |
|
cellHeight = fixedCellHeight; |
855 |
|
cellHeights = null; |
856 |
|
} |
857 |
|
else |
858 |
{ |
{ |
859 |
ListCellRenderer rend = list.getCellRenderer(); |
cellHeight = -1; |
860 |
for (int i = 0; i < nrows; ++i) |
for (int i = 0; i < nrows; ++i) |
861 |
{ |
{ |
862 |
Component flyweight = rend.getListCellRendererComponent(list, |
Component flyweight = |
863 |
list.getModel() |
rend.getListCellRendererComponent(list, |
864 |
.getElementAt(i), |
list.getModel().getElementAt(i), |
865 |
0, false, |
i, list.isSelectedIndex(i), |
866 |
false); |
list.getSelectionModel().getAnchorSelectionIndex() == i); |
867 |
Dimension dim = flyweight.getPreferredSize(); |
Dimension dim = flyweight.getPreferredSize(); |
868 |
cellHeights[i] = dim.height; |
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); |
|
869 |
} |
} |
870 |
} |
} |
871 |
|
|
872 |
|
// Update the cellWidth field. |
873 |
|
int fixedCellWidth = list.getFixedCellWidth(); |
874 |
|
if (fixedCellWidth > 0) |
875 |
|
cellWidth = fixedCellWidth; |
876 |
else |
else |
877 |
{ |
{ |
878 |
cellHeight = list.getFixedCellHeight(); |
for (int i = 0; i < nrows; ++i) |
879 |
cellWidth = list.getFixedCellWidth(); |
{ |
880 |
|
Component flyweight = |
881 |
|
rend.getListCellRendererComponent(list, |
882 |
|
list.getModel().getElementAt(i), |
883 |
|
i, list.isSelectedIndex(i), |
884 |
|
list.getSelectionModel().getAnchorSelectionIndex() == i); |
885 |
|
Dimension dim = flyweight.getPreferredSize(); |
886 |
|
cellWidth = Math.max(cellWidth, dim.width); |
887 |
|
} |
888 |
|
if (list.getLayoutOrientation() == JList.VERTICAL) |
889 |
|
cellWidth = Math.max(cellWidth, list.getSize().width); |
890 |
} |
} |
891 |
} |
} |
892 |
|
|
945 |
*/ |
*/ |
946 |
protected void uninstallDefaults() |
protected void uninstallDefaults() |
947 |
{ |
{ |
|
UIDefaults defaults = UIManager.getLookAndFeelDefaults(); |
|
948 |
list.setForeground(null); |
list.setForeground(null); |
949 |
list.setBackground(null); |
list.setBackground(null); |
950 |
list.setSelectionForeground(null); |
list.setSelectionForeground(null); |
978 |
|
|
979 |
// FIXME: Are these two really needed? At least they are not documented. |
// FIXME: Are these two really needed? At least they are not documented. |
980 |
//keyListener = new KeyHandler(); |
//keyListener = new KeyHandler(); |
|
list.addComponentListener(componentListener); |
|
981 |
componentListener = new ComponentHandler(); |
componentListener = new ComponentHandler(); |
982 |
|
list.addComponentListener(componentListener); |
983 |
//list.addKeyListener(keyListener); |
//list.addKeyListener(keyListener); |
984 |
} |
} |
985 |
|
|
1002 |
*/ |
*/ |
1003 |
protected void installKeyboardActions() |
protected void installKeyboardActions() |
1004 |
{ |
{ |
1005 |
UIDefaults defaults = UIManager.getLookAndFeelDefaults(); |
InputMap focusInputMap = (InputMap) UIManager.get("List.focusInputMap"); |
|
InputMap focusInputMap = (InputMap)defaults.get("List.focusInputMap"); |
|
1006 |
InputMapUIResource parentInputMap = new InputMapUIResource(); |
InputMapUIResource parentInputMap = new InputMapUIResource(); |
1007 |
// FIXME: The JDK uses a LazyActionMap for parentActionMap |
// FIXME: The JDK uses a LazyActionMap for parentActionMap |
1008 |
ActionMap parentActionMap = new ActionMap(); |
ActionMap parentActionMap = new ActionMapUIResource(); |
1009 |
action = new ListAction(); |
action = new ListAction(); |
1010 |
Object keys[] = focusInputMap.allKeys(); |
Object keys[] = focusInputMap.allKeys(); |
1011 |
// Register key bindings in the UI InputMap-ActionMap pair |
// Register key bindings in the UI InputMap-ActionMap pair |
1111 |
} |
} |
1112 |
|
|
1113 |
/** |
/** |
|
* 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); |
|
|
} |
|
|
|
|
|
/** |
|
1114 |
* Paints a single cell in the list. |
* Paints a single cell in the list. |
1115 |
* |
* |
1116 |
* @param g The graphics context to paint in |
* @param g The graphics context to paint in |
1132 |
Component comp = rend.getListCellRendererComponent(list, |
Component comp = rend.getListCellRendererComponent(list, |
1133 |
data.getElementAt(row), |
data.getElementAt(row), |
1134 |
0, isSel, hasFocus); |
0, isSel, hasFocus); |
|
//comp.setBounds(new Rectangle(0, 0, bounds.width, bounds.height)); |
|
|
//comp.paint(g); |
|
1135 |
rendererPane.paintComponent(g, comp, list, bounds); |
rendererPane.paintComponent(g, comp, list, bounds); |
1136 |
} |
} |
1137 |
|
|
1138 |
/** |
/** |
1139 |
* Paints the list by calling {@link #paintBackground} and then repeatedly |
* Paints the list by repeatedly calling {@link #paintCell} for each visible |
1140 |
* calling {@link #paintCell} for each visible cell in the list. |
* cell in the list. |
1141 |
* |
* |
1142 |
* @param g The graphics context to paint with |
* @param g The graphics context to paint with |
1143 |
* @param c Ignored; uses the saved {@link JList} reference |
* @param c Ignored; uses the saved {@link JList} reference |
1154 |
ListSelectionModel sel = list.getSelectionModel(); |
ListSelectionModel sel = list.getSelectionModel(); |
1155 |
int lead = sel.getLeadSelectionIndex(); |
int lead = sel.getLeadSelectionIndex(); |
1156 |
Rectangle clip = g.getClipBounds(); |
Rectangle clip = g.getClipBounds(); |
|
paintBackground(g, list); |
|
1157 |
|
|
1158 |
for (int row = 0; row < nrows; ++row) |
int startIndex = list.locationToIndex(new Point(clip.x, clip.y)); |
1159 |
|
int endIndex = list.locationToIndex(new Point(clip.x + clip.width, |
1160 |
|
clip.y + clip.height)); |
1161 |
|
|
1162 |
|
for (int row = startIndex; row <= endIndex; ++row) |
1163 |
{ |
{ |
1164 |
Rectangle bounds = getCellBounds(list, row, row); |
Rectangle bounds = getCellBounds(list, row, row); |
1165 |
if (bounds.intersects(clip)) |
if (bounds.intersects(clip)) |
1168 |
} |
} |
1169 |
|
|
1170 |
/** |
/** |
1171 |
* Computes the index of a list cell given a point within the list. |
* Computes the index of a list cell given a point within the list. If the |
1172 |
|
* location lies outside the bounds of the list, the greatest index in the |
1173 |
|
* list model is returned. |
1174 |
* |
* |
1175 |
* @param list the list which on which the computation is based on |
* @param list the list which on which the computation is based on |
1176 |
* @param location the coordinates |
* @param location the coordinates |
1177 |
* |
* |
1178 |
* @return the index of the list item that is located at the given |
* @return the index of the list item that is located at the given |
1179 |
* coordinates or <code>null</code> if the location is invalid |
* coordinates or <code>-1</code> if the list model is empty |
1180 |
*/ |
*/ |
1181 |
public int locationToIndex(JList list, Point location) |
public int locationToIndex(JList list, Point location) |
1182 |
{ |
{ |
1226 |
int numberOfItems2 = list.getModel().getSize(); |
int numberOfItems2 = list.getModel().getSize(); |
1227 |
int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1; |
int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1; |
1228 |
|
|
|
Dimension listDim2 = list.getSize(); |
|
1229 |
int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1); |
int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1); |
1230 |
int gridY2 = Math.min(location.y / cellHeight, visibleRows2); |
int gridY2 = Math.min(location.y / cellHeight, visibleRows2); |
1231 |
index = gridY2 + gridX2 * visibleRows2; |
index = gridY2 + gridX2 * visibleRows2; |