1. /*
  2. * @(#)SynthListUI.java 1.8 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import javax.swing.*;
  9. import javax.swing.event.*;
  10. import javax.swing.plaf.*;
  11. import javax.swing.text.Position;
  12. import java.awt.*;
  13. import java.awt.event.*;
  14. import java.awt.datatransfer.Transferable;
  15. import java.awt.dnd.*;
  16. import java.util.ArrayList;
  17. import java.util.TooManyListenersException;
  18. import java.beans.PropertyChangeListener;
  19. import java.beans.PropertyChangeEvent;
  20. /**
  21. * A Windows L&F implementation of ListUI.
  22. * <p>
  23. *
  24. * @version 1.8, 01/23/03 (based on BasicListUI v 1.91)
  25. * @author Hans Muller
  26. * @author Philip Milne
  27. */
  28. class SynthListUI extends ListUI implements SynthUI {
  29. protected JList list = null;
  30. private SynthStyle style;
  31. protected CellRendererPane rendererPane;
  32. // Listeners that this UI attaches to the JList
  33. protected FocusListener focusListener;
  34. protected MouseInputListener mouseInputListener;
  35. protected ListSelectionListener listSelectionListener;
  36. protected ListDataListener listDataListener;
  37. protected PropertyChangeListener propertyChangeListener;
  38. private KeyListener keyListener;
  39. // PENDING(hmuller) need a doc pointer to #getRowHeight, #maybeUpdateLayoutState
  40. protected int[] cellHeights = null;
  41. protected int cellHeight = -1;
  42. protected int cellWidth = -1;
  43. protected int updateLayoutStateNeeded = modelChanged;
  44. /**
  45. * Height of the list. When asked to paint, if the current size of
  46. * the list differs, this will update the layout state.
  47. */
  48. private int listHeight;
  49. /**
  50. * Width of the list. When asked to paint, if the current size of
  51. * the list differs, this will update the layout state.
  52. */
  53. private int listWidth;
  54. /**
  55. * The layout orientation of the list.
  56. */
  57. private int layoutOrientation;
  58. // Following ivars are used if the list is laying out horizontally
  59. /**
  60. * Number of columns to create.
  61. */
  62. private int columnCount;
  63. /**
  64. * Preferred height to make the list, this is only used if the
  65. * the list is layed out horizontally.
  66. */
  67. private int preferredHeight;
  68. /**
  69. * Number of rows per column. This is only used if the row height is
  70. * fixed.
  71. */
  72. private int rowsPerColumn;
  73. /* The bits below define JList property changes that affect layout.
  74. * When one of these properties changes we set a bit in
  75. * updateLayoutStateNeeded. The change is dealt with lazily, see
  76. * maybeUpdateLayoutState. Changes to the JLists model, e.g. the
  77. * models length changed, are handled similarly, see DataListener.
  78. */
  79. protected final static int modelChanged = 1 << 0;
  80. protected final static int selectionModelChanged = 1 << 1;
  81. protected final static int fontChanged = 1 << 2;
  82. protected final static int fixedCellWidthChanged = 1 << 3;
  83. protected final static int fixedCellHeightChanged = 1 << 4;
  84. protected final static int prototypeCellValueChanged = 1 << 5;
  85. protected final static int cellRendererChanged = 1 << 6;
  86. private final static int layoutOrientationChanged = 1 << 7;
  87. private final static int heightChanged = 1 << 8;
  88. private final static int widthChanged = 1 << 9;
  89. private final static int componentOrientationChanged = 1 << 10;
  90. public static void loadActionMap(ActionMap map) {
  91. // NOTE: this needs to remain static. If you have a need to
  92. // have Actions that reference the UI in the ActionMap,
  93. // then you'll also need to change the registeration of the
  94. // ActionMap.
  95. map.put("selectPreviousColumn",
  96. new IncrementLeadByColumnAction("selectPreviousColumn",
  97. CHANGE_SELECTION, -1));
  98. map.put("selectPreviousColumnExtendSelection",
  99. new IncrementLeadByColumnAction
  100. ("selectPreviousColumnExtendSelection",
  101. EXTEND_SELECTION, -1));
  102. map.put("selectNextColumn",
  103. new IncrementLeadByColumnAction("selectNextColumn",
  104. CHANGE_SELECTION, 1));
  105. map.put("selectNextColumnExtendSelection",
  106. new IncrementLeadByColumnAction
  107. ("selectNextColumnExtendSelection", EXTEND_SELECTION, 1));
  108. map.put("selectPreviousRow",
  109. new IncrementLeadSelectionAction("selectPreviousRow",
  110. CHANGE_SELECTION, -1));
  111. map.put("selectPreviousRowExtendSelection",
  112. new IncrementLeadSelectionAction
  113. ("selectPreviousRowExtendSelection",EXTEND_SELECTION, -1));
  114. map.put("selectNextRow",
  115. new IncrementLeadSelectionAction("selectNextRow",
  116. CHANGE_SELECTION, 1));
  117. map.put("selectNextRowExtendSelection",
  118. new IncrementLeadSelectionAction
  119. ("selectNextRowExtendSelection", EXTEND_SELECTION, 1));
  120. map.put("selectFirstRow",
  121. new HomeAction("selectFirstRow", CHANGE_SELECTION));
  122. map.put("selectFirstRowExtendSelection",
  123. new HomeAction("selectFirstRowExtendSelection",
  124. EXTEND_SELECTION));
  125. map.put("selectLastRow",
  126. new EndAction("selctLastRow", CHANGE_SELECTION));
  127. map.put("selectLastRowExtendSelection",
  128. new EndAction("selectLastRowExtendSelection",
  129. EXTEND_SELECTION));
  130. map.put("scrollUp",
  131. new PageUpAction("scrollUp", CHANGE_SELECTION));
  132. map.put("scrollUpExtendSelection",
  133. new PageUpAction("scrollUpExtendSelection",
  134. EXTEND_SELECTION));
  135. map.put("scrollDown",
  136. new PageDownAction("scrollDown", CHANGE_SELECTION));
  137. map.put("scrollDownExtendSelection",
  138. new PageDownAction("scrollDownExtendSelection",
  139. EXTEND_SELECTION));
  140. map.put("selectAll", new SelectAllAction("selectAll"));
  141. map.put("clearSelection", new
  142. ClearSelectionAction("clearSelection"));
  143. map.put(TransferHandler.getCutAction().getValue(Action.NAME),
  144. TransferHandler.getCutAction());
  145. map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
  146. TransferHandler.getCopyAction());
  147. map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
  148. TransferHandler.getPasteAction());
  149. }
  150. /**
  151. * Paint one List cell: compute the relevant state, get the "rubber stamp"
  152. * cell renderer component, and then use the CellRendererPane to paint it.
  153. * Subclasses may want to override this method rather than paint().
  154. *
  155. * @see #paint
  156. */
  157. protected void paintCell(
  158. Graphics g,
  159. int row,
  160. Rectangle rowBounds,
  161. ListCellRenderer cellRenderer,
  162. ListModel dataModel,
  163. ListSelectionModel selModel,
  164. int leadIndex)
  165. {
  166. Object value = dataModel.getElementAt(row);
  167. boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
  168. boolean isSelected = selModel.isSelectedIndex(row);
  169. Component rendererComponent =
  170. cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
  171. int cx = rowBounds.x;
  172. int cy = rowBounds.y;
  173. int cw = rowBounds.width;
  174. int ch = rowBounds.height;
  175. rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true);
  176. }
  177. /**
  178. * Paint the rows that intersect the Graphics objects clipRect. This
  179. * method calls paintCell as necessary. Subclasses
  180. * may want to override these methods.
  181. *
  182. * @see #paintCell
  183. */
  184. public void update(Graphics g, JComponent c) {
  185. SynthContext context = getContext(c);
  186. SynthLookAndFeel.update(context, g);
  187. paint(context, g);
  188. context.dispose();
  189. }
  190. public void paint(Graphics g, JComponent c) {
  191. SynthContext context = getContext(c);
  192. paint(context, g);
  193. context.dispose();
  194. }
  195. protected void paint(SynthContext context, Graphics g) {
  196. switch (layoutOrientation) {
  197. case JList.VERTICAL_WRAP:
  198. if (list.getHeight() != listHeight) {
  199. updateLayoutStateNeeded |= heightChanged;
  200. redrawList();
  201. }
  202. break;
  203. case JList.HORIZONTAL_WRAP:
  204. if (list.getWidth() != listWidth) {
  205. updateLayoutStateNeeded |= widthChanged;
  206. redrawList();
  207. }
  208. break;
  209. default:
  210. break;
  211. }
  212. maybeUpdateLayoutState();
  213. ListCellRenderer renderer = list.getCellRenderer();
  214. ListModel dataModel = list.getModel();
  215. ListSelectionModel selModel = list.getSelectionModel();
  216. int size;
  217. if ((renderer == null) || (size = dataModel.getSize()) == 0) {
  218. return;
  219. }
  220. // Determine how many columns we need to paint
  221. Rectangle paintBounds = g.getClipBounds();
  222. int startColumn, endColumn;
  223. if (SynthLookAndFeel.isLeftToRight(list)) {
  224. startColumn = convertLocationToColumn(paintBounds.x,
  225. paintBounds.y);
  226. endColumn = convertLocationToColumn(paintBounds.x +
  227. paintBounds.width,
  228. paintBounds.y);
  229. } else {
  230. startColumn = convertLocationToColumn(paintBounds.x +
  231. paintBounds.width,
  232. paintBounds.y);
  233. endColumn = convertLocationToColumn(paintBounds.x,
  234. paintBounds.y);
  235. }
  236. int maxY = paintBounds.y + paintBounds.height;
  237. int leadIndex = list.getLeadSelectionIndex();
  238. int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ?
  239. columnCount : 1;
  240. for (int colCounter = startColumn; colCounter <= endColumn;
  241. colCounter++) {
  242. // And then how many rows in this columnn
  243. int row = convertLocationToRowInColumn(paintBounds.y, colCounter);
  244. int rowCount = getRowCount(colCounter);
  245. int index = getModelIndex(colCounter, row);
  246. Rectangle rowBounds = getCellBounds(list, index, index);
  247. if (rowBounds == null) {
  248. // Not valid, bail!
  249. return;
  250. }
  251. while (row < rowCount && rowBounds.y < maxY &&
  252. index < size) {
  253. rowBounds.height = getHeight(colCounter, row);
  254. g.setClip(rowBounds.x, rowBounds.y, rowBounds.width,
  255. rowBounds.height);
  256. g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width,
  257. paintBounds.height);
  258. paintCell(g, index, rowBounds, renderer, dataModel, selModel,
  259. leadIndex);
  260. rowBounds.y += rowBounds.height;
  261. index += rowIncrement;
  262. row++;
  263. }
  264. }
  265. }
  266. /**
  267. * The preferredSize of the list depends upon the layout orientation.
  268. * <table><tr><td>Layout Orientation</td><td>Preferred Size</td></tr>
  269. * <tr>
  270. * <td>JList.VERTICAL
  271. * <td>The preferredSize of the list is total height of the rows
  272. * and the maximum width of the cells. If JList.fixedCellHeight
  273. * is specified then the total height of the rows is just
  274. * (cellVerticalMargins + fixedCellHeight) * model.getSize() where
  275. * rowVerticalMargins is the space we allocate for drawing
  276. * the yellow focus outline. Similarly if fixedCellWidth is
  277. * specified then we just use that.
  278. * </td>
  279. * <tr>
  280. * <td>JList.VERTICAL_WRAP
  281. * <td>If the visible row count is greater than zero, the preferredHeight
  282. * is the maximum cell height * visibleRowCount. If the visible row
  283. * count is <= 0, the preferred height is either the current height
  284. * of the list, or the maximum cell height, whichever is
  285. * bigger. The preferred width is than the maximum cell width *
  286. * number of columns needed. Where the number of columns needs is
  287. * list.height / max cell height. Max cell height is either the fixed
  288. * cell height, or is determined by iterating through all the cells
  289. * to find the maximum height from the ListCellRenderer.
  290. * <tr>
  291. * <td>JList.HORIZONTAL_WRAP
  292. * <td>If the visible row count is greater than zero, the preferredHeight
  293. * is the maximum cell height * adjustedRowCount. Where
  294. * visibleRowCount is used to determine the number of columns.
  295. * Because this lays out horizontally the number of rows is
  296. * then determined from the column count. For example, lets say
  297. * you have a model with 10 items and the visible row count is 8.
  298. * The number of columns needed to display this is 2, but you no
  299. * longer need 8 rows to display this, you only need 5, thus
  300. * the adjustedRowCount is 5.
  301. * <p>If the visible row
  302. * count is <= 0, the preferred height is dictated by the
  303. * number of columns, which will be as many as can fit in the width
  304. * of the <code>JList</code> (width / max cell width), with at
  305. * least one column. The preferred height then becomes the
  306. * model size / number of columns * maximum cell height.
  307. * Max cell height is either the fixed
  308. * cell height, or is determined by iterating through all the cells
  309. * to find the maximum height from the ListCellRenderer.
  310. * </table>
  311. * The above specifies the raw preferred width and height. The resulting
  312. * preferred width is the above width + insets.left + insets.right and
  313. * the resulting preferred height is the above height + insets.top +
  314. * insets.bottom. Where the <code>Insets</code> are determined from
  315. * <code>list.getInsets()</code>.
  316. *
  317. * @param c The JList component.
  318. * @return The total size of the list.
  319. */
  320. public Dimension getPreferredSize(JComponent c) {
  321. maybeUpdateLayoutState();
  322. int lastRow = list.getModel().getSize() - 1;
  323. if (lastRow < 0) {
  324. return new Dimension(0, 0);
  325. }
  326. Insets insets = list.getInsets();
  327. int width = cellWidth * columnCount + insets.left + insets.right;
  328. int height;
  329. if (layoutOrientation != JList.VERTICAL) {
  330. height = preferredHeight;
  331. }
  332. else {
  333. Rectangle bounds = getCellBounds(list, lastRow);
  334. if (bounds != null) {
  335. height = bounds.y + bounds.height + insets.bottom;
  336. }
  337. else {
  338. height = 0;
  339. }
  340. }
  341. return new Dimension(width, height);
  342. }
  343. /**
  344. * @return the preferred size
  345. * @see #getPreferredSize
  346. */
  347. public Dimension getMinimumSize(JComponent c) {
  348. return getPreferredSize(c);
  349. }
  350. /**
  351. * @return the preferred size
  352. * @see #getPreferredSize
  353. */
  354. public Dimension getMaximumSize(JComponent c) {
  355. return getPreferredSize(c);
  356. }
  357. /**
  358. * Selected the previous row and force it to be visible.
  359. *
  360. * @see JList#ensureIndexIsVisible
  361. */
  362. protected void selectPreviousIndex() {
  363. int s = list.getSelectedIndex();
  364. if(s > 0) {
  365. s -= 1;
  366. list.setSelectedIndex(s);
  367. list.ensureIndexIsVisible(s);
  368. }
  369. }
  370. /**
  371. * Selected the previous row and force it to be visible.
  372. *
  373. * @see JList#ensureIndexIsVisible
  374. */
  375. protected void selectNextIndex()
  376. {
  377. int s = list.getSelectedIndex();
  378. if((s + 1) < list.getModel().getSize()) {
  379. s += 1;
  380. list.setSelectedIndex(s);
  381. list.ensureIndexIsVisible(s);
  382. }
  383. }
  384. /**
  385. * Registers the keyboard bindings on the <code>JList</code> that the
  386. * <code>BasicListUI</code> is associated with. This method is called at
  387. * installUI() time.
  388. *
  389. * @see #installUI
  390. */
  391. protected void installKeyboardActions() {
  392. InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
  393. SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
  394. inputMap);
  395. LazyActionMap.installLazyActionMap(list, SynthListUI.class,
  396. "List.actionMap");
  397. }
  398. InputMap getInputMap(int condition) {
  399. if (condition == JComponent.WHEN_FOCUSED) {
  400. SynthContext context = getContext(list, ENABLED);
  401. InputMap keyMap = (InputMap)context.getStyle().get(context,
  402. "List.focusInputMap");
  403. InputMap rtlKeyMap;
  404. if (!SynthLookAndFeel.isLeftToRight(list) &&
  405. (rtlKeyMap = (InputMap)context.getStyle().get(context,
  406. "List.focusInputMap.RightToLeft")) != null) {
  407. rtlKeyMap.setParent(keyMap);
  408. keyMap = rtlKeyMap;
  409. }
  410. context.dispose();
  411. return keyMap;
  412. }
  413. return null;
  414. }
  415. /**
  416. * Unregisters keyboard actions installed from
  417. * <code>installKeyboardActions</code>.
  418. * This method is called at uninstallUI() time - subclassess should
  419. * ensure that all of the keyboard actions registered at installUI
  420. * time are removed here.
  421. *
  422. * @see #installUI
  423. */
  424. protected void uninstallKeyboardActions() {
  425. SwingUtilities.replaceUIActionMap(list, null);
  426. SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
  427. }
  428. /**
  429. * Create and install the listeners for the JList, its model, and its
  430. * selectionModel. This method is called at installUI() time.
  431. *
  432. * @see #installUI
  433. * @see #uninstallListeners
  434. */
  435. protected void installListeners()
  436. {
  437. focusListener = createFocusListener();
  438. mouseInputListener = createMouseInputListener();
  439. propertyChangeListener = createPropertyChangeListener();
  440. listSelectionListener = createListSelectionListener();
  441. listDataListener = createListDataListener();
  442. keyListener = createKeyListener();
  443. list.addFocusListener(focusListener);
  444. list.addMouseListener(defaultDragRecognizer);
  445. list.addMouseMotionListener(defaultDragRecognizer);
  446. list.addMouseListener(mouseInputListener);
  447. list.addMouseMotionListener(mouseInputListener);
  448. list.addPropertyChangeListener(propertyChangeListener);
  449. list.addKeyListener(keyListener);
  450. ListModel model = list.getModel();
  451. if (model != null) {
  452. model.addListDataListener(listDataListener);
  453. }
  454. ListSelectionModel selectionModel = list.getSelectionModel();
  455. if (selectionModel != null) {
  456. selectionModel.addListSelectionListener(listSelectionListener);
  457. }
  458. }
  459. /**
  460. * Remove the listeners for the JList, its model, and its
  461. * selectionModel. All of the listener fields, are reset to
  462. * null here. This method is called at uninstallUI() time,
  463. * it should be kept in sync with installListeners.
  464. *
  465. * @see #uninstallUI
  466. * @see #installListeners
  467. */
  468. protected void uninstallListeners()
  469. {
  470. list.removeFocusListener(focusListener);
  471. list.removeMouseListener(defaultDragRecognizer);
  472. list.removeMouseMotionListener(defaultDragRecognizer);
  473. list.removeMouseListener(mouseInputListener);
  474. list.removeMouseMotionListener(mouseInputListener);
  475. list.removePropertyChangeListener(propertyChangeListener);
  476. list.removeKeyListener(keyListener);
  477. ListModel model = list.getModel();
  478. if (model != null) {
  479. model.removeListDataListener(listDataListener);
  480. }
  481. ListSelectionModel selectionModel = list.getSelectionModel();
  482. if (selectionModel != null) {
  483. selectionModel.removeListSelectionListener(listSelectionListener);
  484. }
  485. focusListener = null;
  486. mouseInputListener = null;
  487. listSelectionListener = null;
  488. listDataListener = null;
  489. propertyChangeListener = null;
  490. keyListener = null;
  491. }
  492. /**
  493. * Initialize JList properties, e.g. font, foreground, and background,
  494. * and add the CellRendererPane. The font, foreground, and background
  495. * properties are only set if their current value is either null
  496. * or a UIResource, other properties are set if the current
  497. * value is null.
  498. *
  499. * @see #uninstallDefaults
  500. * @see #installUI
  501. * @see CellRendererPane
  502. */
  503. protected void installDefaults() {
  504. columnCount = 1;
  505. list.setLayout(null);
  506. fetchStyle(list);
  507. if (list.getCellRenderer() == null) {
  508. list.setCellRenderer(new DefaultListCellRenderer.UIResource());
  509. }
  510. TransferHandler th = list.getTransferHandler();
  511. if (th == null || th instanceof UIResource) {
  512. list.setTransferHandler(defaultTransferHandler);
  513. }
  514. DropTarget dropTarget = list.getDropTarget();
  515. if (dropTarget instanceof UIResource) {
  516. try {
  517. dropTarget.addDropTargetListener(new ListDropTargetListener());
  518. } catch (TooManyListenersException tmle) {
  519. // should not happen... swing drop target is multicast
  520. }
  521. }
  522. }
  523. private void fetchStyle(JComponent c) {
  524. SynthContext context = getContext(list, ENABLED);
  525. SynthStyle oldStyle = style;
  526. style = SynthLookAndFeel.updateStyle(context, this);
  527. if (style != oldStyle) {
  528. context.setComponentState(SELECTED);
  529. Color sbg = list.getSelectionBackground();
  530. if (sbg == null || sbg instanceof UIResource) {
  531. list.setSelectionBackground(style.getColor(
  532. context, ColorType.TEXT_BACKGROUND));
  533. }
  534. Color sfg = list.getSelectionForeground();
  535. if (sfg == null || sfg instanceof UIResource) {
  536. list.setSelectionForeground(style.getColor(
  537. context, ColorType.TEXT_FOREGROUND));
  538. }
  539. }
  540. context.dispose();
  541. }
  542. /**
  543. * Set the JList properties that haven't been explicitly overridden to
  544. * null. A property is considered overridden if its current value
  545. * is not a UIResource.
  546. *
  547. * @see #installDefaults
  548. * @see #uninstallUI
  549. * @see CellRendererPane
  550. */
  551. protected void uninstallDefaults() {
  552. if (list.getSelectionBackground() instanceof UIResource) {
  553. list.setSelectionBackground(null);
  554. }
  555. if (list.getSelectionForeground() instanceof UIResource) {
  556. list.setSelectionForeground(null);
  557. }
  558. if (list.getCellRenderer() instanceof UIResource) {
  559. list.setCellRenderer(null);
  560. }
  561. if (list.getTransferHandler() instanceof UIResource) {
  562. list.setTransferHandler(null);
  563. }
  564. SynthContext context = getContext(list, ENABLED);
  565. style.uninstallDefaults(context);
  566. // Uninstall any class specific state that was installed in
  567. // fetchStyle here.
  568. context.dispose();
  569. style = null;
  570. }
  571. /**
  572. * Initializes <code>this.list</code> by calling <code>installDefaults()</code>,
  573. * <code>installListeners()</code>, and <code>installKeyboardActions()</code>
  574. * in order.
  575. *
  576. * @see #installDefaults
  577. * @see #installListeners
  578. * @see #installKeyboardActions
  579. */
  580. public void installUI(JComponent c)
  581. {
  582. list = (JList)c;
  583. layoutOrientation = list.getLayoutOrientation();
  584. rendererPane = new CellRendererPane();
  585. list.add(rendererPane);
  586. installDefaults();
  587. installListeners();
  588. installKeyboardActions();
  589. }
  590. /**
  591. * Uninitializes <code>this.list</code> by calling <code>uninstallListeners()</code>,
  592. * <code>uninstallKeyboardActions()</code>, and <code>uninstallDefaults()</code>
  593. * in order. Sets this.list to null.
  594. *
  595. * @see #uninstallListeners
  596. * @see #uninstallKeyboardActions
  597. * @see #uninstallDefaults
  598. */
  599. public void uninstallUI(JComponent c)
  600. {
  601. uninstallListeners();
  602. uninstallDefaults();
  603. uninstallKeyboardActions();
  604. cellWidth = cellHeight = -1;
  605. cellHeights = null;
  606. listWidth = listHeight = -1;
  607. list.remove(rendererPane);
  608. rendererPane = null;
  609. list = null;
  610. }
  611. public SynthContext getContext(JComponent c) {
  612. return getContext(c, getComponentState(c));
  613. }
  614. private SynthContext getContext(JComponent c, int state) {
  615. return SynthContext.getContext(SynthContext.class, c,
  616. SynthLookAndFeel.getRegion(c), style, state);
  617. }
  618. private Region getRegion(JComponent c) {
  619. return SynthLookAndFeel.getRegion(c);
  620. }
  621. private int getComponentState(JComponent c) {
  622. return SynthLookAndFeel.getComponentState(c);
  623. }
  624. /**
  625. * Returns a new instance of BasicListUI. BasicListUI delegates are
  626. * allocated one per JList.
  627. *
  628. * @return A new ListUI implementation for the Windows look and feel.
  629. */
  630. public static ComponentUI createUI(JComponent list) {
  631. return new SynthListUI();
  632. }
  633. /**
  634. * Convert a point in <code>JList</code> coordinates to the closest index
  635. * of the cell at that location. To determine if the cell actually
  636. * contains the specified location use a combination of this method and
  637. * <code>getCellBounds</code>. Returns -1 if the model is empty.
  638. *
  639. * @return The index of the cell at location, or -1.
  640. * @see ListUI#locationToIndex
  641. */
  642. public int locationToIndex(JList list, Point location) {
  643. maybeUpdateLayoutState();
  644. return convertLocationToModel(location.x, location.y);
  645. }
  646. /**
  647. * @return The origin of the index'th cell, null if index is invalid.
  648. * @see ListUI#indexToLocation
  649. */
  650. public Point indexToLocation(JList list, int index) {
  651. maybeUpdateLayoutState();
  652. Rectangle rect = getCellBounds(list, index, index);
  653. if (rect != null) {
  654. return new Point(rect.x, rect.y);
  655. }
  656. return null;
  657. }
  658. /**
  659. * @return The bounds of the index'th cell.
  660. * @see ListUI#getCellBounds
  661. */
  662. public Rectangle getCellBounds(JList list, int index1, int index2) {
  663. maybeUpdateLayoutState();
  664. int minIndex = Math.min(index1, index2);
  665. int maxIndex = Math.max(index1, index2);
  666. if (minIndex >= list.getModel().getSize()) {
  667. return null;
  668. }
  669. Rectangle minBounds = getCellBounds(list, minIndex);
  670. if (minBounds == null) {
  671. return null;
  672. }
  673. if (minIndex == maxIndex) {
  674. return minBounds;
  675. }
  676. Rectangle maxBounds = getCellBounds(list, maxIndex);
  677. if (maxBounds != null) {
  678. if (layoutOrientation == JList.HORIZONTAL_WRAP) {
  679. int minRow = convertModelToRow(minIndex);
  680. int maxRow = convertModelToRow(maxIndex);
  681. if (minRow != maxRow) {
  682. minBounds.x = 0;
  683. minBounds.width = list.getWidth();
  684. }
  685. }
  686. else if (minBounds.x != maxBounds.x) {
  687. // Different columns
  688. minBounds.y = 0;
  689. minBounds.height = list.getHeight();
  690. }
  691. minBounds.add(maxBounds);
  692. }
  693. return minBounds;
  694. }
  695. /**
  696. * Gets the bounds of the specified model index, returning the resulting
  697. * bounds, or null if <code>index</code> is not valid.
  698. */
  699. private Rectangle getCellBounds(JList list, int index) {
  700. maybeUpdateLayoutState();
  701. int row = convertModelToRow(index);
  702. int column = convertModelToColumn(index);
  703. if (row == -1 || column == -1) {
  704. return null;
  705. }
  706. Insets insets = list.getInsets();
  707. int x;
  708. int w = cellWidth;
  709. int y = insets.top;
  710. int h;
  711. switch (layoutOrientation) {
  712. case JList.VERTICAL_WRAP:
  713. case JList.HORIZONTAL_WRAP:
  714. if (list.getComponentOrientation().isLeftToRight()) {
  715. x = insets.left + column * cellWidth;
  716. } else {
  717. x = list.getWidth() - insets.right - (column+1) * cellWidth;
  718. }
  719. y += cellHeight * row;
  720. h = cellHeight;
  721. break;
  722. default:
  723. x = insets.left;
  724. if (cellHeights == null) {
  725. y += (cellHeight * row);
  726. }
  727. else if (row >= cellHeights.length) {
  728. y = 0;
  729. }
  730. else {
  731. for(int i = 0; i < row; i++) {
  732. y += cellHeights[i];
  733. }
  734. }
  735. w = list.getWidth() - (insets.left + insets.right);
  736. h = getRowHeight(index);
  737. break;
  738. }
  739. return new Rectangle(x, y, w, h);
  740. }
  741. // PENDING(hmuller) explain the cell geometry abstraction in
  742. // the getRowHeight javadoc
  743. /**
  744. * Returns the height of the specified row based on the current layout.
  745. *
  746. * @return The specified row height or -1 if row isn't valid.
  747. * @see #convertYToRow
  748. * @see #convertRowToY
  749. * @see #updateLayoutState
  750. */
  751. protected int getRowHeight(int row)
  752. {
  753. return getHeight(0, row);
  754. }
  755. /**
  756. * Convert the JList relative coordinate to the row that contains it,
  757. * based on the current layout. If y0 doesn't fall within any row,
  758. * return -1.
  759. *
  760. * @return The row that contains y0, or -1.
  761. * @see #getRowHeight
  762. * @see #updateLayoutState
  763. */
  764. protected int convertYToRow(int y0)
  765. {
  766. return convertLocationToRow(0, y0, false);
  767. }
  768. /**
  769. * Return the JList relative Y coordinate of the origin of the specified
  770. * row or -1 if row isn't valid.
  771. *
  772. * @return The Y coordinate of the origin of row, or -1.
  773. * @see #getRowHeight
  774. * @see #updateLayoutState
  775. */
  776. protected int convertRowToY(int row)
  777. {
  778. if (getRowCount(0) >= row || row < 0) {
  779. return -1;
  780. }
  781. Rectangle bounds = getCellBounds(list, row, row);
  782. return bounds.y;
  783. }
  784. /**
  785. * Returns the height of the cell at the passed in location.
  786. */
  787. private int getHeight(int column, int row) {
  788. if (column < 0 || column > columnCount || row < 0) {
  789. return -1;
  790. }
  791. if (layoutOrientation != JList.VERTICAL) {
  792. return cellHeight;
  793. }
  794. if (row >= list.getModel().getSize()) {
  795. return -1;
  796. }
  797. return (cellHeights == null) ? cellHeight :
  798. ((row < cellHeights.length) ? cellHeights[row] : -1);
  799. }
  800. /**
  801. * Returns the row at location x/y.
  802. *
  803. * @param closest If true and the location doesn't exactly match a
  804. * particular location, this will return the closest row.
  805. */
  806. private int convertLocationToRow(int x, int y0, boolean closest) {
  807. int size = list.getModel().getSize();
  808. if (size <= 0) {
  809. return -1;
  810. }
  811. Insets insets = list.getInsets();
  812. if (cellHeights == null) {
  813. int row = (cellHeight == 0) ? 0 :
  814. ((y0 - insets.top) / cellHeight);
  815. if (closest) {
  816. if (row < 0) {
  817. row = 0;
  818. }
  819. else if (row >= size) {
  820. row = size - 1;
  821. }
  822. }
  823. return row;
  824. }
  825. else if (size > cellHeights.length) {
  826. return -1;
  827. }
  828. else {
  829. int y = insets.top;
  830. int row = 0;
  831. if (closest && y0 < y) {
  832. return 0;
  833. }
  834. int i;
  835. for (i = 0; i < size; i++) {
  836. if ((y0 >= y) && (y0 < y + cellHeights[i])) {
  837. return row;
  838. }
  839. y += cellHeights[i];
  840. row += 1;
  841. }
  842. return i - 1;
  843. }
  844. }
  845. /**
  846. * Returns the closest row that starts at the specified y-location
  847. * in the passed in column.
  848. */
  849. private int convertLocationToRowInColumn(int y, int column) {
  850. int x = 0;
  851. if (layoutOrientation != JList.VERTICAL) {
  852. if (list.getComponentOrientation().isLeftToRight()) {
  853. x = column * cellWidth;
  854. } else {
  855. x = list.getWidth() - (column+1)*cellWidth - list.getInsets().right;
  856. }
  857. }
  858. return convertLocationToRow(x, y, true);
  859. }
  860. /**
  861. * Returns the closest location to the model index of the passed in
  862. * location.
  863. */
  864. private int convertLocationToModel(int x, int y) {
  865. int row = convertLocationToRow(x, y, true);
  866. int column = convertLocationToColumn(x, y);
  867. if (row >= 0 && column >= 0) {
  868. return getModelIndex(column, row);
  869. }
  870. return -1;
  871. }
  872. /**
  873. * Returns the number of rows in the given column.
  874. */
  875. private int getRowCount(int column) {
  876. if (column < 0 || column >= columnCount) {
  877. return -1;
  878. }
  879. if (layoutOrientation == JList.VERTICAL ||
  880. (column == 0 && columnCount == 1)) {
  881. return list.getModel().getSize();
  882. }
  883. if (column >= columnCount) {
  884. return -1;
  885. }
  886. if (layoutOrientation == JList.VERTICAL_WRAP) {
  887. if (column < (columnCount - 1)) {
  888. return rowsPerColumn;
  889. }
  890. return list.getModel().getSize() - (columnCount - 1) *
  891. rowsPerColumn;
  892. }
  893. // JList.HORIZONTAL_WRAP
  894. int diff = columnCount - (columnCount * rowsPerColumn -
  895. list.getModel().getSize());
  896. if (column >= diff) {
  897. return Math.max(0, rowsPerColumn - 1);
  898. }
  899. return rowsPerColumn;
  900. }
  901. /**
  902. * Returns the model index for the specified display location.
  903. * If <code>column</code>x<code>row</code> is beyond the length of the
  904. * model, this will return the model size - 1.
  905. */
  906. private int getModelIndex(int column, int row) {
  907. switch (layoutOrientation) {
  908. case JList.VERTICAL_WRAP:
  909. return Math.min(list.getModel().getSize() - 1, rowsPerColumn *
  910. column + Math.min(row, rowsPerColumn-1));
  911. case JList.HORIZONTAL_WRAP:
  912. return Math.min(list.getModel().getSize() - 1, row * columnCount +
  913. column);
  914. default:
  915. return row;
  916. }
  917. }
  918. /**
  919. * Returns the closest column to the passed in location.
  920. */
  921. private int convertLocationToColumn(int x, int y) {
  922. if (cellWidth > 0) {
  923. if (layoutOrientation == JList.VERTICAL) {
  924. return 0;
  925. }
  926. Insets insets = list.getInsets();
  927. int col;
  928. if (list.getComponentOrientation().isLeftToRight()) {
  929. col = (x - insets.left) / cellWidth;
  930. } else {
  931. col = (list.getWidth() - x - insets.right) / cellWidth;
  932. }
  933. if (col < 0) {
  934. return 0;
  935. }
  936. else if (col >= columnCount) {
  937. return columnCount - 1;
  938. }
  939. return col;
  940. }
  941. return 0;
  942. }
  943. /**
  944. * Returns the row that the model index <code>index</code> will be
  945. * displayed in..
  946. */
  947. private int convertModelToRow(int index) {
  948. int size = list.getModel().getSize();
  949. if ((index < 0) || (index >= size)) {
  950. return -1;
  951. }
  952. if (layoutOrientation != JList.VERTICAL && columnCount > 1 &&
  953. rowsPerColumn > 0) {
  954. if (layoutOrientation == JList.VERTICAL_WRAP) {
  955. return index % rowsPerColumn;
  956. }
  957. return index / columnCount;
  958. }
  959. return index;
  960. }
  961. /**
  962. * Returns the column that the model index <code>index</code> will be
  963. * displayed in.
  964. */
  965. private int convertModelToColumn(int index) {
  966. int size = list.getModel().getSize();
  967. if ((index < 0) || (index >= size)) {
  968. return -1;
  969. }
  970. if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 &&
  971. columnCount > 1) {
  972. if (layoutOrientation == JList.VERTICAL_WRAP) {
  973. return index / rowsPerColumn;
  974. }
  975. return index % columnCount;
  976. }
  977. return 0;
  978. }
  979. /**
  980. * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset
  981. * updateLayoutStateNeeded. This method should be called by methods
  982. * before doing any computation based on the geometry of the list.
  983. * For example it's the first call in paint() and getPreferredSize().
  984. *
  985. * @see #updateLayoutState
  986. */
  987. protected void maybeUpdateLayoutState()
  988. {
  989. if (updateLayoutStateNeeded != 0) {
  990. updateLayoutState();
  991. updateLayoutStateNeeded = 0;
  992. }
  993. }
  994. /**
  995. * Recompute the value of cellHeight or cellHeights based
  996. * and cellWidth, based on the current font and the current
  997. * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue.
  998. *
  999. * @see #maybeUpdateLayoutState
  1000. */
  1001. protected void updateLayoutState()
  1002. {
  1003. /* If both JList fixedCellWidth and fixedCellHeight have been
  1004. * set, then initialize cellWidth and cellHeight, and set
  1005. * cellHeights to null.
  1006. */
  1007. int fixedCellHeight = list.getFixedCellHeight();
  1008. int fixedCellWidth = list.getFixedCellWidth();
  1009. cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1;
  1010. if (fixedCellHeight != -1) {
  1011. cellHeight = fixedCellHeight;
  1012. cellHeights = null;
  1013. }
  1014. else {
  1015. cellHeight = -1;
  1016. cellHeights = new int[list.getModel().getSize()];
  1017. }
  1018. /* If either of JList fixedCellWidth and fixedCellHeight haven't
  1019. * been set, then initialize cellWidth and cellHeights by
  1020. * scanning through the entire model. Note: if the renderer is
  1021. * null, we just set cellWidth and cellHeights[*] to zero,
  1022. * if they're not set already.
  1023. */
  1024. if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) {
  1025. ListModel dataModel = list.getModel();
  1026. int dataModelSize = dataModel.getSize();
  1027. ListCellRenderer renderer = list.getCellRenderer();
  1028. if (renderer != null) {
  1029. for(int index = 0; index < dataModelSize; index++) {
  1030. Object value = dataModel.getElementAt(index);
  1031. Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
  1032. rendererPane.add(c);
  1033. Dimension cellSize = c.getPreferredSize();
  1034. if (fixedCellWidth == -1) {
  1035. cellWidth = Math.max(cellSize.width, cellWidth);
  1036. }
  1037. if (fixedCellHeight == -1) {
  1038. cellHeights[index] = cellSize.height;
  1039. }
  1040. }
  1041. }
  1042. else {
  1043. if (cellWidth == -1) {
  1044. cellWidth = 0;
  1045. }
  1046. if (cellHeights == null) {
  1047. cellHeights = new int[dataModelSize];
  1048. }
  1049. for(int index = 0; index < dataModelSize; index++) {
  1050. cellHeights[index] = 0;
  1051. }
  1052. }
  1053. }
  1054. columnCount = 1;
  1055. if (layoutOrientation != JList.VERTICAL) {
  1056. updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight);
  1057. }
  1058. }
  1059. /**
  1060. * Invoked when the list is layed out horizontally to determine how
  1061. * many columns to create.
  1062. * <p>
  1063. * This updates the <code>rowsPerColumn, </code><code>columnCount</code>,
  1064. * <code>preferredHeight</code> and potentially <code>cellHeight</code>
  1065. * instance variables.
  1066. */
  1067. private void updateHorizontalLayoutState(int fixedCellWidth,
  1068. int fixedCellHeight) {
  1069. int visRows = list.getVisibleRowCount();
  1070. int dataModelSize = list.getModel().getSize();
  1071. Insets insets = list.getInsets();
  1072. listHeight = list.getHeight();
  1073. listWidth = list.getWidth();
  1074. if (dataModelSize == 0) {
  1075. rowsPerColumn = columnCount = 0;
  1076. preferredHeight = insets.top + insets.bottom;
  1077. return;
  1078. }
  1079. int height;
  1080. if (fixedCellHeight != -1) {
  1081. height = fixedCellHeight;
  1082. }
  1083. else {
  1084. // Determine the max of the renderer heights.
  1085. int maxHeight = 0;
  1086. if (cellHeights.length > 0) {
  1087. maxHeight = cellHeights[cellHeights.length - 1];
  1088. for (int counter = cellHeights.length - 2;
  1089. counter >= 0; counter--) {
  1090. maxHeight = Math.max(maxHeight, cellHeights[counter]);
  1091. }
  1092. }
  1093. height = cellHeight = maxHeight;
  1094. cellHeights = null;
  1095. }
  1096. // The number of rows is either determined by the visible row
  1097. // count, or by the height of the list.
  1098. rowsPerColumn = dataModelSize;
  1099. if (visRows > 0) {
  1100. rowsPerColumn = visRows;
  1101. columnCount = Math.max(1, dataModelSize / rowsPerColumn);
  1102. if (dataModelSize > 0 && dataModelSize > rowsPerColumn &&
  1103. dataModelSize % rowsPerColumn != 0) {
  1104. columnCount++;
  1105. }
  1106. if (layoutOrientation == JList.HORIZONTAL_WRAP) {
  1107. // Because HORIZONTAL_WRAP flows differently, the
  1108. // rowsPerColumn needs to be adjusted.
  1109. rowsPerColumn = (dataModelSize / columnCount);
  1110. if (dataModelSize % columnCount > 0) {
  1111. rowsPerColumn++;
  1112. }
  1113. }
  1114. }
  1115. else if (layoutOrientation == JList.VERTICAL_WRAP && height != 0) {
  1116. rowsPerColumn = Math.max(1, (listHeight - insets.top -
  1117. insets.bottom) / height);
  1118. columnCount = Math.max(1, dataModelSize / rowsPerColumn);
  1119. if (dataModelSize > 0 && dataModelSize > rowsPerColumn &&
  1120. dataModelSize % rowsPerColumn != 0) {
  1121. columnCount++;
  1122. }
  1123. }
  1124. else if (layoutOrientation == JList.HORIZONTAL_WRAP && cellWidth > 0 &&
  1125. listWidth > 0) {
  1126. columnCount = Math.max(1, (listWidth - insets.left -
  1127. insets.right) / cellWidth);
  1128. rowsPerColumn = dataModelSize / columnCount;
  1129. if (dataModelSize % columnCount > 0) {
  1130. rowsPerColumn++;
  1131. }
  1132. }
  1133. preferredHeight = rowsPerColumn * cellHeight + insets.top +
  1134. insets.bottom;
  1135. }
  1136. /**
  1137. * Mouse input, and focus handling for JList. An instance of this
  1138. * class is added to the appropriate java.awt.Component lists
  1139. * at installUI() time. Note keyboard input is handled with JComponent
  1140. * KeyboardActions, see installKeyboardActions().
  1141. * <p>
  1142. * <strong>Warning:</strong>
  1143. * Serialized objects of this class will not be compatible with
  1144. * future Swing releases. The current serialization support is
  1145. * appropriate for short term storage or RMI between applications running
  1146. * the same version of Swing. As of 1.4, support for long term storage
  1147. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  1148. * has been added to the <code>java.beans</code> package.
  1149. * Please see {@link java.beans.XMLEncoder}.
  1150. *
  1151. * @see #createMouseInputListener
  1152. * @see #installKeyboardActions
  1153. * @see #installUI
  1154. */
  1155. class MouseInputHandler implements MouseInputListener
  1156. {
  1157. public void mouseClicked(MouseEvent e) {}
  1158. public void mouseEntered(MouseEvent e) {}
  1159. public void mouseExited(MouseEvent e) {}
  1160. public void mousePressed(MouseEvent e)
  1161. {
  1162. if (e.isConsumed()) {
  1163. selectedOnPress = false;
  1164. return;
  1165. }
  1166. selectedOnPress = true;
  1167. adjustFocusAndSelection(e);
  1168. }
  1169. void adjustFocusAndSelection(MouseEvent e) {
  1170. if (!SwingUtilities.isLeftMouseButton(e)) {
  1171. return;
  1172. }
  1173. if (!list.isEnabled()) {
  1174. return;
  1175. }
  1176. /* Request focus before updating the list selection. This implies
  1177. * that the current focus owner will see a focusLost() event
  1178. * before the lists selection is updated IF requestFocus() is
  1179. * synchronous (it is on Windows). See bug 4122345
  1180. */
  1181. if (!list.hasFocus() && list.isRequestFocusEnabled()) {
  1182. list.requestFocus();
  1183. }
  1184. int row = convertLocationToModel(e.getX(), e.getY());
  1185. if (row != -1) {
  1186. boolean adjusting = (e.getID() == MouseEvent.MOUSE_PRESSED) ? true : false;
  1187. list.setValueIsAdjusting(adjusting);
  1188. int anchorIndex = list.getAnchorSelectionIndex();
  1189. if (e.isControlDown()) {
  1190. if (list.isSelectedIndex(row)) {
  1191. list.removeSelectionInterval(row, row);
  1192. }
  1193. else {
  1194. list.addSelectionInterval(row, row);
  1195. }
  1196. }
  1197. else if (e.isShiftDown() && (anchorIndex != -1)) {
  1198. list.setSelectionInterval(anchorIndex, row);
  1199. }
  1200. else {
  1201. list.setSelectionInterval(row, row);
  1202. }
  1203. }
  1204. }
  1205. public void mouseDragged(MouseEvent e) {
  1206. if (e.isConsumed()) {
  1207. return;
  1208. }
  1209. if (!SwingUtilities.isLeftMouseButton(e)) {
  1210. return;
  1211. }
  1212. if (!list.isEnabled()) {
  1213. return;
  1214. }
  1215. if (e.isShiftDown() || e.isControlDown()) {
  1216. return;
  1217. }
  1218. int row = convertLocationToModel(e.getX(), e.getY());
  1219. if (row != -1) {
  1220. Rectangle cellBounds = getCellBounds(list, row, row);
  1221. if (cellBounds != null) {
  1222. list.scrollRectToVisible(cellBounds);
  1223. list.setSelectionInterval(row, row);
  1224. }
  1225. }
  1226. }
  1227. public void mouseMoved(MouseEvent e) {
  1228. }
  1229. public void mouseReleased(MouseEvent e) {
  1230. if (selectedOnPress) {
  1231. if (!SwingUtilities.isLeftMouseButton(e)) {
  1232. return;
  1233. }
  1234. list.setValueIsAdjusting(false);
  1235. } else {
  1236. adjustFocusAndSelection(e);
  1237. }
  1238. }
  1239. private boolean selectedOnPress;
  1240. }
  1241. /**
  1242. * Creates a delegate that implements MouseInputListener.
  1243. * The delegate is added to the corresponding java.awt.Component listener
  1244. * lists at installUI() time. Subclasses can override this method to return
  1245. * a custom MouseInputListener, e.g.
  1246. * <pre>
  1247. * class MyListUI extends BasicListUI {
  1248. * protected MouseInputListener <b>createMouseInputListener</b>() {
  1249. * return new MyMouseInputHandler();
  1250. * }
  1251. * class MyMouseInputHandler extends MouseInputHandler {
  1252. * public void mouseMoved(MouseEvent e) {
  1253. * // do some extra work when the mouse moves
  1254. * super.mouseMoved(e);
  1255. * }
  1256. * }
  1257. * }
  1258. * </pre>
  1259. *
  1260. * @see MouseInputHandler
  1261. * @see #installUI
  1262. */
  1263. protected MouseInputListener createMouseInputListener() {
  1264. return new MouseInputHandler();
  1265. }
  1266. /**
  1267. * This inner class is marked "public" due to a compiler bug.
  1268. * This class should be treated as a "protected" inner class.
  1269. * Instantiate it only within subclasses of BasicTableUI.
  1270. */
  1271. class FocusHandler implements FocusListener
  1272. {
  1273. protected void repaintCellFocus()
  1274. {
  1275. int leadIndex = list.getLeadSelectionIndex();
  1276. if (leadIndex != -1) {
  1277. Rectangle r = getCellBounds(list, leadIndex, leadIndex);
  1278. if (r != null) {
  1279. list.repaint(r.x, r.y, r.width, r.height);
  1280. }
  1281. }
  1282. }
  1283. /* The focusGained() focusLost() methods run when the JList
  1284. * focus changes.
  1285. */
  1286. public void focusGained(FocusEvent e) {
  1287. // hasFocus = true;
  1288. repaintCellFocus();
  1289. }
  1290. public void focusLost(FocusEvent e) {
  1291. // hasFocus = false;
  1292. repaintCellFocus();
  1293. }
  1294. }
  1295. protected FocusListener createFocusListener() {
  1296. return new FocusHandler();
  1297. }
  1298. /**
  1299. * The ListSelectionListener that's added to the JLists selection
  1300. * model at installUI time, and whenever the JList.selectionModel property
  1301. * changes. When the selection changes we repaint the affected rows.
  1302. * <p>
  1303. * <strong>Warning:</strong>
  1304. * Serialized objects of this class will not be compatible with
  1305. * future Swing releases. The current serialization support is
  1306. * appropriate for short term storage or RMI between applications running
  1307. * the same version of Swing. As of 1.4, support for long term storage
  1308. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  1309. * has been added to the <code>java.beans</code> package.
  1310. * Please see {@link java.beans.XMLEncoder}.
  1311. *
  1312. * @see #createListSelectionListener
  1313. * @see #getCellBounds
  1314. * @see #installUI
  1315. */
  1316. class ListSelectionHandler implements ListSelectionListener
  1317. {
  1318. public void valueChanged(ListSelectionEvent e)
  1319. {
  1320. maybeUpdateLayoutState();
  1321. Rectangle bounds = getCellBounds(list, e.getFirstIndex(),
  1322. e.getLastIndex());
  1323. if (bounds != null) {
  1324. list.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
  1325. }
  1326. }
  1327. }
  1328. /**
  1329. * Creates an instance of ListSelectionHandler that's added to
  1330. * the JLists by selectionModel as needed. Subclasses can override
  1331. * this method to return a custom ListSelectionListener, e.g.
  1332. * <pre>
  1333. * class MyListUI extends BasicListUI {
  1334. * protected ListSelectionListener <b>createListSelectionListener</b>() {
  1335. * return new MySelectionListener();
  1336. * }
  1337. * class MySelectionListener extends ListSelectionHandler {
  1338. * public void valueChanged(ListSelectionEvent e) {
  1339. * // do some extra work when the selection changes
  1340. * super.valueChange(e);
  1341. * }
  1342. * }
  1343. * }
  1344. * </pre>
  1345. *
  1346. * @see ListSelectionHandler
  1347. * @see #installUI
  1348. */
  1349. protected ListSelectionListener createListSelectionListener() {
  1350. return new ListSelectionHandler();
  1351. }
  1352. private void redrawList() {
  1353. list.revalidate();
  1354. list.repaint();
  1355. }
  1356. /**
  1357. * The ListDataListener that's added to the JLists model at
  1358. * installUI time, and whenever the JList.model property changes.
  1359. * <p>
  1360. * <strong>Warning:</strong>
  1361. * Serialized objects of this class will not be compatible with
  1362. * future Swing releases. The current serialization support is
  1363. * appropriate for short term storage or RMI between applications running
  1364. * the same version of Swing. As of 1.4, support for long term storage
  1365. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  1366. * has been added to the <code>java.beans</code> package.
  1367. * Please see {@link java.beans.XMLEncoder}.
  1368. *
  1369. * @see JList#getModel
  1370. * @see #maybeUpdateLayoutState
  1371. * @see #createListDataListener
  1372. * @see #installUI
  1373. */
  1374. class ListDataHandler implements ListDataListener
  1375. {
  1376. public void intervalAdded(ListDataEvent e) {
  1377. updateLayoutStateNeeded = modelChanged;
  1378. int minIndex = Math.min(e.getIndex0(), e.getIndex1());
  1379. int maxIndex = Math.max(e.getIndex0(), e.getIndex1());
  1380. /* Sync the SelectionModel with the DataModel.
  1381. */
  1382. ListSelectionModel sm = list.getSelectionModel();
  1383. if (sm != null) {
  1384. sm.insertIndexInterval(minIndex, maxIndex - minIndex+1, true);
  1385. }
  1386. /* Repaint the entire list, from the origin of
  1387. * the first added cell, to the bottom of the
  1388. * component.
  1389. */
  1390. redrawList();
  1391. }
  1392. public void intervalRemoved(ListDataEvent e)
  1393. {
  1394. updateLayoutStateNeeded = modelChanged;
  1395. /* Sync the SelectionModel with the DataModel.
  1396. */
  1397. ListSelectionModel sm = list.getSelectionModel();
  1398. if (sm != null) {
  1399. sm.removeIndexInterval(e.getIndex0(), e.getIndex1());
  1400. }
  1401. /* Repaint the entire list, from the origin of
  1402. * the first removed cell, to the bottom of the
  1403. * component.
  1404. */
  1405. redrawList();
  1406. }
  1407. public void contentsChanged(ListDataEvent e) {
  1408. updateLayoutStateNeeded = modelChanged;
  1409. redrawList();
  1410. }
  1411. }
  1412. /**
  1413. * Creates an instance of ListDataListener that's added to
  1414. * the JLists by model as needed. Subclasses can override
  1415. * this method to return a custom ListDataListener, e.g.
  1416. * <pre>
  1417. * class MyListUI extends BasicListUI {
  1418. * protected ListDataListener <b>createListDataListener</b>() {
  1419. * return new MyListDataListener();
  1420. * }
  1421. * class MyListDataListener extends ListDataHandler {
  1422. * public void contentsChanged(ListDataEvent e) {
  1423. * // do some extra work when the models contents change
  1424. * super.contentsChange(e);
  1425. * }
  1426. * }
  1427. * }
  1428. * </pre>
  1429. *
  1430. * @see ListDataListener
  1431. * @see JList#getModel
  1432. * @see #installUI
  1433. */
  1434. protected ListDataListener createListDataListener() {
  1435. return new ListDataHandler();
  1436. }
  1437. /**
  1438. * The PropertyChangeListener that's added to the JList at
  1439. * installUI time. When the value of a JList property that
  1440. * affects layout changes, we set a bit in updateLayoutStateNeeded.
  1441. * If the JLists model changes we additionally remove our listeners
  1442. * from the old model. Likewise for the JList selectionModel.
  1443. * <p>
  1444. * <strong>Warning:</strong>
  1445. * Serialized objects of this class will not be compatible with
  1446. * future Swing releases. The current serialization support is
  1447. * appropriate for short term storage or RMI between applications running
  1448. * the same version of Swing. As of 1.4, support for long term storage
  1449. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  1450. * has been added to the <code>java.beans</code> package.
  1451. * Please see {@link java.beans.XMLEncoder}.
  1452. *
  1453. * @see #maybeUpdateLayoutState
  1454. * @see #createPropertyChangeListener
  1455. * @see #installUI
  1456. */
  1457. class PropertyChangeHandler implements PropertyChangeListener
  1458. {
  1459. public void propertyChange(PropertyChangeEvent e)
  1460. {
  1461. String propertyName = e.getPropertyName();
  1462. if (SynthLookAndFeel.shouldUpdateStyle(e)) {
  1463. fetchStyle((JList)e.getSource());
  1464. }
  1465. /* If the JList.model property changes, remove our listener,
  1466. * listDataListener from the old model and add it to the new one.
  1467. */
  1468. if (propertyName.equals("model")) {
  1469. ListModel oldModel = (ListModel)e.getOldValue();
  1470. ListModel newModel = (ListModel)e.getNewValue();
  1471. if (oldModel != null) {
  1472. oldModel.removeListDataListener(listDataListener);
  1473. }
  1474. if (newModel != null) {
  1475. newModel.addListDataListener(listDataListener);
  1476. }
  1477. updateLayoutStateNeeded |= modelChanged;
  1478. redrawList();
  1479. }
  1480. /* If the JList.selectionModel property changes, remove our listener,
  1481. * listSelectionListener from the old selectionModel and add it to the new one.
  1482. */
  1483. else if (propertyName.equals("selectionModel")) {
  1484. ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue();
  1485. ListSelectionModel newModel = (ListSelectionModel)e.getNewValue();
  1486. if (oldModel != null) {
  1487. oldModel.removeListSelectionListener(listSelectionListener);
  1488. }
  1489. if (newModel != null) {
  1490. newModel.addListSelectionListener(listSelectionListener);
  1491. }
  1492. updateLayoutStateNeeded |= modelChanged;
  1493. redrawList();
  1494. }
  1495. else if (propertyName.equals("cellRenderer")) {
  1496. updateLayoutStateNeeded |= cellRendererChanged;
  1497. redrawList();
  1498. }
  1499. else if (propertyName.equals("font")) {
  1500. updateLayoutStateNeeded |= fontChanged;
  1501. redrawList();
  1502. }
  1503. else if (propertyName.equals("prototypeCellValue")) {
  1504. updateLayoutStateNeeded |= prototypeCellValueChanged;
  1505. redrawList();
  1506. }
  1507. else if (propertyName.equals("fixedCellHeight")) {
  1508. updateLayoutStateNeeded |= fixedCellHeightChanged;
  1509. redrawList();
  1510. }
  1511. else if (propertyName.equals("fixedCellWidth")) {
  1512. updateLayoutStateNeeded |= fixedCellWidthChanged;
  1513. redrawList();
  1514. }
  1515. else if (propertyName.equals("cellRenderer")) {
  1516. updateLayoutStateNeeded |= cellRendererChanged;
  1517. redrawList();
  1518. }
  1519. else if (propertyName.equals("selectionForeground")) {
  1520. list.repaint();
  1521. }
  1522. else if (propertyName.equals("selectionBackground")) {
  1523. list.repaint();
  1524. }
  1525. else if ("layoutOrientation".equals(propertyName)) {
  1526. updateLayoutStateNeeded |= layoutOrientationChanged;
  1527. layoutOrientation = list.getLayoutOrientation();
  1528. redrawList();
  1529. }
  1530. else if ("visibleRowCount".equals(propertyName)) {
  1531. if (layoutOrientation != JList.VERTICAL) {
  1532. updateLayoutStateNeeded |= layoutOrientationChanged;
  1533. redrawList();
  1534. }
  1535. }
  1536. else if ("componentOrientation".equals(propertyName)) {
  1537. updateLayoutStateNeeded |= componentOrientationChanged;
  1538. redrawList();
  1539. InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
  1540. SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
  1541. inputMap);
  1542. } else if ("transferHandler".equals(propertyName)) {
  1543. DropTarget dropTarget = list.getDropTarget();
  1544. if (dropTarget instanceof UIResource) {
  1545. try {
  1546. dropTarget.addDropTargetListener(new ListDropTargetListener());
  1547. } catch (TooManyListenersException tmle) {
  1548. // should not happen... swing drop target is multicast
  1549. }
  1550. }
  1551. }
  1552. }
  1553. }
  1554. /**
  1555. * Creates an instance of PropertyChangeHandler that's added to
  1556. * the JList by installUI(). Subclasses can override this method
  1557. * to return a custom PropertyChangeListener, e.g.
  1558. * <pre>
  1559. * class MyListUI extends BasicListUI {
  1560. * protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
  1561. * return new MyPropertyChangeListener();
  1562. * }
  1563. * class MyPropertyChangeListener extends PropertyChangeHandler {
  1564. * public void propertyChange(PropertyChangeEvent e) {
  1565. * if (e.getPropertyName().equals("model")) {
  1566. * // do some extra work when the model changes
  1567. * }
  1568. * super.propertyChange(e);
  1569. * }
  1570. * }
  1571. * }
  1572. * </pre>
  1573. *
  1574. * @see PropertyChangeListener
  1575. * @see #installUI
  1576. */
  1577. protected PropertyChangeListener createPropertyChangeListener() {
  1578. return new PropertyChangeHandler();
  1579. }
  1580. /**
  1581. * Creates an instance of KeyHandler that's added to
  1582. * the JList by installUI().
  1583. */
  1584. private KeyListener createKeyListener() {
  1585. return new KeyHandler();
  1586. }
  1587. private static class KeyHandler implements KeyListener {
  1588. private String prefix = "";
  1589. private long lastTime = 0L;
  1590. /**
  1591. * Invoked when a key has been typed.
  1592. *
  1593. * Moves the keyboard focus to the first element
  1594. * whose first letter matches the alphanumeric key
  1595. * pressed by the user. Subsequent same key presses
  1596. * move the keyboard focus to the next object that
  1597. * starts with the same letter.
  1598. */
  1599. public void keyTyped(KeyEvent e) {
  1600. JList src = (JList)e.getSource();
  1601. ListModel model = src.getModel();
  1602. if (model.getSize() == 0 || e.isAltDown() || e.isControlDown() || e.isMetaDown()) {
  1603. // Nothing to select
  1604. return;
  1605. }
  1606. boolean startingFromSelection = true;
  1607. char c = e.getKeyChar();
  1608. long time = e.getWhen();
  1609. int startIndex;
  1610. if (time - lastTime < 1000L && (prefix.length() != 1 || c != prefix.charAt(0))) {
  1611. prefix += c;
  1612. startIndex = src.getSelectedIndex();
  1613. } else {
  1614. prefix = "" + c;
  1615. startIndex = src.getSelectedIndex() + 1;
  1616. }
  1617. lastTime = time;
  1618. if (startIndex < 0 || startIndex >= model.getSize()) {
  1619. startingFromSelection = false;
  1620. startIndex = 0;
  1621. }
  1622. int index = src.getNextMatch(prefix, startIndex,
  1623. Position.Bias.Forward);
  1624. if (index >= 0) {
  1625. src.setSelectedIndex(index);
  1626. } else if (startingFromSelection) { // wrap
  1627. index = src.getNextMatch(prefix, 0,
  1628. Position.Bias.Forward);
  1629. if (index >= 0) {
  1630. src.setSelectedIndex(index);
  1631. }
  1632. }
  1633. }
  1634. /**
  1635. * Invoked when a key has been pressed.
  1636. */
  1637. public void keyPressed(KeyEvent e) {
  1638. }
  1639. /**
  1640. * Invoked when a key has been released.
  1641. * See the class description for {@link KeyEvent} for a definition of
  1642. * a key released event.
  1643. */
  1644. public void keyReleased(KeyEvent e) {
  1645. }
  1646. }
  1647. // Keyboard navigation actions.
  1648. // NOTE: DefaultListSelectionModel.setAnchorSelectionIndex and
  1649. // DefaultListSelectionModel.setLeadSelectionIndex both force the
  1650. // new index to be selected. Because of this not all the bindings
  1651. // could be appropriately implemented. Specifically those that
  1652. // change the lead/anchor without selecting are not enabled.
  1653. // Once this has been fixed the following actions will appropriately
  1654. // work with selectionType == CHANGE_LEAD.
  1655. /** Used by IncrementLeadSelectionAction. Indicates the action should
  1656. * change the lead, and not select it. */
  1657. private static final int CHANGE_LEAD = 0;
  1658. /** Used by IncrementLeadSelectionAction. Indicates the action should
  1659. * change the selection and lead. */
  1660. private static final int CHANGE_SELECTION = 1;
  1661. /** Used by IncrementLeadSelectionAction. Indicates the action should
  1662. * extend the selection from the anchor to the next index. */
  1663. private static final int EXTEND_SELECTION = 2;
  1664. /**
  1665. * Action to increment the selection in the list up/down a row at
  1666. * a type. This also has the option to extend the selection, or
  1667. * only move the lead.
  1668. */
  1669. private static class IncrementLeadSelectionAction extends AbstractAction {
  1670. /** Amount to offset, subclasses will define what this means. */
  1671. protected int amount;
  1672. /** One of CHANGE_LEAD, CHANGE_SELECTION or EXTEND_SELECTION. */
  1673. protected int selectionType;
  1674. protected IncrementLeadSelectionAction(String name, int type) {
  1675. this(name, type, -1);
  1676. }
  1677. protected IncrementLeadSelectionAction(String name, int type,
  1678. int amount) {
  1679. super(name);
  1680. this.amount = amount;
  1681. this.selectionType = type;
  1682. }
  1683. /**
  1684. * Returns the next index to select. This is based on the lead
  1685. * selected index and the <code>amount</code> ivar.
  1686. */
  1687. protected int getNextIndex(JList list) {
  1688. int index = list.getLeadSelectionIndex();
  1689. int size = list.getModel().getSize();
  1690. if (index == -1) {
  1691. if (size > 0) {
  1692. if (amount > 0) {
  1693. index = 0;
  1694. }
  1695. else {
  1696. index = size - 1;
  1697. }
  1698. }
  1699. }
  1700. else {
  1701. index += getAmount(list);
  1702. }
  1703. return index;
  1704. }
  1705. /**
  1706. * Returns the amount to increment by.
  1707. */
  1708. protected int getAmount(JList list) {
  1709. if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
  1710. ListUI ui = list.getUI();
  1711. // PENDING:
  1712. if (ui instanceof SynthListUI) {
  1713. return ((SynthListUI)ui).columnCount * amount;
  1714. }
  1715. }
  1716. return amount;
  1717. }
  1718. /**
  1719. * Ensures the particular index is visible. This simply forwards
  1720. * the method to list.
  1721. */
  1722. protected void ensureIndexIsVisible(JList list, int index) {
  1723. list.ensureIndexIsVisible(index);
  1724. }
  1725. /**
  1726. * Invokes <code>getNextIndex</code> to determine the next index
  1727. * to select. If the index is valid (not -1 and < size of the model),
  1728. * this will either: move the selection to the new index if
  1729. * the selectionType == CHANGE_SELECTION, move the lead to the
  1730. * new index if selectionType == CHANGE_LEAD, otherwise the
  1731. * selection is extend from the anchor to the new index and the
  1732. * lead is set to the new index.
  1733. */
  1734. public void actionPerformed(ActionEvent e) {
  1735. JList list = (JList)e.getSource();
  1736. int index = getNextIndex(list);
  1737. if (index >= 0 && index < list.getModel().getSize()) {
  1738. ListSelectionModel lsm = list.getSelectionModel();
  1739. if (selectionType == EXTEND_SELECTION) {
  1740. /*
  1741. The following block is supposed to handle the
  1742. case when the control modifier is used
  1743. to move the lead without changing the
  1744. selection. The DefaultListSelectionModel
  1745. needs a new property here, to change the
  1746. behavior of "setLeadSelectionIndex" so
  1747. that it does not adjust the selection.
  1748. Until then, this cannot be implemented
  1749. properly and we will remove this code
  1750. altogether to fix bug #4317662.
  1751. */
  1752. /*
  1753. int anchor = lsm.getAnchorSelectionIndex();
  1754. if (anchor == -1) {
  1755. anchor = index;
  1756. }
  1757. list.setSelectionInterval(anchor, index);
  1758. lsm.setAnchorSelectionIndex(anchor);
  1759. */
  1760. lsm.setLeadSelectionIndex(index);
  1761. }
  1762. else if (selectionType == CHANGE_SELECTION) {
  1763. list.setSelectedIndex(index);
  1764. }
  1765. else {
  1766. lsm.setLeadSelectionIndex(index);
  1767. }
  1768. ensureIndexIsVisible(list, index);
  1769. }
  1770. }
  1771. }
  1772. /**
  1773. * IncrementLeadByColumnAction extends the selection to the
  1774. * next column. If there is only one column in the list, this will
  1775. * not change the selection in anyway.
  1776. */
  1777. private static class IncrementLeadByColumnAction extends
  1778. IncrementLeadSelectionAction {
  1779. IncrementLeadByColumnAction(String name, int type, int amount) {
  1780. super(name, type, amount);
  1781. }
  1782. /**
  1783. * Maps the current lead to a column, and adds the amount passed
  1784. * into the constructor to it. This will return -1 if the
  1785. * list is currently not layed out horizontally.
  1786. */
  1787. protected int getNextIndex(JList list) {
  1788. if (list.getLayoutOrientation() != JList.VERTICAL) {
  1789. ListUI ui = list.getUI();
  1790. // PENDING:
  1791. if (ui instanceof SynthListUI) {
  1792. SynthListUI bui = (SynthListUI)ui;
  1793. if (bui.columnCount > 1) {
  1794. int index = list.getLeadSelectionIndex();
  1795. if (index == -1) {
  1796. return 0;
  1797. }
  1798. int size = list.getModel().getSize();
  1799. int column = bui.convertModelToColumn(index);
  1800. int row = bui.convertModelToRow(index);
  1801. column += amount;
  1802. if (column >= bui.columnCount || column < 0) {
  1803. // No wrapping.
  1804. return -1;
  1805. }
  1806. int maxRowCount = bui.getRowCount(column);
  1807. if (row >= maxRowCount) {
  1808. row = maxRowCount - 1;
  1809. }
  1810. return bui.getModelIndex(column, row);
  1811. }
  1812. }
  1813. }
  1814. // Won't change the selection.
  1815. return -1;
  1816. }
  1817. }
  1818. /**
  1819. * Action to move the selection to the first item in the list.
  1820. */
  1821. private static class HomeAction extends IncrementLeadSelectionAction {
  1822. protected HomeAction(String name, int type) {
  1823. super(name, type);
  1824. }
  1825. protected int getNextIndex(JList list) {
  1826. return 0;
  1827. }
  1828. }
  1829. /**
  1830. * Action to move the selection to the last item in the list.
  1831. */
  1832. private static class EndAction extends IncrementLeadSelectionAction {
  1833. protected EndAction(String name, int type) {
  1834. super(name, type);
  1835. }
  1836. protected int getNextIndex(JList list) {
  1837. return list.getModel().getSize() - 1;
  1838. }
  1839. }
  1840. /**
  1841. * Action to move up one page.
  1842. */
  1843. private static class PageUpAction extends IncrementLeadSelectionAction {
  1844. protected PageUpAction(String name, int type) {
  1845. super(name, type);
  1846. }
  1847. protected int getNextIndex(JList list) {
  1848. int index = list.getFirstVisibleIndex();
  1849. ListSelectionModel lsm = list.getSelectionModel();
  1850. if (lsm.getLeadSelectionIndex() == index) {
  1851. Rectangle visRect = list.getVisibleRect();
  1852. visRect.y = Math.max(0, visRect.y - visRect.height);
  1853. index = list.locationToIndex(visRect.getLocation());
  1854. }
  1855. return index;
  1856. }
  1857. protected void ensureIndexIsVisible(JList list, int index) {
  1858. Rectangle visRect = list.getVisibleRect();
  1859. Rectangle cellBounds = list.getCellBounds(index, index);
  1860. cellBounds.height = visRect.height;
  1861. list.scrollRectToVisible(cellBounds);
  1862. }
  1863. }
  1864. /**
  1865. * Action to move down one page.
  1866. */
  1867. private static class PageDownAction extends IncrementLeadSelectionAction {
  1868. protected PageDownAction(String name, int type) {
  1869. super(name, type);
  1870. }
  1871. protected int getNextIndex(JList list) {
  1872. int index = list.getLastVisibleIndex();
  1873. ListSelectionModel lsm = list.getSelectionModel();
  1874. if (index == -1) {
  1875. // Will happen if size < viewport size.
  1876. index = list.getModel().getSize() - 1;
  1877. }
  1878. if (lsm.getLeadSelectionIndex() == index) {
  1879. Rectangle visRect = list.getVisibleRect();
  1880. visRect.y += visRect.height + visRect.height - 1;
  1881. index = list.locationToIndex(visRect.getLocation());
  1882. if (index == -1) {
  1883. index = list.getModel().getSize() - 1;
  1884. }
  1885. }
  1886. return index;
  1887. }
  1888. protected void ensureIndexIsVisible(JList list, int index) {
  1889. Rectangle visRect = list.getVisibleRect();
  1890. Rectangle cellBounds = list.getCellBounds(index, index);
  1891. cellBounds.y = Math.max(0, cellBounds.y + cellBounds.height -
  1892. visRect.height);
  1893. cellBounds.height = visRect.height;
  1894. list.scrollRectToVisible(cellBounds);
  1895. }
  1896. }
  1897. /**
  1898. * Action to select all the items in the list.
  1899. */
  1900. private static class SelectAllAction extends AbstractAction {
  1901. private SelectAllAction(String name) {
  1902. super(name);
  1903. }
  1904. public void actionPerformed(ActionEvent e) {
  1905. JList list = (JList)e.getSource();
  1906. // Select all should not alter the lead and anchor.
  1907. // ListSelectionModel encforces the selection to the anchor/lead,
  1908. // so it is commented out.
  1909. // ListSelectionModel lsm = list.getSelectionModel();
  1910. // int anchor = lsm.getAnchorSelectionIndex();
  1911. // int lead = lsm.getLeadSelectionIndex();
  1912. int size = list.getModel().getSize();
  1913. if (size > 0) {
  1914. ListSelectionModel lsm = list.getSelectionModel();
  1915. if (lsm.getSelectionMode() == ListSelectionModel.
  1916. SINGLE_SELECTION){
  1917. if (list.getMinSelectionIndex() == -1) {
  1918. list.setSelectionInterval(0, 0);
  1919. }
  1920. }
  1921. else {
  1922. list.setSelectionInterval(0, size - 1);
  1923. list.ensureIndexIsVisible(list.getLeadSelectionIndex());
  1924. }
  1925. }
  1926. // lsm.setAnchorSelectionIndex(anchor);
  1927. // lsm.setLeadSelectionIndex(lead);
  1928. }
  1929. }
  1930. /**
  1931. * Action to clear the selection in the list.
  1932. */
  1933. private static class ClearSelectionAction extends AbstractAction {
  1934. private ClearSelectionAction(String name) {
  1935. super(name);
  1936. }
  1937. public void actionPerformed(ActionEvent e) {
  1938. JList list = (JList)e.getSource();
  1939. // Unselect all should not alter the lead and anchor.
  1940. // ListSelectionModel encforces the selection to the anchor/lead,
  1941. // so it is commented out.
  1942. // ListSelectionModel lsm = list.getSelectionModel();
  1943. // int anchor = lsm.getAnchorSelectionIndex();
  1944. // int lead = lsm.getLeadSelectionIndex();
  1945. list.clearSelection();
  1946. // lsm.setAnchorSelectionIndex(anchor);
  1947. // lsm.setLeadSelectionIndex(lead);
  1948. }
  1949. }
  1950. private static final ListDragGestureRecognizer defaultDragRecognizer = new ListDragGestureRecognizer();
  1951. /**
  1952. * Drag gesture recognizer for JList components
  1953. */
  1954. static class ListDragGestureRecognizer extends SynthDragGestureRecognizer {
  1955. /**
  1956. * Determines if the following are true:
  1957. * <ul>
  1958. * <li>the press event is located over a selection
  1959. * <li>the dragEnabled property is true
  1960. * <li>A TranferHandler is installed
  1961. * </ul>
  1962. * <p>
  1963. * This is implemented to perform the superclass behavior
  1964. * followed by a check if the dragEnabled
  1965. * property is set and if the location picked is selected.
  1966. */
  1967. protected boolean isDragPossible(MouseEvent e) {
  1968. if (super.isDragPossible(e)) {
  1969. JList list = (JList) this.getComponent(e);
  1970. if (list.getDragEnabled()) {
  1971. ListUI ui = list.getUI();
  1972. int row = ui.locationToIndex(list, new Point(e.getX(),e.getY()));
  1973. if ((row != -1) && list.isSelectedIndex(row)) {
  1974. return true;
  1975. }
  1976. }
  1977. }
  1978. return false;
  1979. }
  1980. }
  1981. /**
  1982. * A DropTargetListener to extend the default Swing handling of drop operations
  1983. * by moving the list selection to the nearest location to the mouse pointer.
  1984. * Also adds autoscroll.
  1985. */
  1986. class ListDropTargetListener extends SynthDropTargetListener {
  1987. /**
  1988. * called to save the state of a component in case it needs to
  1989. * be restored because a drop is not performed.
  1990. */
  1991. protected void saveComponentState(JComponent comp) {
  1992. JList list = (JList) comp;
  1993. selectedIndices = list.getSelectedIndices();
  1994. }
  1995. /**
  1996. * called to restore the state of a component
  1997. * because a drop was not performed.
  1998. */
  1999. protected void restoreComponentState(JComponent comp) {
  2000. JList list = (JList) comp;
  2001. list.setSelectedIndices(selectedIndices);
  2002. }
  2003. /**
  2004. * called to set the insertion location to match the current
  2005. * mouse pointer coordinates.
  2006. */
  2007. protected void updateInsertionLocation(JComponent comp, Point p) {
  2008. JList list = (JList) comp;
  2009. int index = convertLocationToModel(p.x, p.y);
  2010. if (index != -1) {
  2011. list.setSelectionInterval(index, index);
  2012. }
  2013. }
  2014. private int[] selectedIndices;
  2015. }
  2016. private static final TransferHandler defaultTransferHandler = new ListTransferHandler();
  2017. static class ListTransferHandler extends TransferHandler implements UIResource {
  2018. /**
  2019. * Create a Transferable to use as the source for a data transfer.
  2020. *
  2021. * @param c The component holding the data to be transfered. This
  2022. * argument is provided to enable sharing of TransferHandlers by
  2023. * multiple components.
  2024. * @return The representation of the data to be transfered.
  2025. *
  2026. */
  2027. protected Transferable createTransferable(JComponent c) {
  2028. if (c instanceof JList) {
  2029. JList list = (JList) c;
  2030. Object[] values = list.getSelectedValues();
  2031. if (values == null || values.length == 0) {
  2032. return null;
  2033. }
  2034. StringBuffer plainBuf = new StringBuffer();
  2035. StringBuffer htmlBuf = new StringBuffer();
  2036. htmlBuf.append("<html>\n<body>\n<ul>\n");
  2037. for (int i = 0; i < values.length; i++) {
  2038. Object obj = values[i];
  2039. String val = ((obj == null) ? "" : obj.toString());
  2040. plainBuf.append(val + "\n");
  2041. htmlBuf.append(" <li>" + val + "\n");
  2042. }
  2043. // remove the last newline
  2044. plainBuf.deleteCharAt(plainBuf.length() - 1);
  2045. htmlBuf.append("</ul>\n</body>\n</html>");
  2046. return new SynthTransferable(plainBuf.toString(), htmlBuf.toString());
  2047. }
  2048. return null;
  2049. }
  2050. public int getSourceActions(JComponent c) {
  2051. return COPY;
  2052. }
  2053. }
  2054. }