1. /*
  2. * @(#)BasicListUI.java 1.51 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import javax.swing.*;
  9. import javax.swing.event.*;
  10. import javax.swing.plaf.*;
  11. import java.awt.*;
  12. import java.awt.event.*;
  13. import java.beans.PropertyChangeListener;
  14. import java.beans.PropertyChangeEvent;
  15. /**
  16. * A Windows L&F implementation of ListUI.
  17. * <p>
  18. *
  19. * @version 1.51 11/29/01
  20. * @author Hans Muller
  21. * @author Philip Milne
  22. */
  23. public class BasicListUI extends ListUI
  24. {
  25. protected JList list = null;
  26. protected CellRendererPane rendererPane;
  27. // Listeners that this UI attaches to the JList
  28. protected FocusListener focusListener;
  29. protected MouseInputListener mouseInputListener;
  30. protected ListSelectionListener listSelectionListener;
  31. protected ListDataListener listDataListener;
  32. protected PropertyChangeListener propertyChangeListener;
  33. // PENDING(hmuller) need a doc pointer to #getRowHeight, #maybeUpdateLayout
  34. protected int[] cellHeights = null;
  35. protected int cellHeight = -1;
  36. protected int cellWidth = -1;
  37. protected int updateLayoutStateNeeded = modelChanged;
  38. /* The bits below define JList property changes that affect layout.
  39. * When one of these properties changes we set a bit in
  40. * updateLayoutStateNeeded. The change is dealt with lazily, see
  41. * maybeUpdateLayout. Changes to the JLists model, e.g. the
  42. * models length changed, are handled similarly, see DataListener.
  43. */
  44. protected final static int modelChanged = 1 << 0;
  45. protected final static int selectionModelChanged = 1 << 1;
  46. protected final static int fontChanged = 1 << 2;
  47. protected final static int fixedCellWidthChanged = 1 << 3;
  48. protected final static int fixedCellHeightChanged = 1 << 4;
  49. protected final static int prototypeCellValueChanged = 1 << 5;
  50. protected final static int cellRendererChanged = 1 << 6;
  51. /**
  52. * Paint one List cell: compute the relevant state, get the "rubber stamp"
  53. * cell renderer component, and then use the CellRendererPane to paint it.
  54. * Subclasses may want to override this method rather than paint().
  55. *
  56. * @see #paint
  57. */
  58. protected void paintCell(
  59. Graphics g,
  60. int row,
  61. Rectangle rowBounds,
  62. ListCellRenderer cellRenderer,
  63. ListModel dataModel,
  64. ListSelectionModel selModel,
  65. int leadIndex)
  66. {
  67. Object value = dataModel.getElementAt(row);
  68. boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
  69. boolean isSelected = selModel.isSelectedIndex(row);
  70. Component rendererComponent =
  71. cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
  72. int cx = rowBounds.x;
  73. int cy = rowBounds.y;
  74. int cw = rowBounds.width;
  75. int ch = rowBounds.height;
  76. rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
  77. }
  78. /**
  79. * Paint the rows that intersect the Graphics objects clipRect. This
  80. * method calls paintCell as necessary. Subclasses
  81. * may want to override these methods.
  82. *
  83. * @see #paintCell
  84. */
  85. public void paint(Graphics g, JComponent c)
  86. {
  87. maybeUpdateLayoutState();
  88. ListCellRenderer renderer = list.getCellRenderer();
  89. ListModel dataModel = list.getModel();
  90. ListSelectionModel selModel = list.getSelectionModel();
  91. if ((renderer == null) || (dataModel.getSize() == 0)) {
  92. return;
  93. }
  94. /* Compute the area we're going to paint in terms of the affected
  95. * rows (firstPaintRow, lastPaintRow), and the clip bounds.
  96. */
  97. Rectangle paintBounds = g.getClipBounds();
  98. int firstPaintRow = convertYToRow(paintBounds.y);
  99. int lastPaintRow = convertYToRow((paintBounds.y + paintBounds.height) - 1);
  100. if (firstPaintRow == -1) {
  101. firstPaintRow = 0;
  102. }
  103. if (lastPaintRow == -1) {
  104. lastPaintRow = dataModel.getSize() - 1;
  105. }
  106. Rectangle rowBounds = getCellBounds(list, firstPaintRow, firstPaintRow);
  107. if (rowBounds == null) {
  108. return;
  109. }
  110. int leadIndex = list.getLeadSelectionIndex();
  111. for(int row = firstPaintRow; row <= lastPaintRow; row++) {
  112. rowBounds.height = getRowHeight(row);
  113. /* Set the clip rect to be the intersection of rowBounds
  114. * and paintBounds and then paint the cell.
  115. */
  116. g.setClip(rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height);
  117. g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width, paintBounds.height);
  118. paintCell(g, row, rowBounds, renderer, dataModel, selModel, leadIndex);
  119. rowBounds.y += rowBounds.height;
  120. }
  121. }
  122. /**
  123. * The preferredSize of a list is total height of the rows
  124. * and the maximum width of the cells. If JList.fixedCellHeight
  125. * is specified then the total height of the rows is just
  126. * (cellVerticalMargins + fixedCellHeight) * model.getSize() where
  127. * rowVerticalMargins is the space we allocate for drawing
  128. * the yellow focus outline. Similarly if JListfixedCellWidth is
  129. * specified then we just use that plus the horizontal margins.
  130. *
  131. * @param c The JList component.
  132. * @return The total size of the list.
  133. */
  134. public Dimension getPreferredSize(JComponent c) {
  135. maybeUpdateLayoutState();
  136. int lastRow = list.getModel().getSize() - 1;
  137. if (lastRow < 0) {
  138. return new Dimension(0, 0);
  139. }
  140. Insets insets = list.getInsets();
  141. int width = cellWidth + insets.left + insets.right;
  142. int height = convertRowToY(lastRow) + getRowHeight(lastRow) + insets.bottom;
  143. return new Dimension(width, height);
  144. }
  145. /**
  146. * @returns The preferred size.
  147. * @see #getPreferredSize
  148. */
  149. public Dimension getMinimumSize(JComponent c) {
  150. return getPreferredSize(c);
  151. }
  152. /**
  153. * @returns The preferred size.
  154. * @see #getPreferredSize
  155. */
  156. public Dimension getMaximumSize(JComponent c) {
  157. return getPreferredSize(c);
  158. }
  159. /**
  160. * Selected the previous row and force it to be visible.
  161. * Called by the KeyEvent.VK_UP keyboard action.
  162. *
  163. * @see #installKeyboardActions
  164. * @see JList#ensureIndexIsVisible
  165. */
  166. protected void selectPreviousIndex() {
  167. int s = list.getSelectedIndex();
  168. if(s > 0) {
  169. s -= 1;
  170. list.setSelectedIndex(s);
  171. list.ensureIndexIsVisible(s);
  172. }
  173. }
  174. /**
  175. * Selected the previous row and force it to be visible.
  176. * Called by the KeyEvent.VK_DOWN keyboard action.
  177. *
  178. * @see #installKeyboardActions
  179. * @see JList#ensureIndexIsVisible
  180. */
  181. protected void selectNextIndex()
  182. {
  183. int s = list.getSelectedIndex();
  184. if((s + 1) < list.getModel().getSize()) {
  185. s += 1;
  186. list.setSelectedIndex(s);
  187. list.ensureIndexIsVisible(s);
  188. }
  189. }
  190. /**
  191. * Register keyboard actions for the up and down arrow keys. The
  192. * actions just call out to protected methods, subclasses that
  193. * want to override or extend keyboard behavior should consider
  194. * just overriding those methods. This method is called at
  195. * installUI() time.
  196. *
  197. * @see #selectPreviousIndex
  198. * @see #selectNextIndex
  199. * @see #installUI
  200. */
  201. protected void installKeyboardActions() {
  202. // up
  203. list.registerKeyboardAction(new IncrementLeadSelectionAction
  204. ("SelectPreviousRow", CHANGE_SELECTION, -1),
  205. KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
  206. JComponent.WHEN_FOCUSED);
  207. list.registerKeyboardAction(new IncrementLeadSelectionAction
  208. ("SelectPreviousRow", CHANGE_SELECTION, -1),
  209. KeyStroke.getKeyStroke("KP_UP"),
  210. JComponent.WHEN_FOCUSED);
  211. list.registerKeyboardAction(new IncrementLeadSelectionAction
  212. ("ExtendSelectPreviousRow", EXTEND_SELECTION, -1),
  213. KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.
  214. SHIFT_MASK), JComponent.WHEN_FOCUSED);
  215. list.registerKeyboardAction(new IncrementLeadSelectionAction
  216. ("ExtendSelectPreviousRow", EXTEND_SELECTION, -1),
  217. KeyStroke.getKeyStroke("shift KP_UP"),
  218. JComponent.WHEN_FOCUSED);
  219. // down
  220. list.registerKeyboardAction(new IncrementLeadSelectionAction
  221. ("SelectNextRow", CHANGE_SELECTION, 1),
  222. KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
  223. JComponent.WHEN_FOCUSED);
  224. list.registerKeyboardAction(new IncrementLeadSelectionAction
  225. ("SelectNextRow", CHANGE_SELECTION, 1),
  226. KeyStroke.getKeyStroke("KP_DOWN"),
  227. JComponent.WHEN_FOCUSED);
  228. list.registerKeyboardAction(new IncrementLeadSelectionAction
  229. ("ExtendSelectPreviousRow", EXTEND_SELECTION, 1),
  230. KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.
  231. SHIFT_MASK), JComponent.WHEN_FOCUSED);
  232. list.registerKeyboardAction(new IncrementLeadSelectionAction
  233. ("ExtendSelectPreviousRow", EXTEND_SELECTION, 1),
  234. KeyStroke.getKeyStroke("shift KP_DOWN"),
  235. JComponent.WHEN_FOCUSED);
  236. // home
  237. list.registerKeyboardAction(new HomeAction
  238. ("SelectHome", CHANGE_SELECTION),
  239. KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
  240. JComponent.WHEN_FOCUSED);
  241. list.registerKeyboardAction(new HomeAction
  242. ("ExtendSelectHome", EXTEND_SELECTION),
  243. KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.
  244. SHIFT_MASK), JComponent.WHEN_FOCUSED);
  245. // end
  246. list.registerKeyboardAction(new EndAction
  247. ("SelectEnd", CHANGE_SELECTION),
  248. KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
  249. JComponent.WHEN_FOCUSED);
  250. list.registerKeyboardAction(new EndAction
  251. ("ExtendSelectEnd", EXTEND_SELECTION),
  252. KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.
  253. SHIFT_MASK), JComponent.WHEN_FOCUSED);
  254. // page up
  255. list.registerKeyboardAction(new PageUpAction
  256. ("SelectPageUp", CHANGE_SELECTION),
  257. KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
  258. JComponent.WHEN_FOCUSED);
  259. list.registerKeyboardAction(new PageUpAction
  260. ("ExtendSelectPageUp", EXTEND_SELECTION),
  261. KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, InputEvent.
  262. SHIFT_MASK), JComponent.WHEN_FOCUSED);
  263. // page down
  264. list.registerKeyboardAction(new PageDownAction
  265. ("SelectPageDown", CHANGE_SELECTION),
  266. KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
  267. JComponent.WHEN_FOCUSED);
  268. list.registerKeyboardAction(new PageDownAction
  269. ("ExtendSelectPageDown", EXTEND_SELECTION),
  270. KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,
  271. InputEvent.SHIFT_MASK), JComponent.WHEN_FOCUSED);
  272. // select all
  273. ActionListener selectAll = new SelectAllAction("SelectAll");
  274. list.registerKeyboardAction(selectAll,
  275. KeyStroke.getKeyStroke(KeyEvent.VK_A,
  276. InputEvent.CTRL_MASK),
  277. JComponent.WHEN_FOCUSED);
  278. list.registerKeyboardAction(selectAll, KeyStroke.getKeyStroke
  279. (KeyEvent.VK_SLASH, InputEvent.CTRL_MASK),
  280. JComponent.WHEN_FOCUSED);
  281. // clear selection
  282. list.registerKeyboardAction(new ClearSelectionAction("ClearSelection"),
  283. KeyStroke.getKeyStroke(KeyEvent.
  284. VK_BACK_SLASH, InputEvent.CTRL_MASK),
  285. JComponent.WHEN_FOCUSED);
  286. }
  287. /**
  288. * Unregister keyboard actions for the up and down arrow keys.
  289. * This method is called at uninstallUI() time - subclassess should
  290. * ensure that all of the keyboard actions registered at installUI
  291. * time are removed here.
  292. *
  293. * @see #selectPreviousIndex
  294. * @see #selectNextIndex
  295. * @see #installUI
  296. */
  297. protected void uninstallKeyboardActions() {
  298. // up
  299. list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP,
  300. 0));
  301. list.unregisterKeyboardAction(KeyStroke.getKeyStroke("KP_UP"));
  302. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  303. (KeyEvent.VK_UP, InputEvent.SHIFT_MASK));
  304. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  305. ("shift KP_UP"));
  306. // down
  307. list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.
  308. VK_DOWN, 0));
  309. list.unregisterKeyboardAction(KeyStroke.getKeyStroke("KP_DOWN"));
  310. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  311. (KeyEvent.VK_DOWN, InputEvent.SHIFT_MASK));
  312. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  313. ("shift KP_DOWN"));
  314. // home
  315. list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_HOME,
  316. 0));
  317. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  318. (KeyEvent.VK_HOME, InputEvent.SHIFT_MASK));
  319. // end
  320. list.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.
  321. VK_END, 0));
  322. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  323. (KeyEvent.VK_END, InputEvent.SHIFT_MASK));
  324. // page up
  325. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  326. (KeyEvent.VK_PAGE_UP, 0));
  327. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  328. (KeyEvent.VK_PAGE_UP, InputEvent.SHIFT_MASK));
  329. // page down
  330. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  331. (KeyEvent.VK_PAGE_DOWN, 0));
  332. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  333. (KeyEvent.VK_PAGE_DOWN, InputEvent.SHIFT_MASK));
  334. // select all
  335. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  336. (KeyEvent.VK_A, InputEvent.CTRL_MASK));
  337. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  338. (KeyEvent.VK_SLASH, InputEvent.CTRL_MASK));
  339. // clear selection
  340. list.unregisterKeyboardAction(KeyStroke.getKeyStroke
  341. (KeyEvent.VK_BACK_SLASH, InputEvent.CTRL_MASK));
  342. }
  343. /**
  344. * Create and install the listeners for the JList, its model, and its
  345. * selectionModel. This method is called at installUI() time.
  346. *
  347. * @see #installUI
  348. * @see #uninstallListeners
  349. */
  350. protected void installListeners()
  351. {
  352. focusListener = createFocusListener();
  353. mouseInputListener = createMouseInputListener();
  354. propertyChangeListener = createPropertyChangeListener();
  355. listSelectionListener = createListSelectionListener();
  356. listDataListener = createListDataListener();
  357. list.addFocusListener(focusListener);
  358. list.addMouseListener(mouseInputListener);
  359. list.addMouseMotionListener(mouseInputListener);
  360. list.addPropertyChangeListener(propertyChangeListener);
  361. ListModel model = list.getModel();
  362. if (model != null) {
  363. model.addListDataListener(listDataListener);
  364. }
  365. ListSelectionModel selectionModel = list.getSelectionModel();
  366. if (selectionModel != null) {
  367. selectionModel.addListSelectionListener(listSelectionListener);
  368. }
  369. }
  370. /**
  371. * Remove the listeners for the JList, its model, and its
  372. * selectionModel. All of the listener fields, are reset to
  373. * null here. This method is called at uninstallUI() time,
  374. * it should be kept in sync with installListeners.
  375. *
  376. * @see #uninstallUI
  377. * @see #installListeners
  378. */
  379. protected void uninstallListeners()
  380. {
  381. list.removeFocusListener(focusListener);
  382. list.removeMouseListener(mouseInputListener);
  383. list.removeMouseMotionListener(mouseInputListener);
  384. list.removePropertyChangeListener(propertyChangeListener);
  385. ListModel model = list.getModel();
  386. if (model != null) {
  387. model.removeListDataListener(listDataListener);
  388. }
  389. ListSelectionModel selectionModel = list.getSelectionModel();
  390. if (selectionModel != null) {
  391. selectionModel.removeListSelectionListener(listSelectionListener);
  392. }
  393. focusListener = null;
  394. mouseInputListener = null;
  395. listSelectionListener = null;
  396. listDataListener = null;
  397. propertyChangeListener = null;
  398. }
  399. /**
  400. * Initialize JList properties, e.g. font, foreground, and background,
  401. * and add the CellRendererPane. The font, foreground, and background
  402. * properties are only set if their current value is either null
  403. * or a UIResource, other properties are set if the current
  404. * value is null.
  405. *
  406. * @see #uninstallDefaults
  407. * @see #installUI
  408. * @see CellRendererPane
  409. */
  410. protected void installDefaults()
  411. {
  412. list.setLayout(null);
  413. LookAndFeel.installBorder(list, "List.border");
  414. LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");
  415. if (list.getCellRenderer() == null) {
  416. list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer")));
  417. }
  418. Color sbg = list.getSelectionBackground();
  419. if (sbg == null || sbg instanceof UIResource) {
  420. list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
  421. }
  422. Color sfg = list.getSelectionForeground();
  423. if (sfg == null || sfg instanceof UIResource) {
  424. list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
  425. }
  426. }
  427. /**
  428. * Set the JList properties that haven't been explicitly overriden to
  429. * null. A property is considered overridden if its current value
  430. * is not a UIResource.
  431. *
  432. * @see #installDefaults
  433. * @see #uninstallUI
  434. * @see CellRendererPane
  435. */
  436. protected void uninstallDefaults()
  437. {
  438. if (list.getCellRenderer() instanceof UIResource) {
  439. list.setCellRenderer(null);
  440. }
  441. }
  442. /**
  443. * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
  444. * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
  445. * in order.
  446. *
  447. * @see #installDefaults
  448. * @see #installListeners
  449. * @see #installKeyboardActions
  450. */
  451. public void installUI(JComponent c)
  452. {
  453. list = (JList)c;
  454. rendererPane = new CellRendererPane();
  455. list.add(rendererPane);
  456. installDefaults();
  457. installListeners();
  458. installKeyboardActions();
  459. }
  460. /**
  461. * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
  462. * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>
  463. * in order. Sets this.list to null.
  464. *
  465. * @see #uninstallListeners
  466. * @see #uninstallKeyboardActions
  467. * @see #uninstallDefaults
  468. */
  469. public void uninstallUI(JComponent c)
  470. {
  471. uninstallDefaults();
  472. uninstallListeners();
  473. uninstallKeyboardActions();
  474. cellWidth = cellHeight = -1;
  475. cellHeights = null;
  476. list.remove(rendererPane);
  477. rendererPane = null;
  478. list = null;
  479. }
  480. /**
  481. * Returns a new instance of BasicListUI. BasicListUI delegates are
  482. * allocated one per JList.
  483. *
  484. * @return A new ListUI implementation for the Windows look and feel.
  485. */
  486. public static ComponentUI createUI(JComponent list) {
  487. return new BasicListUI();
  488. }
  489. /**
  490. * @return The index of the cell at location, or -1.
  491. * @see ListUI#locationToIndex
  492. */
  493. public int locationToIndex(JList list, Point location) {
  494. maybeUpdateLayoutState();
  495. return convertYToRow(location.y);
  496. }
  497. /**
  498. * @return The origin of the index'th cell.
  499. * @see ListUI#indexToLocation
  500. */
  501. public Point indexToLocation(JList list, int index) {
  502. maybeUpdateLayoutState();
  503. return new Point(0, convertRowToY(index));
  504. }
  505. /**
  506. * @return The bounds of the index'th cell.
  507. * @see ListUI#getCellBounds
  508. */
  509. public Rectangle getCellBounds(JList list, int index1, int index2) {
  510. maybeUpdateLayoutState();
  511. int minIndex = Math.min(index1, index2);
  512. int maxIndex = Math.max(index1, index2);
  513. int minY = convertRowToY(minIndex);
  514. int maxY = convertRowToY(maxIndex);
  515. if ((minY == -1) || (maxY == -1)) {
  516. return null;
  517. }
  518. Insets insets = list.getInsets();
  519. int x = insets.left;
  520. int y = minY;
  521. int w = list.getWidth() - (insets.left + insets.right);
  522. int h = (maxY + getRowHeight(maxIndex)) - minY;
  523. return new Rectangle(x, y, w, h);
  524. }
  525. // PENDING(hmuller) explain the cell geometry abstraction in
  526. // the getRowHeight javadoc
  527. /**
  528. * Returns the height of the specified row based on the current layout.
  529. *
  530. * @return The specified row height or -1 if row isn't valid.
  531. * @see #convertYToRow
  532. * @see #convertRowToY
  533. * @see #updateLayoutState
  534. */
  535. protected int getRowHeight(int row)
  536. {
  537. if ((row < 0) || (row >= list.getModel().getSize())) {
  538. return -1;
  539. }
  540. return (cellHeights == null) ? cellHeight : ((row < cellHeights.length) ? cellHeights[row] : -1);
  541. }
  542. /**
  543. * Convert the JList relative coordinate to the row that contains it,
  544. * based on the current layout. If y0 doesn't fall within any row,
  545. * return -1.
  546. *
  547. * @return The row that contains y0, or -1.
  548. * @see #getRowHeight
  549. * @see #updateLayoutState
  550. */
  551. protected int convertYToRow(int y0)
  552. {
  553. int nrows = list.getModel().getSize();
  554. Insets insets = list.getInsets();
  555. if (cellHeights == null) {
  556. int row = (cellHeight == 0) ? 0 : ((y0 - insets.top) / cellHeight);
  557. return ((row < 0) || (row >= nrows)) ? -1 : row;
  558. }
  559. else if (nrows > cellHeights.length) {
  560. return -1;
  561. }
  562. else {
  563. int y = insets.top;
  564. int row = 0;
  565. for(int i = 0; i < nrows; i++) {
  566. if ((y0 >= y) && (y0 < y + cellHeights[i])) {
  567. return row;
  568. }
  569. y += cellHeights[i];
  570. row += 1;
  571. }
  572. return -1;
  573. }
  574. }
  575. /**
  576. * Return the JList relative Y coordinate of the origin of the specified
  577. * row or -1 if row isn't valid.
  578. *
  579. * @return The Y coordinate of the origin of row, or -1.
  580. * @see #getRowHeight
  581. * @see #updateLayoutState
  582. */
  583. protected int convertRowToY(int row)
  584. {
  585. int nrows = list.getModel().getSize();
  586. Insets insets = list.getInsets();
  587. if ((row < 0) || (row >= nrows)) {
  588. return -1;
  589. }
  590. if (cellHeights == null) {
  591. return insets.top + (cellHeight * row);
  592. }
  593. else if (row >= cellHeights.length) {
  594. return -1;
  595. }
  596. else {
  597. int y = insets.top;
  598. for(int i = 0; i < row; i++) {
  599. y += cellHeights[i];
  600. }
  601. return y;
  602. }
  603. }
  604. /**
  605. * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
  606. * updateLayoutStateNeeded. This method should be called by methods
  607. * before doing any computation based on the geometry of the list.
  608. * For example it's the first call in paint() and getPreferredSize().
  609. *
  610. * @see #updateLayoutState
  611. */
  612. protected void maybeUpdateLayoutState()
  613. {
  614. if (updateLayoutStateNeeded != 0) {
  615. updateLayoutState();
  616. updateLayoutStateNeeded = 0;
  617. }
  618. }
  619. /**
  620. * Recompute the value of cellHeight or cellHeights based
  621. * and cellWidth, based on the current font and the current
  622. * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
  623. *
  624. * @see #maybeUpdateLayoutState
  625. */
  626. protected void updateLayoutState()
  627. {
  628. /* If both JList fixedCellWidth and fixedCellHeight have been
  629. * set, then initialize cellWidth and cellHeight, and set
  630. * cellHeights to null.
  631. */
  632. int fixedCellHeight = list.getFixedCellHeight();
  633. int fixedCellWidth = list.getFixedCellWidth();
  634. cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
  635. if (fixedCellHeight != -1) {
  636. cellHeight = fixedCellHeight;
  637. cellHeights = null;
  638. }
  639. else {
  640. cellHeight = -1;
  641. cellHeights = new int[list.getModel().getSize()];
  642. }
  643. /* If either of JList fixedCellWidth and fixedCellHeight haven't
  644. * been set, then initialize cellWidth and cellHeights by
  645. * scanning through the entire model. Note: if the renderer is
  646. * null, we just set cellWidth and cellHeights[*] to zero,
  647. * if they're not set already.
  648. */
  649. if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
  650. ListModel dataModel = list.getModel();
  651. int dataModelSize = dataModel.getSize();
  652. ListCellRenderer renderer = list.getCellRenderer();
  653. if (renderer != null) {
  654. for(int index = 0; index < dataModelSize; index++) {
  655. Object value = dataModel.getElementAt(index);
  656. Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
  657. rendererPane.add(c);
  658. Dimension cellSize = c.getPreferredSize();
  659. if (fixedCellWidth == -1) {
  660. cellWidth = Math.max(cellSize.width, cellWidth);
  661. }
  662. if (fixedCellHeight == -1) {
  663. cellHeights[index] = cellSize.height;
  664. }
  665. }
  666. }
  667. else {
  668. if (cellWidth == -1) {
  669. cellWidth = 0;
  670. }
  671. for(int index = 0; index < dataModelSize; index++) {
  672. cellHeights[index] = 0;
  673. }
  674. }
  675. }
  676. list.invalidate();
  677. }
  678. /**
  679. * Mouse input, and focus handling for JList. An instance of this
  680. * class is added to the appropriate java.awt.Component lists
  681. * at installUI() time. Note keyboard input is handled with JComponent
  682. * KeyboardActions, see installKeyboardActions().
  683. * <p>
  684. * <strong>Warning:</strong>
  685. * Serialized objects of this class will not be compatible with
  686. * future Swing releases. The current serialization support is appropriate
  687. * for short term storage or RMI between applications running the same
  688. * version of Swing. A future release of Swing will provide support for
  689. * long term persistence.
  690. *
  691. * @see #createMouseInputListener
  692. * @see #installKeyboardActions
  693. * @see #installUI
  694. */
  695. public class MouseInputHandler implements MouseInputListener
  696. {
  697. public void mouseClicked(MouseEvent e) {}
  698. public void mouseEntered(MouseEvent e) {}
  699. public void mouseExited(MouseEvent e) {}
  700. public void mousePressed(MouseEvent e)
  701. {
  702. if (!SwingUtilities.isLeftMouseButton(e)) {
  703. return;
  704. }
  705. if (!list.isEnabled()) {
  706. return;
  707. }
  708. /* Request focus before updating the list selection. This implies
  709. * that the current focus owner will see a focusLost() event
  710. * before the lists selection is updated IF requestFocus() is
  711. * synchronous (it is on Windows). See bug 4122345
  712. */
  713. if (!list.hasFocus()) {
  714. list.requestFocus();
  715. }
  716. int row = convertYToRow(e.getY());
  717. if (row != -1) {
  718. list.setValueIsAdjusting(true);
  719. int anchorIndex = list.getAnchorSelectionIndex();
  720. if (e.isControlDown()) {
  721. if (list.isSelectedIndex(row)) {
  722. list.removeSelectionInterval(row, row);
  723. }
  724. else {
  725. list.addSelectionInterval(row, row);
  726. }
  727. }
  728. else if (e.isShiftDown() && (anchorIndex != -1)) {
  729. list.setSelectionInterval(anchorIndex, row);
  730. }
  731. else {
  732. list.setSelectionInterval(row, row);
  733. }
  734. }
  735. }
  736. public void mouseDragged(MouseEvent e) {
  737. if (!SwingUtilities.isLeftMouseButton(e)) {
  738. return;
  739. }
  740. if (!list.isEnabled()) {
  741. return;
  742. }
  743. if (e.isShiftDown() || e.isControlDown()) {
  744. return;
  745. }
  746. int row = convertYToRow(e.getY());
  747. if (row != -1) {
  748. Rectangle cellBounds = getCellBounds(list, row, row);
  749. if (cellBounds != null) {
  750. list.scrollRectToVisible(cellBounds);
  751. list.setSelectionInterval(row, row);
  752. }
  753. }
  754. }
  755. public void mouseMoved(MouseEvent e) {
  756. }
  757. public void mouseReleased(MouseEvent e) {
  758. if (!SwingUtilities.isLeftMouseButton(e)) {
  759. return;
  760. }
  761. list.setValueIsAdjusting(false);
  762. }
  763. }
  764. /**
  765. * Creates a delegate that implements MouseInputListener.
  766. * The delegate is added to the corresponding java.awt.Component listener
  767. * lists at installUI() time. Subclasses can override this method to return
  768. * a custom MouseInputListener, e.g.
  769. * <pre>
  770. * class MyListUI extends BasicListUI {
  771. * protected MouseInputListener <b>createMouseInputListener</b>() {
  772. * return new MyMouseInputHandler();
  773. * }
  774. * public class MyMouseInputHandler extends MouseInputHandler {
  775. * public void mouseMoved(MouseEvent e) {
  776. * // do some extra work when the mouse moves
  777. * super.mouseMoved(e);
  778. * }
  779. * }
  780. * }
  781. * </pre>
  782. *
  783. * @see MouseInputHandler
  784. * @see #installUI
  785. */
  786. protected MouseInputListener createMouseInputListener() {
  787. return new MouseInputHandler();
  788. }
  789. /**
  790. * This inner class is marked "public" due to a compiler bug.
  791. * This class should be treated as a "protected" inner class.
  792. * Instantiate it only within subclasses of BasicTableUI.
  793. */
  794. public class FocusHandler implements FocusListener
  795. {
  796. protected void repaintCellFocus()
  797. {
  798. int leadIndex = list.getLeadSelectionIndex();
  799. if (leadIndex != -1) {
  800. Rectangle r = getCellBounds(list, leadIndex, leadIndex);
  801. if (r != null) {
  802. list.repaint(r.x, r.y, r.width, r.height);
  803. }
  804. }
  805. }
  806. /* The focusGained() focusLost() methods run when the JList
  807. * focus changes.
  808. */
  809. public void focusGained(FocusEvent e) {
  810. // hasFocus = true;
  811. repaintCellFocus();
  812. }
  813. public void focusLost(FocusEvent e) {
  814. // hasFocus = false;
  815. repaintCellFocus();
  816. }
  817. }
  818. protected FocusListener createFocusListener() {
  819. return new FocusHandler();
  820. }
  821. /**
  822. * The ListSelectionListener that's added to the JLists selection
  823. * model at installUI time, and whenever the JList.selectionModel property
  824. * changes. When the selection changes we repaint the affected rows.
  825. * <p>
  826. * <strong>Warning:</strong>
  827. * Serialized objects of this class will not be compatible with
  828. * future Swing releases. The current serialization support is appropriate
  829. * for short term storage or RMI between applications running the same
  830. * version of Swing. A future release of Swing will provide support for
  831. * long term persistence.
  832. *
  833. * @see #createListSelectionListener
  834. * @see #getCellBounds
  835. * @see #installUI
  836. */
  837. public class ListSelectionHandler implements ListSelectionListener
  838. {
  839. public void valueChanged(ListSelectionEvent e)
  840. {
  841. maybeUpdateLayoutState();
  842. int minY = convertRowToY(e.getFirstIndex());
  843. int maxY = convertRowToY(e.getLastIndex());
  844. if ((minY == -1) || (maxY == -1)) {
  845. list.repaint(0, 0, list.getWidth(), list.getHeight());
  846. }
  847. else {
  848. maxY += getRowHeight(e.getLastIndex());
  849. list.repaint(0, minY, list.getWidth(), maxY - minY);
  850. }
  851. }
  852. }
  853. /**
  854. * Creates an instance of ListSelectionHandler that's added to
  855. * the JLists by selectionModel as needed. Subclasses can override
  856. * this method to return a custom ListSelectionListener, e.g.
  857. * <pre>
  858. * class MyListUI extends BasicListUI {
  859. * protected ListSelectionListener <b>createListSelectionListener</b>() {
  860. * return new MySelectionListener();
  861. * }
  862. * public class MySelectionListener extends ListSelectionHandler {
  863. * public void valueChanged(ListSelectionEvent e) {
  864. * // do some extra work when the selection changes
  865. * super.valueChange(e);
  866. * }
  867. * }
  868. * }
  869. * </pre>
  870. *
  871. * @see ListSelectionHandler
  872. * @see #installUI
  873. */
  874. protected ListSelectionListener createListSelectionListener() {
  875. return new ListSelectionHandler();
  876. }
  877. private void redrawList() {
  878. list.revalidate();
  879. list.repaint();
  880. }
  881. /**
  882. * The ListDataListener that's added to the JLists model at
  883. * installUI time, and whenever the JList.model property changes.
  884. * <p>
  885. * <strong>Warning:</strong>
  886. * Serialized objects of this class will not be compatible with
  887. * future Swing releases. The current serialization support is appropriate
  888. * for short term storage or RMI between applications running the same
  889. * version of Swing. A future release of Swing will provide support for
  890. * long term persistence.
  891. *
  892. * @see JList#getModel
  893. * @see #maybeUpdateLayout
  894. * @see #createListDataListener
  895. * @see #installUI
  896. */
  897. public class ListDataHandler implements ListDataListener
  898. {
  899. public void intervalAdded(ListDataEvent e) {
  900. updateLayoutStateNeeded = modelChanged;
  901. int minIndex = Math.min(e.getIndex0(), e.getIndex1());
  902. int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
  903. /* Sync the SelectionModel with the DataModel.
  904. */
  905. ListSelectionModel sm = list.getSelectionModel();
  906. if (sm != null) {
  907. sm.insertIndexInterval(minIndex, maxIndex - minIndex, true);
  908. }
  909. /* Repaint the entire list, from the origin of
  910. * the first added cell, to the bottom of the
  911. * component.
  912. */
  913. int y = Math.max(0, convertRowToY(minIndex));
  914. int h = list.getHeight() - y;
  915. list.revalidate();
  916. list.repaint(0, y, list.getWidth(), h);
  917. }
  918. public void intervalRemoved(ListDataEvent e)
  919. {
  920. updateLayoutStateNeeded = modelChanged;
  921. /* Sync the SelectionModel with the DataModel.
  922. */
  923. ListSelectionModel sm = list.getSelectionModel();
  924. if (sm != null) {
  925. sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
  926. }
  927. /* Repaint the entire list, from the origin of
  928. * the first removed cell, to the bottom of the
  929. * component.
  930. */
  931. int minIndex = Math.min(e.getIndex0(), e.getIndex1());
  932. int y = Math.max(0, convertRowToY(minIndex));
  933. int h = list.getHeight() - y;
  934. list.revalidate();
  935. list.repaint(0, y, list.getWidth(), h);
  936. }
  937. public void contentsChanged(ListDataEvent e) {
  938. updateLayoutStateNeeded = modelChanged;
  939. redrawList();
  940. }
  941. }
  942. /**
  943. * Creates an instance of ListDataListener that's added to
  944. * the JLists by model as needed. Subclasses can override
  945. * this method to return a custom ListDataListener, e.g.
  946. * <pre>
  947. * class MyListUI extends BasicListUI {
  948. * protected ListDataListener <b>createListDataListener</b>() {
  949. * return new MyListDataListener();
  950. * }
  951. * public class MyListDataListener extends ListDataHandler {
  952. * public void contentsChanged(ListDataEvent e) {
  953. * // do some extra work when the models contents change
  954. * super.contentsChange(e);
  955. * }
  956. * }
  957. * }
  958. * </pre>
  959. *
  960. * @see ListDataListener
  961. * @see JList#getModel
  962. * @see #installUI
  963. */
  964. protected ListDataListener createListDataListener() {
  965. return new ListDataHandler();
  966. }
  967. /**
  968. * The PropertyChangeListener that's added to the JList at
  969. * installUI time. When the value of a JList property that
  970. * affects layout changes, we set a bit in updateLayoutStateNeeded.
  971. * If the JLists model changes we additionally remove our listeners
  972. * from the old model. Likewise for the JList selectionModel.
  973. * <p>
  974. * <strong>Warning:</strong>
  975. * Serialized objects of this class will not be compatible with
  976. * future Swing releases. The current serialization support is appropriate
  977. * for short term storage or RMI between applications running the same
  978. * version of Swing. A future release of Swing will provide support for
  979. * long term persistence.
  980. *
  981. * @see #maybeUpdateLayout
  982. * @see #createPropertyListener
  983. * @see #installUI
  984. */
  985. public class PropertyChangeHandler implements PropertyChangeListener
  986. {
  987. public void propertyChange(PropertyChangeEvent e)
  988. {
  989. String propertyName = e.getPropertyName();
  990. /* If the JList.model property changes, remove our listener,
  991. * listDataListener from the old model and add it to the new one.
  992. */
  993. if (propertyName.equals("model")) {
  994. ListModel oldModel = (ListModel)e.getOldValue();
  995. ListModel newModel = (ListModel)e.getNewValue();
  996. if (oldModel != null) {
  997. oldModel.removeListDataListener(listDataListener);
  998. }
  999. if (newModel != null) {
  1000. newModel.addListDataListener(listDataListener);
  1001. }
  1002. updateLayoutStateNeeded |= modelChanged;
  1003. redrawList();
  1004. }
  1005. /* If the JList.selectionModel property changes, remove our listener,
  1006. * listSelectionListener from the old selectionModel and add it to the new one.
  1007. */
  1008. else if (propertyName.equals("selectionModel")) {
  1009. ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
  1010. ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
  1011. if (oldModel != null) {
  1012. oldModel.removeListSelectionListener(listSelectionListener);
  1013. }
  1014. if (newModel != null) {
  1015. newModel.addListSelectionListener(listSelectionListener);
  1016. }
  1017. updateLayoutStateNeeded |= modelChanged;
  1018. redrawList();
  1019. }
  1020. else if (propertyName.equals("cellRenderer")) {
  1021. updateLayoutStateNeeded |= cellRendererChanged;
  1022. redrawList();
  1023. }
  1024. else if (propertyName.equals("font")) {
  1025. updateLayoutStateNeeded |= fontChanged;
  1026. redrawList();
  1027. }
  1028. else if (propertyName.equals("prototypeCellValue")) {
  1029. updateLayoutStateNeeded |= prototypeCellValueChanged;
  1030. redrawList();
  1031. }
  1032. else if (propertyName.equals("fixedCellHeight")) {
  1033. updateLayoutStateNeeded |= fixedCellHeightChanged;
  1034. redrawList();
  1035. }
  1036. else if (propertyName.equals("fixedCellWidth")) {
  1037. updateLayoutStateNeeded |= fixedCellWidthChanged;
  1038. redrawList();
  1039. }
  1040. else if (propertyName.equals("cellRenderer")) {
  1041. updateLayoutStateNeeded |= cellRendererChanged;
  1042. redrawList();
  1043. }
  1044. else if (propertyName.equals("selectionForeground")) {
  1045. list.repaint();
  1046. }
  1047. else if (propertyName.equals("selectionBackground")) {
  1048. list.repaint();
  1049. }
  1050. }
  1051. }
  1052. /**
  1053. * Creates an instance of PropertyChangeHandler that's added to
  1054. * the JList by installUI(). Subclasses can override this method
  1055. * to return a custom PropertyChangeListener, e.g.
  1056. * <pre>
  1057. * class MyListUI extends BasicListUI {
  1058. * protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
  1059. * return new MyPropertyChangeListener();
  1060. * }
  1061. * public class MyPropertyChangeListener extends PropertyChangeHandler {
  1062. * public void propertyChange(PropertyChangeEvent e) {
  1063. * if (e.getPropertyName().equals("model")) {
  1064. * // do some extra work when the model changes
  1065. * }
  1066. * super.propertyChange(e);
  1067. * }
  1068. * }
  1069. * }
  1070. * </pre>
  1071. *
  1072. * @see PropertyChangeListener
  1073. * @see #installUI
  1074. */
  1075. protected PropertyChangeListener createPropertyChangeListener() {
  1076. return new PropertyChangeHandler();
  1077. }
  1078. // Keyboard navigation actions.
  1079. // NOTE: DefaultListSelectionModel.setAnchorSelectionIndex and
  1080. // DefaultListSelectionModel.setLeadSelectionIndex both force the
  1081. // new index to be selected. Because of this not all the bindings
  1082. // could be appropriately implemented. Specifically those that
  1083. // change the lead/anchor without selecting are not enabled.
  1084. // Once this has been fixed the following actions will appropriately
  1085. // work with selectionType == CHANGE_LEAD.
  1086. /** Used by IncrementLeadSelectionAction. Indicates the action should
  1087. * change the lead, and not select it. */
  1088. private static final int CHANGE_LEAD = 0;
  1089. /** Used by IncrementLeadSelectionAction. Indicates the action should
  1090. * change the selection and lead. */
  1091. private static final int CHANGE_SELECTION = 1;
  1092. /** Used by IncrementLeadSelectionAction. Indicates the action should
  1093. * extend the selection from the anchor to the next index. */
  1094. private static final int EXTEND_SELECTION = 2;
  1095. /**
  1096. * A generaic action this is only enabled when the JList is enabled.
  1097. */
  1098. private abstract class ListAction implements ActionListener {
  1099. protected ListAction(String name) {
  1100. }
  1101. public boolean isEnabled() { return (list != null &&
  1102. list.isEnabled()); }
  1103. }
  1104. /**
  1105. * Action to increment the selection in the list up/down a row at
  1106. * a type. This also has the option to extend the selection, or
  1107. * only move the lead.
  1108. */
  1109. private class IncrementLeadSelectionAction extends ListAction {
  1110. /** Amount to offset, subclasses will define what this means. */
  1111. protected int amount;
  1112. /** One of CHANGE_LEAD, CHANGE_SELECTION or EXTEND_SELECTION. */
  1113. protected int selectionType;
  1114. protected IncrementLeadSelectionAction(String name, int type) {
  1115. this(name, type, -1);
  1116. }
  1117. protected IncrementLeadSelectionAction(String name, int type,
  1118. int amount) {
  1119. super(name);
  1120. this.amount = amount;
  1121. this.selectionType = type;
  1122. }
  1123. /**
  1124. * Returns the next index to select. This is based on the lead
  1125. * selected index and the <code>amount</code> ivar.
  1126. */
  1127. protected int getNextIndex() {
  1128. int index = list.getLeadSelectionIndex();
  1129. int size = list.getModel().getSize();
  1130. if (index == -1) {
  1131. if (size > 0) {
  1132. if (amount > 0) {
  1133. index = 0;
  1134. }
  1135. else {
  1136. index = size - 1;
  1137. }
  1138. }
  1139. }
  1140. else {
  1141. index += amount;
  1142. }
  1143. return index;
  1144. }
  1145. /**
  1146. * Ensures the particular index is visible. This simply forwards
  1147. * the method to list.
  1148. */
  1149. protected void ensureIndexIsVisible(int index) {
  1150. list.ensureIndexIsVisible(index);
  1151. }
  1152. /**
  1153. * Invokes <code>getNextIndex</code> to determine the next index
  1154. * to select. If the index is valid (not -1 and < size of the model),
  1155. * this will either: move the selection to the new index if
  1156. * the selectionType == CHANGE_SELECTION, move the lead to the
  1157. * new index if selectionType == CHANGE_LEAD, otherwise the
  1158. * selection is extend from the anchor to the new index and the
  1159. * lead is set to the new index.
  1160. */
  1161. public void actionPerformed(ActionEvent e) {
  1162. int index = getNextIndex();
  1163. if (index >= 0 && index < list.getModel().getSize()) {
  1164. ListSelectionModel lsm = list.getSelectionModel();
  1165. if (selectionType == EXTEND_SELECTION) {
  1166. int anchor = lsm.getAnchorSelectionIndex();
  1167. if (anchor == -1) {
  1168. anchor = index;
  1169. }
  1170. list.setSelectionInterval(anchor, index);
  1171. lsm.setAnchorSelectionIndex(anchor);
  1172. lsm.setLeadSelectionIndex(index);
  1173. }
  1174. else if (selectionType == CHANGE_SELECTION) {
  1175. list.setSelectedIndex(index);
  1176. }
  1177. else {
  1178. lsm.setLeadSelectionIndex(index);
  1179. }
  1180. ensureIndexIsVisible(index);
  1181. }
  1182. }
  1183. }
  1184. /**
  1185. * Action to move the selection to the first item in the list.
  1186. */
  1187. private class HomeAction extends IncrementLeadSelectionAction {
  1188. protected HomeAction(String name, int type) {
  1189. super(name, type);
  1190. }
  1191. protected int getNextIndex() {
  1192. return 0;
  1193. }
  1194. }
  1195. /**
  1196. * Action to move the selection to the last item in the list.
  1197. */
  1198. private class EndAction extends IncrementLeadSelectionAction {
  1199. protected EndAction(String name, int type) {
  1200. super(name, type);
  1201. }
  1202. protected int getNextIndex() {
  1203. return list.getModel().getSize() - 1;
  1204. }
  1205. }
  1206. /**
  1207. * Action to move up one page.
  1208. */
  1209. private class PageUpAction extends IncrementLeadSelectionAction {
  1210. protected PageUpAction(String name, int type) {
  1211. super(name, type);
  1212. }
  1213. protected int getNextIndex() {
  1214. int index = list.getFirstVisibleIndex();
  1215. ListSelectionModel lsm = list.getSelectionModel();
  1216. if (lsm.getLeadSelectionIndex() == index) {
  1217. Rectangle visRect = list.getVisibleRect();
  1218. visRect.y = Math.max(0, visRect.y - visRect.height);
  1219. index = list.locationToIndex(visRect.getLocation());
  1220. }
  1221. return index;
  1222. }
  1223. protected void ensureIndexIsVisible(int index) {
  1224. Rectangle visRect = list.getVisibleRect();
  1225. Rectangle cellBounds = list.getCellBounds(index, index);
  1226. cellBounds.height = visRect.height;
  1227. list.scrollRectToVisible(cellBounds);
  1228. }
  1229. }
  1230. /**
  1231. * Action to move down one page.
  1232. */
  1233. private class PageDownAction extends IncrementLeadSelectionAction {
  1234. protected PageDownAction(String name, int type) {
  1235. super(name, type);
  1236. }
  1237. protected int getNextIndex() {
  1238. int index = list.getLastVisibleIndex();
  1239. ListSelectionModel lsm = list.getSelectionModel();
  1240. if (index == -1) {
  1241. // Will happen if size < viewport size.
  1242. index = list.getModel().getSize() - 1;
  1243. }
  1244. if (lsm.getLeadSelectionIndex() == index) {
  1245. Rectangle visRect = list.getVisibleRect();
  1246. visRect.y += visRect.height + visRect.height - 1;
  1247. index = list.locationToIndex(visRect.getLocation());
  1248. if (index == -1) {
  1249. index = list.getModel().getSize() - 1;
  1250. }
  1251. }
  1252. return index;
  1253. }
  1254. protected void ensureIndexIsVisible(int index) {
  1255. Rectangle visRect = list.getVisibleRect();
  1256. Rectangle cellBounds = list.getCellBounds(index, index);
  1257. cellBounds.y = Math.max(0, cellBounds.y + cellBounds.height -
  1258. visRect.height);
  1259. cellBounds.height = visRect.height;
  1260. list.scrollRectToVisible(cellBounds);
  1261. }
  1262. }
  1263. /**
  1264. * Action to select all the items in the list.
  1265. */
  1266. private class SelectAllAction extends ListAction {
  1267. private SelectAllAction(String name) {
  1268. super(name);
  1269. }
  1270. public void actionPerformed(ActionEvent e) {
  1271. // Select all should not alter the lead and anchor.
  1272. // ListSelectionModel encforces the selection to the anchor/lead,
  1273. // so it is commented out.
  1274. // ListSelectionModel lsm = list.getSelectionModel();
  1275. // int anchor = lsm.getAnchorSelectionIndex();
  1276. // int lead = lsm.getLeadSelectionIndex();
  1277. list.setSelectionInterval(0, list.getModel().getSize() - 1);
  1278. // lsm.setAnchorSelectionIndex(anchor);
  1279. // lsm.setLeadSelectionIndex(lead);
  1280. }
  1281. }
  1282. /**
  1283. * Action to clear the selection in the list.
  1284. */
  1285. private class ClearSelectionAction extends ListAction {
  1286. private ClearSelectionAction(String name) {
  1287. super(name);
  1288. }
  1289. public void actionPerformed(ActionEvent e) {
  1290. // Unselect all should not alter the lead and anchor.
  1291. // ListSelectionModel encforces the selection to the anchor/lead,
  1292. // so it is commented out.
  1293. // ListSelectionModel lsm = list.getSelectionModel();
  1294. // int anchor = lsm.getAnchorSelectionIndex();
  1295. // int lead = lsm.getLeadSelectionIndex();
  1296. list.clearSelection();
  1297. // lsm.setAnchorSelectionIndex(anchor);
  1298. // lsm.setLeadSelectionIndex(lead);
  1299. }
  1300. }
  1301. }