603 * of the list, or the maximum cell height, whichever is
604 * bigger. The preferred width is than the maximum cell width *
605 * number of columns needed. Where the number of columns needs is
606 * list.height / max cell height. Max cell height is either the fixed
607 * cell height, or is determined by iterating through all the cells
608 * to find the maximum height from the ListCellRenderer.
609 * <tr>
610 * <td>JList.HORIZONTAL_WRAP
611 * <td>If the visible row count is greater than zero, the preferredHeight
612 * is the maximum cell height * adjustedRowCount. Where
613 * visibleRowCount is used to determine the number of columns.
614 * Because this lays out horizontally the number of rows is
615 * then determined from the column count. For example, lets say
616 * you have a model with 10 items and the visible row count is 8.
617 * The number of columns needed to display this is 2, but you no
618 * longer need 8 rows to display this, you only need 5, thus
619 * the adjustedRowCount is 5.
620 * <p>If the visible row
621 * count is <= 0, the preferred height is dictated by the
622 * number of columns, which will be as many as can fit in the width
623 * of the <code>JList</code> (width / max cell width), with at
624 * least one column. The preferred height then becomes the
625 * model size / number of columns * maximum cell height.
626 * Max cell height is either the fixed
627 * cell height, or is determined by iterating through all the cells
628 * to find the maximum height from the ListCellRenderer.
629 * </table>
630 * The above specifies the raw preferred width and height. The resulting
631 * preferred width is the above width + insets.left + insets.right and
632 * the resulting preferred height is the above height + insets.top +
633 * insets.bottom. Where the <code>Insets</code> are determined from
634 * <code>list.getInsets()</code>.
635 *
636 * @param c The JList component.
637 * @return The total size of the list.
638 */
639 public Dimension getPreferredSize(JComponent c) {
640 maybeUpdateLayoutState();
641
642 int lastRow = list.getModel().getSize() - 1;
643 if (lastRow < 0) {
644 return new Dimension(0, 0);
645 }
646
647 Insets insets = list.getInsets();
648 int width = cellWidth * columnCount + insets.left + insets.right;
649 int height;
650
651 if (layoutOrientation != JList.VERTICAL) {
652 height = preferredHeight;
653 }
654 else {
680 }
681
682
683 /**
684 * Selected the previous row and force it to be visible.
685 *
686 * @see JList#ensureIndexIsVisible
687 */
688 protected void selectNextIndex()
689 {
690 int s = list.getSelectedIndex();
691 if((s + 1) < list.getModel().getSize()) {
692 s += 1;
693 list.setSelectedIndex(s);
694 list.ensureIndexIsVisible(s);
695 }
696 }
697
698
699 /**
700 * Registers the keyboard bindings on the <code>JList</code> that the
701 * <code>BasicListUI</code> is associated with. This method is called at
702 * installUI() time.
703 *
704 * @see #installUI
705 */
706 protected void installKeyboardActions() {
707 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
708
709 SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
710 inputMap);
711
712 LazyActionMap.installLazyActionMap(list, BasicListUI.class,
713 "List.actionMap");
714 }
715
716 InputMap getInputMap(int condition) {
717 if (condition == JComponent.WHEN_FOCUSED) {
718 InputMap keyMap = (InputMap)DefaultLookup.get(
719 list, this, "List.focusInputMap");
720 InputMap rtlKeyMap;
721
722 if (isLeftToRight ||
723 ((rtlKeyMap = (InputMap)DefaultLookup.get(list, this,
724 "List.focusInputMap.RightToLeft")) == null)) {
725 return keyMap;
726 } else {
727 rtlKeyMap.setParent(keyMap);
728 return rtlKeyMap;
729 }
730 }
731 return null;
732 }
733
734 /**
735 * Unregisters keyboard actions installed from
736 * <code>installKeyboardActions</code>.
737 * This method is called at uninstallUI() time - subclassess should
738 * ensure that all of the keyboard actions registered at installUI
739 * time are removed here.
740 *
741 * @see #installUI
742 */
743 protected void uninstallKeyboardActions() {
744 SwingUtilities.replaceUIActionMap(list, null);
745 SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
746 }
747
748
749 /**
750 * Creates and installs the listeners for the JList, its model, and its
751 * selectionModel. This method is called at installUI() time.
752 *
753 * @see #installUI
754 * @see #uninstallListeners
755 */
756 protected void installListeners()
903 }
904 if (list.getBackground() instanceof UIResource) {
905 list.setBackground(null);
906 }
907 if (list.getSelectionBackground() instanceof UIResource) {
908 list.setSelectionBackground(null);
909 }
910 if (list.getSelectionForeground() instanceof UIResource) {
911 list.setSelectionForeground(null);
912 }
913 if (list.getCellRenderer() instanceof UIResource) {
914 list.setCellRenderer(null);
915 }
916 if (list.getTransferHandler() instanceof UIResource) {
917 list.setTransferHandler(null);
918 }
919 }
920
921
922 /**
923 * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
924 * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
925 * in order.
926 *
927 * @see #installDefaults
928 * @see #installListeners
929 * @see #installKeyboardActions
930 */
931 public void installUI(JComponent c)
932 {
933 @SuppressWarnings("unchecked")
934 JList<Object> tmp = (JList)c;
935 list = tmp;
936
937 layoutOrientation = list.getLayoutOrientation();
938
939 rendererPane = new CellRendererPane();
940 list.add(rendererPane);
941
942 columnCount = 1;
943
944 updateLayoutStateNeeded = modelChanged;
945 isLeftToRight = list.getComponentOrientation().isLeftToRight();
946
947 installDefaults();
948 installListeners();
949 installKeyboardActions();
950 }
951
952
953 /**
954 * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
955 * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>
956 * in order. Sets this.list to null.
957 *
958 * @see #uninstallListeners
959 * @see #uninstallKeyboardActions
960 * @see #uninstallDefaults
961 */
962 public void uninstallUI(JComponent c)
963 {
964 uninstallListeners();
965 uninstallDefaults();
966 uninstallKeyboardActions();
967
968 cellWidth = cellHeight = -1;
969 cellHeights = null;
970
971 listWidth = listHeight = -1;
972
973 list.remove(rendererPane);
974 rendererPane = null;
975 list = null;
1040 int minRow = convertModelToRow(minIndex);
1041 int maxRow = convertModelToRow(maxIndex);
1042
1043 if (minRow != maxRow) {
1044 minBounds.x = 0;
1045 minBounds.width = list.getWidth();
1046 }
1047 }
1048 else if (minBounds.x != maxBounds.x) {
1049 // Different columns
1050 minBounds.y = 0;
1051 minBounds.height = list.getHeight();
1052 }
1053 minBounds.add(maxBounds);
1054 }
1055 return minBounds;
1056 }
1057
1058 /**
1059 * Gets the bounds of the specified model index, returning the resulting
1060 * bounds, or null if <code>index</code> is not valid.
1061 */
1062 private Rectangle getCellBounds(JList<?> list, int index) {
1063 maybeUpdateLayoutState();
1064
1065 int row = convertModelToRow(index);
1066 int column = convertModelToColumn(index);
1067
1068 if (row == -1 || column == -1) {
1069 return null;
1070 }
1071
1072 Insets insets = list.getInsets();
1073 int x;
1074 int w = cellWidth;
1075 int y = insets.top;
1076 int h;
1077 switch (layoutOrientation) {
1078 case JList.VERTICAL_WRAP:
1079 case JList.HORIZONTAL_WRAP:
1080 if (isLeftToRight) {
1266 }
1267 if (layoutOrientation == JList.VERTICAL_WRAP) {
1268 if (column < (columnCount - 1)) {
1269 return rowsPerColumn;
1270 }
1271 return list.getModel().getSize() - (columnCount - 1) *
1272 rowsPerColumn;
1273 }
1274 // JList.HORIZONTAL_WRAP
1275 int diff = columnCount - (columnCount * rowsPerColumn -
1276 list.getModel().getSize());
1277
1278 if (column >= diff) {
1279 return Math.max(0, rowsPerColumn - 1);
1280 }
1281 return rowsPerColumn;
1282 }
1283
1284 /**
1285 * Returns the model index for the specified display location.
1286 * If <code>column</code>x<code>row</code> is beyond the length of the
1287 * model, this will return the model size - 1.
1288 */
1289 private int getModelIndex(int column, int row) {
1290 switch (layoutOrientation) {
1291 case JList.VERTICAL_WRAP:
1292 return Math.min(list.getModel().getSize() - 1, rowsPerColumn *
1293 column + Math.min(row, rowsPerColumn-1));
1294 case JList.HORIZONTAL_WRAP:
1295 return Math.min(list.getModel().getSize() - 1, row * columnCount +
1296 column);
1297 default:
1298 return row;
1299 }
1300 }
1301
1302 /**
1303 * Returns the closest column to the passed in location.
1304 */
1305 private int convertLocationToColumn(int x, int y) {
1306 if (cellWidth > 0) {
1309 }
1310 Insets insets = list.getInsets();
1311 int col;
1312 if (isLeftToRight) {
1313 col = (x - insets.left) / cellWidth;
1314 } else {
1315 col = (list.getWidth() - x - insets.right - 1) / cellWidth;
1316 }
1317 if (col < 0) {
1318 return 0;
1319 }
1320 else if (col >= columnCount) {
1321 return columnCount - 1;
1322 }
1323 return col;
1324 }
1325 return 0;
1326 }
1327
1328 /**
1329 * Returns the row that the model index <code>index</code> will be
1330 * displayed in..
1331 */
1332 private int convertModelToRow(int index) {
1333 int size = list.getModel().getSize();
1334
1335 if ((index < 0) || (index >= size)) {
1336 return -1;
1337 }
1338
1339 if (layoutOrientation != JList.VERTICAL && columnCount > 1 &&
1340 rowsPerColumn > 0) {
1341 if (layoutOrientation == JList.VERTICAL_WRAP) {
1342 return index % rowsPerColumn;
1343 }
1344 return index / columnCount;
1345 }
1346 return index;
1347 }
1348
1349 /**
1350 * Returns the column that the model index <code>index</code> will be
1351 * displayed in.
1352 */
1353 private int convertModelToColumn(int index) {
1354 int size = list.getModel().getSize();
1355
1356 if ((index < 0) || (index >= size)) {
1357 return -1;
1358 }
1359
1360 if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 &&
1361 columnCount > 1) {
1362 if (layoutOrientation == JList.VERTICAL_WRAP) {
1363 return index / rowsPerColumn;
1364 }
1365 return index % columnCount;
1366 }
1367 return 0;
1368 }
1369
1370 /**
1445 }
1446 if (cellHeights == null) {
1447 cellHeights = new int[dataModelSize];
1448 }
1449 for(int index = 0; index < dataModelSize; index++) {
1450 cellHeights[index] = 0;
1451 }
1452 }
1453 }
1454
1455 columnCount = 1;
1456 if (layoutOrientation != JList.VERTICAL) {
1457 updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight);
1458 }
1459 }
1460
1461 /**
1462 * Invoked when the list is layed out horizontally to determine how
1463 * many columns to create.
1464 * <p>
1465 * This updates the <code>rowsPerColumn, </code><code>columnCount</code>,
1466 * <code>preferredHeight</code> and potentially <code>cellHeight</code>
1467 * instance variables.
1468 */
1469 private void updateHorizontalLayoutState(int fixedCellWidth,
1470 int fixedCellHeight) {
1471 int visRows = list.getVisibleRowCount();
1472 int dataModelSize = list.getModel().getSize();
1473 Insets insets = list.getInsets();
1474
1475 listHeight = list.getHeight();
1476 listWidth = list.getWidth();
1477
1478 if (dataModelSize == 0) {
1479 rowsPerColumn = columnCount = 0;
1480 preferredHeight = insets.top + insets.bottom;
1481 return;
1482 }
1483
1484 int height;
1485
1486 if (fixedCellHeight != -1) {
1542
1543 private Handler getHandler() {
1544 if (handler == null) {
1545 handler = new Handler();
1546 }
1547 return handler;
1548 }
1549
1550 /**
1551 * Mouse input, and focus handling for JList. An instance of this
1552 * class is added to the appropriate java.awt.Component lists
1553 * at installUI() time. Note keyboard input is handled with JComponent
1554 * KeyboardActions, see installKeyboardActions().
1555 * <p>
1556 * <strong>Warning:</strong>
1557 * Serialized objects of this class will not be compatible with
1558 * future Swing releases. The current serialization support is
1559 * appropriate for short term storage or RMI between applications running
1560 * the same version of Swing. As of 1.4, support for long term storage
1561 * of all JavaBeans™
1562 * has been added to the <code>java.beans</code> package.
1563 * Please see {@link java.beans.XMLEncoder}.
1564 *
1565 * @see #createMouseInputListener
1566 * @see #installKeyboardActions
1567 * @see #installUI
1568 */
1569 @SuppressWarnings("serial") // Same-version serialization only
1570 public class MouseInputHandler implements MouseInputListener
1571 {
1572 public void mouseClicked(MouseEvent e) {
1573 getHandler().mouseClicked(e);
1574 }
1575
1576 public void mouseEntered(MouseEvent e) {
1577 getHandler().mouseEntered(e);
1578 }
1579
1580 public void mouseExited(MouseEvent e) {
1581 getHandler().mouseExited(e);
1582 }
1656 /**
1657 * Returns an instance of {@code FocusListener}.
1658 *
1659 * @return an instance of {@code FocusListener}
1660 */
1661 protected FocusListener createFocusListener() {
1662 return getHandler();
1663 }
1664
1665 /**
1666 * The ListSelectionListener that's added to the JLists selection
1667 * model at installUI time, and whenever the JList.selectionModel property
1668 * changes. When the selection changes we repaint the affected rows.
1669 * <p>
1670 * <strong>Warning:</strong>
1671 * Serialized objects of this class will not be compatible with
1672 * future Swing releases. The current serialization support is
1673 * appropriate for short term storage or RMI between applications running
1674 * the same version of Swing. As of 1.4, support for long term storage
1675 * of all JavaBeans™
1676 * has been added to the <code>java.beans</code> package.
1677 * Please see {@link java.beans.XMLEncoder}.
1678 *
1679 * @see #createListSelectionListener
1680 * @see #getCellBounds
1681 * @see #installUI
1682 */
1683 @SuppressWarnings("serial") // Same-version serialization only
1684 public class ListSelectionHandler implements ListSelectionListener
1685 {
1686 public void valueChanged(ListSelectionEvent e)
1687 {
1688 getHandler().valueChanged(e);
1689 }
1690 }
1691
1692
1693 /**
1694 * Creates an instance of {@code ListSelectionHandler} that's added to
1695 * the {@code JLists} by selectionModel as needed. Subclasses can override
1696 * this method to return a custom {@code ListSelectionListener}, e.g.
1716 return getHandler();
1717 }
1718
1719
1720 private void redrawList() {
1721 list.revalidate();
1722 list.repaint();
1723 }
1724
1725
1726 /**
1727 * The {@code ListDataListener} that's added to the {@code JLists} model at
1728 * {@code installUI time}, and whenever the JList.model property changes.
1729 * <p>
1730 * <strong>Warning:</strong>
1731 * Serialized objects of this class will not be compatible with
1732 * future Swing releases. The current serialization support is
1733 * appropriate for short term storage or RMI between applications running
1734 * the same version of Swing. As of 1.4, support for long term storage
1735 * of all JavaBeans™
1736 * has been added to the <code>java.beans</code> package.
1737 * Please see {@link java.beans.XMLEncoder}.
1738 *
1739 * @see JList#getModel
1740 * @see #maybeUpdateLayoutState
1741 * @see #createListDataListener
1742 * @see #installUI
1743 */
1744 @SuppressWarnings("serial") // Same-version serialization only
1745 public class ListDataHandler implements ListDataListener
1746 {
1747 public void intervalAdded(ListDataEvent e) {
1748 getHandler().intervalAdded(e);
1749 }
1750
1751
1752 public void intervalRemoved(ListDataEvent e)
1753 {
1754 getHandler().intervalRemoved(e);
1755 }
1756
1785 * @see #installUI
1786 */
1787 protected ListDataListener createListDataListener() {
1788 return getHandler();
1789 }
1790
1791
1792 /**
1793 * The PropertyChangeListener that's added to the JList at
1794 * installUI time. When the value of a JList property that
1795 * affects layout changes, we set a bit in updateLayoutStateNeeded.
1796 * If the JLists model changes we additionally remove our listeners
1797 * from the old model. Likewise for the JList selectionModel.
1798 * <p>
1799 * <strong>Warning:</strong>
1800 * Serialized objects of this class will not be compatible with
1801 * future Swing releases. The current serialization support is
1802 * appropriate for short term storage or RMI between applications running
1803 * the same version of Swing. As of 1.4, support for long term storage
1804 * of all JavaBeans™
1805 * has been added to the <code>java.beans</code> package.
1806 * Please see {@link java.beans.XMLEncoder}.
1807 *
1808 * @see #maybeUpdateLayoutState
1809 * @see #createPropertyChangeListener
1810 * @see #installUI
1811 */
1812 @SuppressWarnings("serial") // Same-version serialization only
1813 public class PropertyChangeHandler implements PropertyChangeListener
1814 {
1815 public void propertyChange(PropertyChangeEvent e)
1816 {
1817 getHandler().propertyChange(e);
1818 }
1819 }
1820
1821
1822 /**
1823 * Creates an instance of {@code PropertyChangeHandler} that's added to
1824 * the {@code JList} by {@code installUI()}. Subclasses can override this method
1825 * to return a custom {@code PropertyChangeListener}, e.g.
2430 }
2431 }
2432
2433
2434 private class Handler implements FocusListener, KeyListener,
2435 ListDataListener, ListSelectionListener,
2436 MouseInputListener, PropertyChangeListener,
2437 BeforeDrag {
2438 //
2439 // KeyListener
2440 //
2441 private String prefix = "";
2442 private String typedString = "";
2443 private long lastTime = 0L;
2444
2445 /**
2446 * Invoked when a key has been typed.
2447 *
2448 * Moves the keyboard focus to the first element whose prefix matches the
2449 * sequence of alphanumeric keys pressed by the user with delay less
2450 * than value of <code>timeFactor</code> property (or 1000 milliseconds
2451 * if it is not defined). Subsequent same key presses move the keyboard
2452 * focus to the next object that starts with the same letter until another
2453 * key is pressed, then it is treated as the prefix with appropriate number
2454 * of the same letters followed by first typed another letter.
2455 */
2456 public void keyTyped(KeyEvent e) {
2457 JList<?> src = (JList)e.getSource();
2458 ListModel<?> model = src.getModel();
2459
2460 if (model.getSize() == 0 || e.isAltDown() ||
2461 BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
2462 isNavigationKey(e)) {
2463 // Nothing to select
2464 return;
2465 }
2466 boolean startingFromSelection = true;
2467
2468 char c = e.getKeyChar();
2469
2470 long time = e.getWhen();
|
603 * of the list, or the maximum cell height, whichever is
604 * bigger. The preferred width is than the maximum cell width *
605 * number of columns needed. Where the number of columns needs is
606 * list.height / max cell height. Max cell height is either the fixed
607 * cell height, or is determined by iterating through all the cells
608 * to find the maximum height from the ListCellRenderer.
609 * <tr>
610 * <td>JList.HORIZONTAL_WRAP
611 * <td>If the visible row count is greater than zero, the preferredHeight
612 * is the maximum cell height * adjustedRowCount. Where
613 * visibleRowCount is used to determine the number of columns.
614 * Because this lays out horizontally the number of rows is
615 * then determined from the column count. For example, lets say
616 * you have a model with 10 items and the visible row count is 8.
617 * The number of columns needed to display this is 2, but you no
618 * longer need 8 rows to display this, you only need 5, thus
619 * the adjustedRowCount is 5.
620 * <p>If the visible row
621 * count is <= 0, the preferred height is dictated by the
622 * number of columns, which will be as many as can fit in the width
623 * of the {@code JList} (width / max cell width), with at
624 * least one column. The preferred height then becomes the
625 * model size / number of columns * maximum cell height.
626 * Max cell height is either the fixed
627 * cell height, or is determined by iterating through all the cells
628 * to find the maximum height from the ListCellRenderer.
629 * </table>
630 * The above specifies the raw preferred width and height. The resulting
631 * preferred width is the above width + insets.left + insets.right and
632 * the resulting preferred height is the above height + insets.top +
633 * insets.bottom. Where the {@code Insets} are determined from
634 * {@code list.getInsets()}.
635 *
636 * @param c The JList component.
637 * @return The total size of the list.
638 */
639 public Dimension getPreferredSize(JComponent c) {
640 maybeUpdateLayoutState();
641
642 int lastRow = list.getModel().getSize() - 1;
643 if (lastRow < 0) {
644 return new Dimension(0, 0);
645 }
646
647 Insets insets = list.getInsets();
648 int width = cellWidth * columnCount + insets.left + insets.right;
649 int height;
650
651 if (layoutOrientation != JList.VERTICAL) {
652 height = preferredHeight;
653 }
654 else {
680 }
681
682
683 /**
684 * Selected the previous row and force it to be visible.
685 *
686 * @see JList#ensureIndexIsVisible
687 */
688 protected void selectNextIndex()
689 {
690 int s = list.getSelectedIndex();
691 if((s + 1) < list.getModel().getSize()) {
692 s += 1;
693 list.setSelectedIndex(s);
694 list.ensureIndexIsVisible(s);
695 }
696 }
697
698
699 /**
700 * Registers the keyboard bindings on the {@code JList} that the
701 * {@code BasicListUI} is associated with. This method is called at
702 * installUI() time.
703 *
704 * @see #installUI
705 */
706 protected void installKeyboardActions() {
707 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
708
709 SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
710 inputMap);
711
712 LazyActionMap.installLazyActionMap(list, BasicListUI.class,
713 "List.actionMap");
714 }
715
716 InputMap getInputMap(int condition) {
717 if (condition == JComponent.WHEN_FOCUSED) {
718 InputMap keyMap = (InputMap)DefaultLookup.get(
719 list, this, "List.focusInputMap");
720 InputMap rtlKeyMap;
721
722 if (isLeftToRight ||
723 ((rtlKeyMap = (InputMap)DefaultLookup.get(list, this,
724 "List.focusInputMap.RightToLeft")) == null)) {
725 return keyMap;
726 } else {
727 rtlKeyMap.setParent(keyMap);
728 return rtlKeyMap;
729 }
730 }
731 return null;
732 }
733
734 /**
735 * Unregisters keyboard actions installed from
736 * {@code installKeyboardActions}.
737 * This method is called at uninstallUI() time - subclassess should
738 * ensure that all of the keyboard actions registered at installUI
739 * time are removed here.
740 *
741 * @see #installUI
742 */
743 protected void uninstallKeyboardActions() {
744 SwingUtilities.replaceUIActionMap(list, null);
745 SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
746 }
747
748
749 /**
750 * Creates and installs the listeners for the JList, its model, and its
751 * selectionModel. This method is called at installUI() time.
752 *
753 * @see #installUI
754 * @see #uninstallListeners
755 */
756 protected void installListeners()
903 }
904 if (list.getBackground() instanceof UIResource) {
905 list.setBackground(null);
906 }
907 if (list.getSelectionBackground() instanceof UIResource) {
908 list.setSelectionBackground(null);
909 }
910 if (list.getSelectionForeground() instanceof UIResource) {
911 list.setSelectionForeground(null);
912 }
913 if (list.getCellRenderer() instanceof UIResource) {
914 list.setCellRenderer(null);
915 }
916 if (list.getTransferHandler() instanceof UIResource) {
917 list.setTransferHandler(null);
918 }
919 }
920
921
922 /**
923 * Initializes {@code this.list} by calling {@code installDefaults()},
924 * {@code installListeners()}, and {@code installKeyboardActions()}
925 * in order.
926 *
927 * @see #installDefaults
928 * @see #installListeners
929 * @see #installKeyboardActions
930 */
931 public void installUI(JComponent c)
932 {
933 @SuppressWarnings("unchecked")
934 JList<Object> tmp = (JList)c;
935 list = tmp;
936
937 layoutOrientation = list.getLayoutOrientation();
938
939 rendererPane = new CellRendererPane();
940 list.add(rendererPane);
941
942 columnCount = 1;
943
944 updateLayoutStateNeeded = modelChanged;
945 isLeftToRight = list.getComponentOrientation().isLeftToRight();
946
947 installDefaults();
948 installListeners();
949 installKeyboardActions();
950 }
951
952
953 /**
954 * Uninitializes {@code this.list} by calling {@code uninstallListeners()},
955 * {@code uninstallKeyboardActions()}, and {@code uninstallDefaults()}
956 * in order. Sets this.list to null.
957 *
958 * @see #uninstallListeners
959 * @see #uninstallKeyboardActions
960 * @see #uninstallDefaults
961 */
962 public void uninstallUI(JComponent c)
963 {
964 uninstallListeners();
965 uninstallDefaults();
966 uninstallKeyboardActions();
967
968 cellWidth = cellHeight = -1;
969 cellHeights = null;
970
971 listWidth = listHeight = -1;
972
973 list.remove(rendererPane);
974 rendererPane = null;
975 list = null;
1040 int minRow = convertModelToRow(minIndex);
1041 int maxRow = convertModelToRow(maxIndex);
1042
1043 if (minRow != maxRow) {
1044 minBounds.x = 0;
1045 minBounds.width = list.getWidth();
1046 }
1047 }
1048 else if (minBounds.x != maxBounds.x) {
1049 // Different columns
1050 minBounds.y = 0;
1051 minBounds.height = list.getHeight();
1052 }
1053 minBounds.add(maxBounds);
1054 }
1055 return minBounds;
1056 }
1057
1058 /**
1059 * Gets the bounds of the specified model index, returning the resulting
1060 * bounds, or null if {@code index} is not valid.
1061 */
1062 private Rectangle getCellBounds(JList<?> list, int index) {
1063 maybeUpdateLayoutState();
1064
1065 int row = convertModelToRow(index);
1066 int column = convertModelToColumn(index);
1067
1068 if (row == -1 || column == -1) {
1069 return null;
1070 }
1071
1072 Insets insets = list.getInsets();
1073 int x;
1074 int w = cellWidth;
1075 int y = insets.top;
1076 int h;
1077 switch (layoutOrientation) {
1078 case JList.VERTICAL_WRAP:
1079 case JList.HORIZONTAL_WRAP:
1080 if (isLeftToRight) {
1266 }
1267 if (layoutOrientation == JList.VERTICAL_WRAP) {
1268 if (column < (columnCount - 1)) {
1269 return rowsPerColumn;
1270 }
1271 return list.getModel().getSize() - (columnCount - 1) *
1272 rowsPerColumn;
1273 }
1274 // JList.HORIZONTAL_WRAP
1275 int diff = columnCount - (columnCount * rowsPerColumn -
1276 list.getModel().getSize());
1277
1278 if (column >= diff) {
1279 return Math.max(0, rowsPerColumn - 1);
1280 }
1281 return rowsPerColumn;
1282 }
1283
1284 /**
1285 * Returns the model index for the specified display location.
1286 * If {@code column}x{@code row} is beyond the length of the
1287 * model, this will return the model size - 1.
1288 */
1289 private int getModelIndex(int column, int row) {
1290 switch (layoutOrientation) {
1291 case JList.VERTICAL_WRAP:
1292 return Math.min(list.getModel().getSize() - 1, rowsPerColumn *
1293 column + Math.min(row, rowsPerColumn-1));
1294 case JList.HORIZONTAL_WRAP:
1295 return Math.min(list.getModel().getSize() - 1, row * columnCount +
1296 column);
1297 default:
1298 return row;
1299 }
1300 }
1301
1302 /**
1303 * Returns the closest column to the passed in location.
1304 */
1305 private int convertLocationToColumn(int x, int y) {
1306 if (cellWidth > 0) {
1309 }
1310 Insets insets = list.getInsets();
1311 int col;
1312 if (isLeftToRight) {
1313 col = (x - insets.left) / cellWidth;
1314 } else {
1315 col = (list.getWidth() - x - insets.right - 1) / cellWidth;
1316 }
1317 if (col < 0) {
1318 return 0;
1319 }
1320 else if (col >= columnCount) {
1321 return columnCount - 1;
1322 }
1323 return col;
1324 }
1325 return 0;
1326 }
1327
1328 /**
1329 * Returns the row that the model index {@code index} will be
1330 * displayed in..
1331 */
1332 private int convertModelToRow(int index) {
1333 int size = list.getModel().getSize();
1334
1335 if ((index < 0) || (index >= size)) {
1336 return -1;
1337 }
1338
1339 if (layoutOrientation != JList.VERTICAL && columnCount > 1 &&
1340 rowsPerColumn > 0) {
1341 if (layoutOrientation == JList.VERTICAL_WRAP) {
1342 return index % rowsPerColumn;
1343 }
1344 return index / columnCount;
1345 }
1346 return index;
1347 }
1348
1349 /**
1350 * Returns the column that the model index {@code index} will be
1351 * displayed in.
1352 */
1353 private int convertModelToColumn(int index) {
1354 int size = list.getModel().getSize();
1355
1356 if ((index < 0) || (index >= size)) {
1357 return -1;
1358 }
1359
1360 if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 &&
1361 columnCount > 1) {
1362 if (layoutOrientation == JList.VERTICAL_WRAP) {
1363 return index / rowsPerColumn;
1364 }
1365 return index % columnCount;
1366 }
1367 return 0;
1368 }
1369
1370 /**
1445 }
1446 if (cellHeights == null) {
1447 cellHeights = new int[dataModelSize];
1448 }
1449 for(int index = 0; index < dataModelSize; index++) {
1450 cellHeights[index] = 0;
1451 }
1452 }
1453 }
1454
1455 columnCount = 1;
1456 if (layoutOrientation != JList.VERTICAL) {
1457 updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight);
1458 }
1459 }
1460
1461 /**
1462 * Invoked when the list is layed out horizontally to determine how
1463 * many columns to create.
1464 * <p>
1465 * This updates the {@code rowsPerColumn,}{@code columnCount},
1466 * {@code preferredHeight} and potentially {@code cellHeight}
1467 * instance variables.
1468 */
1469 private void updateHorizontalLayoutState(int fixedCellWidth,
1470 int fixedCellHeight) {
1471 int visRows = list.getVisibleRowCount();
1472 int dataModelSize = list.getModel().getSize();
1473 Insets insets = list.getInsets();
1474
1475 listHeight = list.getHeight();
1476 listWidth = list.getWidth();
1477
1478 if (dataModelSize == 0) {
1479 rowsPerColumn = columnCount = 0;
1480 preferredHeight = insets.top + insets.bottom;
1481 return;
1482 }
1483
1484 int height;
1485
1486 if (fixedCellHeight != -1) {
1542
1543 private Handler getHandler() {
1544 if (handler == null) {
1545 handler = new Handler();
1546 }
1547 return handler;
1548 }
1549
1550 /**
1551 * Mouse input, and focus handling for JList. An instance of this
1552 * class is added to the appropriate java.awt.Component lists
1553 * at installUI() time. Note keyboard input is handled with JComponent
1554 * KeyboardActions, see installKeyboardActions().
1555 * <p>
1556 * <strong>Warning:</strong>
1557 * Serialized objects of this class will not be compatible with
1558 * future Swing releases. The current serialization support is
1559 * appropriate for short term storage or RMI between applications running
1560 * the same version of Swing. As of 1.4, support for long term storage
1561 * of all JavaBeans™
1562 * has been added to the {@code java.beans} package.
1563 * Please see {@link java.beans.XMLEncoder}.
1564 *
1565 * @see #createMouseInputListener
1566 * @see #installKeyboardActions
1567 * @see #installUI
1568 */
1569 @SuppressWarnings("serial") // Same-version serialization only
1570 public class MouseInputHandler implements MouseInputListener
1571 {
1572 public void mouseClicked(MouseEvent e) {
1573 getHandler().mouseClicked(e);
1574 }
1575
1576 public void mouseEntered(MouseEvent e) {
1577 getHandler().mouseEntered(e);
1578 }
1579
1580 public void mouseExited(MouseEvent e) {
1581 getHandler().mouseExited(e);
1582 }
1656 /**
1657 * Returns an instance of {@code FocusListener}.
1658 *
1659 * @return an instance of {@code FocusListener}
1660 */
1661 protected FocusListener createFocusListener() {
1662 return getHandler();
1663 }
1664
1665 /**
1666 * The ListSelectionListener that's added to the JLists selection
1667 * model at installUI time, and whenever the JList.selectionModel property
1668 * changes. When the selection changes we repaint the affected rows.
1669 * <p>
1670 * <strong>Warning:</strong>
1671 * Serialized objects of this class will not be compatible with
1672 * future Swing releases. The current serialization support is
1673 * appropriate for short term storage or RMI between applications running
1674 * the same version of Swing. As of 1.4, support for long term storage
1675 * of all JavaBeans™
1676 * has been added to the {@code java.beans} package.
1677 * Please see {@link java.beans.XMLEncoder}.
1678 *
1679 * @see #createListSelectionListener
1680 * @see #getCellBounds
1681 * @see #installUI
1682 */
1683 @SuppressWarnings("serial") // Same-version serialization only
1684 public class ListSelectionHandler implements ListSelectionListener
1685 {
1686 public void valueChanged(ListSelectionEvent e)
1687 {
1688 getHandler().valueChanged(e);
1689 }
1690 }
1691
1692
1693 /**
1694 * Creates an instance of {@code ListSelectionHandler} that's added to
1695 * the {@code JLists} by selectionModel as needed. Subclasses can override
1696 * this method to return a custom {@code ListSelectionListener}, e.g.
1716 return getHandler();
1717 }
1718
1719
1720 private void redrawList() {
1721 list.revalidate();
1722 list.repaint();
1723 }
1724
1725
1726 /**
1727 * The {@code ListDataListener} that's added to the {@code JLists} model at
1728 * {@code installUI time}, and whenever the JList.model property changes.
1729 * <p>
1730 * <strong>Warning:</strong>
1731 * Serialized objects of this class will not be compatible with
1732 * future Swing releases. The current serialization support is
1733 * appropriate for short term storage or RMI between applications running
1734 * the same version of Swing. As of 1.4, support for long term storage
1735 * of all JavaBeans™
1736 * has been added to the {@code java.beans} package.
1737 * Please see {@link java.beans.XMLEncoder}.
1738 *
1739 * @see JList#getModel
1740 * @see #maybeUpdateLayoutState
1741 * @see #createListDataListener
1742 * @see #installUI
1743 */
1744 @SuppressWarnings("serial") // Same-version serialization only
1745 public class ListDataHandler implements ListDataListener
1746 {
1747 public void intervalAdded(ListDataEvent e) {
1748 getHandler().intervalAdded(e);
1749 }
1750
1751
1752 public void intervalRemoved(ListDataEvent e)
1753 {
1754 getHandler().intervalRemoved(e);
1755 }
1756
1785 * @see #installUI
1786 */
1787 protected ListDataListener createListDataListener() {
1788 return getHandler();
1789 }
1790
1791
1792 /**
1793 * The PropertyChangeListener that's added to the JList at
1794 * installUI time. When the value of a JList property that
1795 * affects layout changes, we set a bit in updateLayoutStateNeeded.
1796 * If the JLists model changes we additionally remove our listeners
1797 * from the old model. Likewise for the JList selectionModel.
1798 * <p>
1799 * <strong>Warning:</strong>
1800 * Serialized objects of this class will not be compatible with
1801 * future Swing releases. The current serialization support is
1802 * appropriate for short term storage or RMI between applications running
1803 * the same version of Swing. As of 1.4, support for long term storage
1804 * of all JavaBeans™
1805 * has been added to the {@code java.beans} package.
1806 * Please see {@link java.beans.XMLEncoder}.
1807 *
1808 * @see #maybeUpdateLayoutState
1809 * @see #createPropertyChangeListener
1810 * @see #installUI
1811 */
1812 @SuppressWarnings("serial") // Same-version serialization only
1813 public class PropertyChangeHandler implements PropertyChangeListener
1814 {
1815 public void propertyChange(PropertyChangeEvent e)
1816 {
1817 getHandler().propertyChange(e);
1818 }
1819 }
1820
1821
1822 /**
1823 * Creates an instance of {@code PropertyChangeHandler} that's added to
1824 * the {@code JList} by {@code installUI()}. Subclasses can override this method
1825 * to return a custom {@code PropertyChangeListener}, e.g.
2430 }
2431 }
2432
2433
2434 private class Handler implements FocusListener, KeyListener,
2435 ListDataListener, ListSelectionListener,
2436 MouseInputListener, PropertyChangeListener,
2437 BeforeDrag {
2438 //
2439 // KeyListener
2440 //
2441 private String prefix = "";
2442 private String typedString = "";
2443 private long lastTime = 0L;
2444
2445 /**
2446 * Invoked when a key has been typed.
2447 *
2448 * Moves the keyboard focus to the first element whose prefix matches the
2449 * sequence of alphanumeric keys pressed by the user with delay less
2450 * than value of {@code timeFactor} property (or 1000 milliseconds
2451 * if it is not defined). Subsequent same key presses move the keyboard
2452 * focus to the next object that starts with the same letter until another
2453 * key is pressed, then it is treated as the prefix with appropriate number
2454 * of the same letters followed by first typed another letter.
2455 */
2456 public void keyTyped(KeyEvent e) {
2457 JList<?> src = (JList)e.getSource();
2458 ListModel<?> model = src.getModel();
2459
2460 if (model.getSize() == 0 || e.isAltDown() ||
2461 BasicGraphicsUtils.isMenuShortcutKeyDown(e) ||
2462 isNavigationKey(e)) {
2463 // Nothing to select
2464 return;
2465 }
2466 boolean startingFromSelection = true;
2467
2468 char c = e.getKeyChar();
2469
2470 long time = e.getWhen();
|