1. /*
  2. * @(#)JList.java 1.74 00/04/06
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.event.*;
  12. import java.awt.*;
  13. import java.util.Vector;
  14. import java.util.Locale;
  15. import java.beans.*;
  16. import javax.swing.event.*;
  17. import javax.accessibility.*;
  18. import javax.swing.plaf.*;
  19. import java.io.ObjectOutputStream;
  20. import java.io.ObjectInputStream;
  21. import java.io.IOException;
  22. import java.io.Serializable;
  23. /**
  24. * A component that allows the user to select one or more objects from a
  25. * list. A separate model, <code>ListModel</code>, represents the contents
  26. * of the list. It's easy to display an array or vector of objects, using
  27. * a <code>JList</code> constructor that builds a <code>ListModel</code>
  28. * instance for you:
  29. * <pre>
  30. * // Create a JList that displays the strings in data[]
  31. *
  32. * String[] data = {"one", "two", "three", "four"};
  33. * JList dataList = new JList(data);
  34. *
  35. * // The value of the JList model property is an object that provides
  36. * // a read-only view of the data. It was constructed automatically.
  37. *
  38. * for(int i = 0; i < dataList.getModel().getSize(); i++) {
  39. * System.out.println(dataList.getModel().getElementAt(i));
  40. * }
  41. *
  42. * // Create a JList that displays the superclass of JList.class.
  43. * // We store the superclasses in a java.util.Vector.
  44. *
  45. * Vector superClasses = new Vector();
  46. * Class rootClass = javax.swing.JList.class;
  47. * for(Class cls = rootClass; cls != null; cls = cls.getSuperclass()) {
  48. * superClasses.addElement(cls);
  49. * }
  50. * JList classList = new JList(superClasses);
  51. * </pre>
  52. * <p>
  53. * <code>JList</code> doesn't support scrolling directly.
  54. * To create a scrolling
  55. * list you make the <code>JList</code> the viewport view of a
  56. * <code>JScrollPane</code>. For example:
  57. * <pre>
  58. * JScrollPane scrollPane = new JScrollPane(dataList);
  59. * // Or in two steps:
  60. * JScrollPane scrollPane = new JScrollPane();
  61. * scrollPane.getViewport().setView(dataList);
  62. * </pre>
  63. * <p>
  64. * By default the <code>JList</code> selection model allows any
  65. * combination of items to be selected at a time, using the constant
  66. * <code>MULTIPLE_INTERVAL_SELECTION</code>.
  67. * The selection state is actually managed
  68. * by a separate delegate object, an instance of
  69. * <code>ListSelectionModel</code>.
  70. * However <code>JList</code> provides convenient properties for
  71. * managing the selection.
  72. * <pre>
  73. * String[] data = {"one", "two", "three", "four"};
  74. * JList dataList = new JList(data);
  75. *
  76. * dataList.setSelectedIndex(1); // select "two"
  77. * dataList.getSelectedValue(); // returns "two"
  78. * </pre>
  79. * <p>
  80. * The contents of a <code>JList</code> can be dynamic,
  81. * in other words, the list elements can
  82. * change value and the size of the list can change after the
  83. * <code>JList</code> has
  84. * been created. The <code>JList</code> observes changes in its model with a
  85. * <code>swing.event.ListDataListener</code> implementation. A correct
  86. * implementation of <code>ListModel</code> notifies
  87. * it's listeners each time a change occurs. The changes are
  88. * characterized by a <code>swing.event.ListDataEvent</code>, which identifies
  89. * the range of list indices that have been modified, added, or removed.
  90. * Simple dynamic-content <code>JList</code> applications can use the
  91. * <code>DefaultListModel</code> class to store list elements. This class
  92. * implements the <code>ListModel</code> interface and provides the
  93. * <code>java.util.Vector</code> API as well. Applications that need to
  94. * provide custom <code>ListModel</code> implementations can subclass
  95. * <code>AbstractListModel</code>, which provides basic
  96. * <code>ListDataListener</code> support. For example:
  97. * <pre>
  98. * // This list model has about 2^16 elements. Enjoy scrolling.
  99. *
  100. * <a name="prototype_example">
  101. * ListModel bigData = new AbstractListModel() {
  102. * public int getSize() { return Short.MAX_VALUE; }
  103. * public Object getElementAt(int index) { return "Index " + index; }
  104. * };
  105. *
  106. * JList bigDataList = new JList(bigData);
  107. *
  108. * // We don't want the JList implementation to compute the width
  109. * // or height of all of the list cells, so we give it a string
  110. * // that's as big as we'll need for any cell. It uses this to
  111. * // compute values for the fixedCellWidth and fixedCellHeight
  112. * // properties.
  113. *
  114. * bigDataList.setPrototypeCellValue("Index 1234567890");
  115. * </pre>
  116. * <p>
  117. * <code>JList</code> uses a <code>java.awt.Component</code>, provided by
  118. * a delegate called the
  119. * <code>cellRendererer</code>, to paint the visible cells in the list.
  120. * The cell renderer component is used like a "rubber stamp" to paint
  121. * each visible row. Each time the <code>JList</code> needs to paint a cell
  122. * it asks the cell renderer for the component, moves it into place
  123. * using <code>setBounds()</code> and then draws it by calling its paint method.
  124. * The default cell renderer uses a <code>JLabel</code> component to render
  125. * the string value of each component. You can substitute your
  126. * own cell renderer, using code like this:
  127. * <pre>
  128. * // Display an icon and a string for each object in the list.
  129. *
  130. * <a name="cellrenderer_example">
  131. * class MyCellRenderer extends JLabel implements ListCellRenderer {
  132. * final static ImageIcon longIcon = new ImageIcon("long.gif");
  133. * final static ImageIcon shortIcon = new ImageIcon("short.gif");
  134. *
  135. * // This is the only method defined by ListCellRenderer.
  136. * // We just reconfigure the JLabel each time we're called.
  137. *
  138. * public Component getListCellRendererComponent(
  139. * JList list,
  140. * Object value, // value to display
  141. * int index, // cell index
  142. * boolean isSelected, // is the cell selected
  143. * boolean cellHasFocus) // the list and the cell have the focus
  144. * {
  145. * String s = value.toString();
  146. * setText(s);
  147. * setIcon((s.length() > 10) ? longIcon : shortIcon);
  148. * if (isSelected) {
  149. * setBackground(list.getSelectionBackground());
  150. * setForeground(list.getSelectionForeground());
  151. * }
  152. * else {
  153. * setBackground(list.getBackground());
  154. * setForeground(list.getForeground());
  155. * }
  156. * setEnabled(list.isEnabled());
  157. * setFont(list.getFont());
  158. * return this;
  159. * }
  160. * }
  161. *
  162. * String[] data = {"one", "two", "three", "four"};
  163. * JList dataList = new JList(data);
  164. * dataList.setCellRenderer(new MyCellRenderer());
  165. * </pre>
  166. * <p>
  167. * <code>JList</code> doesn't provide any special support for handling double or
  168. * triple (or N) mouse clicks however it's easy to handle them using
  169. * a <code>MouseListener</code>. Use the <code>JList</code> method
  170. * <code>locationToIndex()</code> to
  171. * determine what cell was clicked. For example:
  172. * <pre>
  173. * final JList list = new JList(dataModel);
  174. * MouseListener mouseListener = new MouseAdapter() {
  175. * public void mouseClicked(MouseEvent e) {
  176. * if (e.getClickCount() == 2) {
  177. * int index = list.locationToIndex(e.getPoint());
  178. * System.out.println("Double clicked on Item " + index);
  179. * }
  180. * }
  181. * };
  182. * list.addMouseListener(mouseListener);
  183. * </pre>
  184. * Note that in this example the <code>dataList</code> is <code>final</code>
  185. * because it's referred to by the anonymous <code>MouseListener</code> class.
  186. * <p>
  187. * For the keyboard keys used by this component in the standard look and
  188. * feel (L&F) renditions, see the
  189. * <a href="doc-files/Key-Index.html#JList">JList</a> key assignments.
  190. * <p>
  191. * <strong>Warning:</strong>
  192. * Serialized objects of this class will not be compatible with
  193. * future Swing releases. The current serialization support is appropriate
  194. * for short term storage or RMI between applications running the same
  195. * version of Swing. A future release of Swing will provide support for
  196. * long term persistence.
  197. *
  198. * <p>
  199. * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/list.html">How to Use Lists</a>
  200. * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
  201. * for further documentation.
  202. * Also see the article <a href="http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html">Advanced JList Programming</a>
  203. * in <a href="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
  204. * <p>
  205. * @see ListModel
  206. * @see AbstractListModel
  207. * @see DefaultListModel
  208. * @see ListSelectionModel
  209. * @see DefaultListSelectionModel
  210. * @see ListCellRenderer
  211. *
  212. * @beaninfo
  213. * attribute: isContainer false
  214. * description: A component which allows for the selection of one or more objects from a list.
  215. *
  216. * @version 1.74 04/06/00
  217. * @author Hans Muller
  218. */
  219. public class JList extends JComponent implements Scrollable, Accessible
  220. {
  221. /**
  222. * @see #getUIClassID
  223. * @see #readObject
  224. */
  225. private static final String uiClassID = "ListUI";
  226. private int fixedCellWidth = -1;
  227. private int fixedCellHeight = -1;
  228. private int horizontalScrollIncrement = -1;
  229. private Object prototypeCellValue;
  230. private int visibleRowCount = 8;
  231. private Color selectionForeground;
  232. private Color selectionBackground;
  233. private ListSelectionModel selectionModel;
  234. private ListModel dataModel;
  235. private ListCellRenderer cellRenderer;
  236. private ListSelectionListener selectionListener;
  237. /**
  238. * Constructs a <code>JList</code> that displays the elements in the
  239. * specified, non-null model. All <code>JList</code> constructors
  240. * delegate to this one.
  241. * @param dataModel the data model for this list
  242. * @exception IllegalArgumentException if <code>dataModel</code>
  243. * is <code>null</code>
  244. */
  245. public JList(ListModel dataModel)
  246. {
  247. if (dataModel == null) {
  248. throw new IllegalArgumentException("dataModel must be non null");
  249. }
  250. this.dataModel = dataModel;
  251. selectionModel = createSelectionModel();
  252. setAutoscrolls(true);
  253. setOpaque(true);
  254. updateUI();
  255. }
  256. /**
  257. * Constructs a <code>JList</code> that displays the elements in the specified
  258. * array. This constructor just delegates to the <code>ListModel</code>
  259. * constructor.
  260. * @param listData the array of Objects to be loaded into the data model
  261. */
  262. public JList(final Object[] listData)
  263. {
  264. this (
  265. new AbstractListModel() {
  266. public int getSize() { return listData.length; }
  267. public Object getElementAt(int i) { return listData[i]; }
  268. }
  269. );
  270. }
  271. /**
  272. * Constructs a <code>JList</code> that displays the elements in the specified
  273. * <code>Vector</code>. This constructor just delegates to the
  274. * <code>ListModel</code> constructor.
  275. * @param listData the <code>Vector</code> to be loaded into the data model
  276. */
  277. public JList(final Vector listData) {
  278. this (
  279. new AbstractListModel() {
  280. public int getSize() { return listData.size(); }
  281. public Object getElementAt(int i) { return listData.elementAt(i); }
  282. }
  283. );
  284. }
  285. /**
  286. * Constructs a <code>JList</code> with an empty model.
  287. */
  288. public JList() {
  289. this (
  290. new AbstractListModel() {
  291. public int getSize() { return 0; }
  292. public Object getElementAt(int i) { return "No Data Model"; }
  293. }
  294. );
  295. }
  296. /**
  297. * Returns the look and feel (L&F) object that renders this component.
  298. *
  299. * @return the ListUI object that renders this component
  300. */
  301. public ListUI getUI() {
  302. return (ListUI)ui;
  303. }
  304. /**
  305. * Sets the look and feel (L&F) object that renders this component.
  306. *
  307. * @param ui the ListUI L&F object
  308. * @see UIDefaults#getUI
  309. */
  310. public void setUI(ListUI ui) {
  311. super.setUI(ui);
  312. }
  313. /**
  314. * Sets the UI property with the "ListUI" from the current default
  315. * UIFactory. This method is called by the <code>JList</code> constructor
  316. * and to update the list's look and feel at runtime.
  317. *
  318. * @see UIManager#getUI
  319. */
  320. public void updateUI() {
  321. setUI((ListUI)UIManager.getUI(this));
  322. invalidate();
  323. }
  324. /**
  325. * Returns the suffix used to construct the name of the look and feel
  326. * (L&F) class used to render this component.
  327. *
  328. * @return the string "ListUI"
  329. * @see JComponent#getUIClassID
  330. * @see UIDefaults#getUI
  331. */
  332. public String getUIClassID() {
  333. return uiClassID;
  334. }
  335. /* -----private-----
  336. * This method is called by setPrototypeCellValue and setCellRenderer
  337. * to update the fixedCellWidth and fixedCellHeight properties from the
  338. * current value of prototypeCellValue (if it's non null).
  339. * <p>
  340. * This method sets fixedCellWidth and fixedCellHeight but does <b>not</b>
  341. * generate PropertyChangeEvents for them.
  342. *
  343. * @see #setPrototypeCellValue
  344. * @see #setCellRenderer
  345. */
  346. private void updateFixedCellSize()
  347. {
  348. ListCellRenderer cr = getCellRenderer();
  349. Object value = getPrototypeCellValue();
  350. if ((cr != null) && (value != null)) {
  351. Component c = cr.getListCellRendererComponent(this, value, 0, false, false);
  352. /* The ListUI implementation will add Component c to its private
  353. * CellRendererPane however we can't assume that's already
  354. * been done here. So we temporarilty set the one "inherited"
  355. * property that may affect the renderer components preferred size:
  356. * its font.
  357. */
  358. Font f = c.getFont();
  359. c.setFont(getFont());
  360. Dimension d = c.getPreferredSize();
  361. fixedCellWidth = d.width;
  362. fixedCellHeight = d.height;
  363. c.setFont(f);
  364. }
  365. }
  366. /**
  367. * Returns the cell width of the "prototypical cell" -- a cell used
  368. * for the calculation of cell widths, because it has the same value
  369. * as all other list items.
  370. *
  371. * @return the value of the <code>prototypeCellValue</code> property
  372. * @see #setPrototypeCellValue
  373. */
  374. public Object getPrototypeCellValue() {
  375. return prototypeCellValue;
  376. }
  377. /**
  378. * Computes the <code>fixedCellWidth</code> and
  379. * <code>fixedCellHeight</code> properties
  380. * by configuring the <code>cellRenderer</code> to index equals
  381. * zero for the specified value and then computing the renderer
  382. * component's preferred size. These properties are useful when the
  383. * list is too long to allow <code>JList</code> to compute the
  384. * width/height of each cell and there is a single cell value that is
  385. * known to occupy as much space as any of the others.
  386. * <p>
  387. * Note that we do set the <code>fixedCellWidth</code> and
  388. * <code>fixedCellHeight</code> properties here but only a
  389. * <code>prototypeCellValue PropertyChangeEvent</code> is fired.
  390. * <p>
  391. * To see an example which sets this property,
  392. * see the <a href = #prototype_example>class description</a> above.
  393. * <p>
  394. * The default value of this property is <code>null</code>.
  395. * <p>
  396. * This is a JavaBeans bound property.
  397. *
  398. * @param prototypeCellValue the value on which to base
  399. * <code>fixedCellWidth</code> and
  400. * <code>fixedCellHeight</code>
  401. * @see #getPrototypeCellValue
  402. * @see #setFixedCellWidth
  403. * @see #setFixedCellHeight
  404. * @see JComponent#addPropertyChangeListener
  405. * @beaninfo
  406. * bound: true
  407. * attribute: visualUpdate true
  408. * description: The cell prototype value, used to compute cell width and height.
  409. */
  410. public void setPrototypeCellValue(Object prototypeCellValue) {
  411. Object oldValue = this.prototypeCellValue;
  412. this.prototypeCellValue = prototypeCellValue;
  413. /* If the cellRenderer has changed and prototypeCellValue
  414. * was set, then recompute fixedCellWidth and fixedCellHeight.
  415. */
  416. if ((prototypeCellValue != null) && !prototypeCellValue.equals(oldValue)) {
  417. updateFixedCellSize();
  418. }
  419. firePropertyChange("prototypeCellValue", oldValue, prototypeCellValue);
  420. }
  421. /**
  422. * Returns the fixed cell width value -- the value specified by setting
  423. * the <code>fixedCellWidth</code> property, rather than that calculated
  424. * from the list elements.
  425. *
  426. * @return the fixed cell width
  427. * @see #setFixedCellWidth
  428. */
  429. public int getFixedCellWidth() {
  430. return fixedCellWidth;
  431. }
  432. /**
  433. * Sets the width of every cell in the list. If <code>width</code> is -1,
  434. * cell widths are computed by applying <code>getPreferredSize</code>
  435. * to the <code>cellRenderer</code> component for each list element.
  436. * <p>
  437. * The default value of this property is -1.
  438. * <p>
  439. * This is a JavaBeans bound property.
  440. *
  441. * @param width the width, in pixels, for all cells in this list
  442. * @see #getPrototypeCellValue
  443. * @see #setFixedCellWidth
  444. * @see JComponent#addPropertyChangeListener
  445. * @beaninfo
  446. * bound: true
  447. * attribute: visualUpdate true
  448. * description: Defines a fixed cell width when greater than zero.
  449. */
  450. public void setFixedCellWidth(int width) {
  451. int oldValue = fixedCellWidth;
  452. fixedCellWidth = width;
  453. firePropertyChange("fixedCellWidth", oldValue, fixedCellWidth);
  454. }
  455. /**
  456. * Returns the fixed cell height value -- the value specified by setting
  457. * the <code>fixedCellHeight</code> property,
  458. * rather than that calculated from the list elements.
  459. *
  460. * @return the fixed cell height, in pixels
  461. * @see #setFixedCellHeight
  462. */
  463. public int getFixedCellHeight() {
  464. return fixedCellHeight;
  465. }
  466. /**
  467. * Sets the height of every cell in the list. If <code>height</code>
  468. * is -1, cell
  469. * heights are computed by applying <code>getPreferredSize</code>
  470. * to the <code>cellRenderer</code> component for each list element.
  471. * <p>
  472. * The default value of this property is -1.
  473. * <p>
  474. * This is a JavaBeans bound property.
  475. *
  476. * @param height an integer giving the height, in pixels, for all cells
  477. * in this list
  478. * @see #getPrototypeCellValue
  479. * @see #setFixedCellWidth
  480. * @see JComponent#addPropertyChangeListener
  481. * @beaninfo
  482. * bound: true
  483. * attribute: visualUpdate true
  484. * description: Defines a fixed cell height when greater than zero.
  485. */
  486. public void setFixedCellHeight(int height) {
  487. int oldValue = fixedCellHeight;
  488. fixedCellHeight = height;
  489. firePropertyChange("fixedCellHeight", oldValue, fixedCellHeight);
  490. }
  491. /**
  492. * Returns the object that renders the list items.
  493. *
  494. * @return the <code>ListCellRenderer</code>
  495. * @see #setCellRenderer
  496. */
  497. public ListCellRenderer getCellRenderer() {
  498. return cellRenderer;
  499. }
  500. /**
  501. * Sets the delegate that's used to paint each cell in the list. If
  502. * <code>prototypeCellValue</code> was set then the
  503. * <code>fixedCellWidth</code> and <code>fixedCellHeight</code>
  504. * properties are set as well. Only one <code>PropertyChangeEvent</code>
  505. * is generated however - for the <code>cellRenderer</code> property.
  506. * <p>
  507. * The default value of this property is provided by the ListUI
  508. * delegate, i.e. by the look and feel implementation.
  509. * <p>
  510. * To see an example which sets the cell renderer,
  511. * see the <a href = #cellrenderer_example>class description</a> above.
  512. * <p>
  513. * This is a JavaBeans bound property.
  514. *
  515. * @param cellRenderer the <code>ListCellRenderer</code>
  516. * that paints list cells
  517. * @see #getCellRenderer
  518. * @beaninfo
  519. * bound: true
  520. * attribute: visualUpdate true
  521. * description: The component used to draw the cells.
  522. */
  523. public void setCellRenderer(ListCellRenderer cellRenderer) {
  524. ListCellRenderer oldValue = this.cellRenderer;
  525. this.cellRenderer = cellRenderer;
  526. /* If the cellRenderer has changed and prototypeCellValue
  527. * was set, then recompute fixedCellWidth and fixedCellHeight.
  528. */
  529. if ((cellRenderer != null) && !cellRenderer.equals(oldValue)) {
  530. updateFixedCellSize();
  531. }
  532. firePropertyChange("cellRenderer", oldValue, cellRenderer);
  533. }
  534. /**
  535. * Returns the selection foreground color.
  536. *
  537. * @return the <code>Color</code> object for the foreground property
  538. * @see #setSelectionForeground
  539. * @see #setSelectionBackground
  540. */
  541. public Color getSelectionForeground() {
  542. return selectionForeground;
  543. }
  544. /**
  545. * Sets the foreground color for selected cells. Cell renderers
  546. * can use this color to render text and graphics for selected
  547. * cells.
  548. * <p>
  549. * The default value of this property is defined by the look
  550. * and feel implementation.
  551. * <p>
  552. * This is a JavaBeans bound property.
  553. *
  554. * @param selectionForeground the <code>Color</code> to use in the foreground
  555. * for selected list items
  556. * @see #getSelectionForeground
  557. * @see #setSelectionBackground
  558. * @see #setForeground
  559. * @see #setBackground
  560. * @see #setFont
  561. * @beaninfo
  562. * bound: true
  563. * attribute: visualUpdate true
  564. * description: The foreground color of selected cells.
  565. */
  566. public void setSelectionForeground(Color selectionForeground) {
  567. Color oldValue = this.selectionForeground;
  568. this.selectionForeground = selectionForeground;
  569. firePropertyChange("selectionForeground", oldValue, selectionForeground);
  570. }
  571. /**
  572. * Returns the background color for selected cells.
  573. *
  574. * @return the <code>Color</code> used for the background of
  575. * selected list items
  576. * @see #setSelectionBackground
  577. * @see #setSelectionForeground
  578. */
  579. public Color getSelectionBackground() {
  580. return selectionBackground;
  581. }
  582. /**
  583. * Sets the background color for selected cells. Cell renderers
  584. * can use this color to the fill selected cells.
  585. * <p>
  586. * The default value of this property is defined by the look
  587. * and feel implementation.
  588. * <p>
  589. * This is a JavaBeans bound property.
  590. *
  591. * @param selectionBackground the <code>Color</code> to use for the
  592. * background of selected cells
  593. * @see #getSelectionBackground
  594. * @see #setSelectionForeground
  595. * @see #setForeground
  596. * @see #setBackground
  597. * @see #setFont
  598. * @beaninfo
  599. * bound: true
  600. * attribute: visualUpdate true
  601. * description: The background color of selected cells.
  602. */
  603. public void setSelectionBackground(Color selectionBackground) {
  604. Color oldValue = this.selectionBackground;
  605. this.selectionBackground = selectionBackground;
  606. firePropertyChange("selectionBackground", oldValue, selectionBackground);
  607. }
  608. /**
  609. * Returns the preferred number of visible rows.
  610. *
  611. * @return an integer indicating the preferred number of rows to display
  612. * without using a scroll bar
  613. * @see #setVisibleRowCount
  614. */
  615. public int getVisibleRowCount() {
  616. return visibleRowCount;
  617. }
  618. /**
  619. * Sets the preferred number of rows in the list that can be displayed
  620. * without a scollbar, as determined by the nearest
  621. * <code>JViewport</code> ancestor, if any.
  622. * The value of this property only affects the value of
  623. * the <code>JList</code>'s <code>preferredScrollableViewportSize</code>.
  624. * <p>
  625. * The default value of this property is 8.
  626. * <p>
  627. * This is a JavaBeans bound property.
  628. *
  629. * @param visibleRowCount an integer specifying the preferred number of
  630. * visible rows
  631. * @see #getVisibleRowCount
  632. * @see JComponent#getVisibleRect
  633. * @see JViewport
  634. * @beaninfo
  635. * bound: true
  636. * attribute: visualUpdate true
  637. * description: The preferred number of cells that can be displayed without a scroll bar.
  638. */
  639. public void setVisibleRowCount(int visibleRowCount) {
  640. int oldValue = this.visibleRowCount;
  641. this.visibleRowCount = Math.max(0, visibleRowCount);
  642. firePropertyChange("visibleRowCount", oldValue, visibleRowCount);
  643. }
  644. /**
  645. * Returns the index of the cell in the upper left corner of the
  646. * <code>JList</code>
  647. * or -1 if nothing is visible or the list is empty. Note that this
  648. * cell may only be partially visible.
  649. *
  650. * @return the index of the first visible cell
  651. * @see #getLastVisibleIndex
  652. * @see JComponent#getVisibleRect
  653. */
  654. public int getFirstVisibleIndex() {
  655. Point visibleUL = getVisibleRect().getLocation();
  656. return locationToIndex(visibleUL);
  657. }
  658. /**
  659. * Returns the index of the cell in the lower right corner of the
  660. * <code>JList</code>
  661. * or -1 if nothing is visible or the list is empty. Note that this
  662. * cell may only be partially visible.
  663. *
  664. * @return the index of the last visible cell
  665. * @see #getLastVisibleIndex
  666. * @see JComponent#getVisibleRect
  667. */
  668. public int getLastVisibleIndex() {
  669. Rectangle r = getVisibleRect();
  670. Point visibleLR = new Point((r.x + r.width) - 1, (r.y + r.height) - 1);
  671. return locationToIndex(visibleLR);
  672. }
  673. /**
  674. * Scrolls the viewport to make the specified cell completely visible.
  675. * Note, for this method to work, the <code>JList</code> must be
  676. * displayed within a <code>JViewport</code>.
  677. *
  678. * @param index the index of the cell to make visible
  679. * @see JComponent#scrollRectToVisible
  680. * @see #getVisibleRect
  681. */
  682. public void ensureIndexIsVisible(int index) {
  683. Rectangle cellBounds = getCellBounds(index, index);
  684. if (cellBounds != null) {
  685. scrollRectToVisible(cellBounds);
  686. }
  687. }
  688. /**
  689. * --- ListUI Delegations ---
  690. */
  691. /**
  692. * Converts a point in <code>JList</code> coordinates to the index
  693. * of the cell at that location. Returns -1 if there's no
  694. * cell at the specified location.
  695. *
  696. * @param location the coordinates of the cell, relative to
  697. * <code>JList</code>
  698. * @return an integer -- the index of the cell at the given location, or -1.
  699. */
  700. public int locationToIndex(Point location) {
  701. ListUI ui = getUI();
  702. return (ui != null) ? ui.locationToIndex(this, location) : -1;
  703. }
  704. /**
  705. * Returns the origin of the specified item in <code>JList</code>
  706. * coordinates. Returns <code>null</code> if <code>index</code> isn't valid.
  707. *
  708. * @param index the index of the <code>JList</code> cell
  709. * @return the origin of the index'th cell
  710. */
  711. public Point indexToLocation(int index) {
  712. ListUI ui = getUI();
  713. return (ui != null) ? ui.indexToLocation(this, index) : null;
  714. }
  715. /**
  716. * Returns the bounds of the specified range of items in <code>JList</code>
  717. * coordinates. Returns <code>null</code> if index isn't valid.
  718. *
  719. * @param index0 the index of the first <code>JList</code> cell in the range
  720. * @param index1 the index of the last <code>JList</code> cell in the range
  721. * @return the bounds of the indexed cells in pixels
  722. */
  723. public Rectangle getCellBounds(int index0, int index1) {
  724. ListUI ui = getUI();
  725. return (ui != null) ? ui.getCellBounds(this, index0, index1) : null;
  726. }
  727. /**
  728. * --- ListModel Support ---
  729. */
  730. /**
  731. * Returns the data model that holds the list of items displayed
  732. * by the <code>JList</code> component.
  733. *
  734. * @return the <code>ListModel</code> that provides the displayed
  735. * list of items
  736. * @see #setModel
  737. */
  738. public ListModel getModel() {
  739. return dataModel;
  740. }
  741. /**
  742. * Sets the model that represents the contents or "value" of the
  743. * list and clears the list selection after notifying
  744. * <code>PropertyChangeListeners</code>.
  745. * <p>
  746. * This is a JavaBeans bound property.
  747. *
  748. * @param model the <code>ListModel</code> that provides the
  749. * list of items for display
  750. * @exception IllegalArgumentException if <code>model</code> is
  751. * <code>null</code>
  752. * @see #getModel
  753. * @beaninfo
  754. * bound: true
  755. * attribute: visualUpdate true
  756. * description: The object that contains the data to be drawn by this JList.
  757. */
  758. public void setModel(ListModel model) {
  759. if (model == null) {
  760. throw new IllegalArgumentException("model must be non null");
  761. }
  762. ListModel oldValue = dataModel;
  763. dataModel = model;
  764. firePropertyChange("model", oldValue, dataModel);
  765. clearSelection();
  766. }
  767. /**
  768. * Constructs a <code>ListModel</code> from an array of objects and then
  769. * applies <code>setModel</code> to it.
  770. *
  771. * @param listData an array of Objects containing the items to display
  772. * in the list
  773. * @see #setModel
  774. */
  775. public void setListData(final Object[] listData) {
  776. setModel (
  777. new AbstractListModel() {
  778. public int getSize() { return listData.length; }
  779. public Object getElementAt(int i) { return listData[i]; }
  780. }
  781. );
  782. }
  783. /**
  784. * Constructs a <code>ListModel</code> from a <code>Vector</code> and then
  785. * applies <code>setModel</code> to it.
  786. *
  787. * @param listData a <code>Vector</code> containing the items to
  788. * display in the list
  789. * @see #setModel
  790. */
  791. public void setListData(final Vector listData) {
  792. setModel (
  793. new AbstractListModel() {
  794. public int getSize() { return listData.size(); }
  795. public Object getElementAt(int i) { return listData.elementAt(i); }
  796. }
  797. );
  798. }
  799. /**
  800. * --- ListSelectionModel delegations and extensions ---
  801. */
  802. /**
  803. * Returns an instance of <code>DefaultListSelectionModel</code>. This
  804. * method is used by the constructor to initialize the
  805. * <code>selectionModel</code> property.
  806. *
  807. * @return the <code>ListSelectionModel</code> used by this
  808. * <code>JList</code>.
  809. * @see #setSelectionModel
  810. * @see DefaultListSelectionModel
  811. */
  812. protected ListSelectionModel createSelectionModel() {
  813. return new DefaultListSelectionModel();
  814. }
  815. /**
  816. * Returns the value of the current selection model. The selection
  817. * model handles the task of making single selections, selections
  818. * of contiguous ranges, and non-contiguous selections.
  819. *
  820. * @return the <code>ListSelectionModel</code> that implements
  821. * list selections
  822. * @see #setSelectionModel
  823. * @see ListSelectionModel
  824. */
  825. public ListSelectionModel getSelectionModel() {
  826. return selectionModel;
  827. }
  828. /**
  829. * Notifies <code>JList</code> <code>ListSelectionListener</code>s that
  830. * the selection model has changed. It's used to forward
  831. * <code>ListSelectionEvents</code> from the <code>selectionModel</code>
  832. * to the <code>ListSelectionListener</code>s added directly to the
  833. * <code>JList</code>.
  834. * @param firstIndex the first selected index
  835. * @param lastIndex the last selected index
  836. * @param isAdjusting true if multiple changes are being made
  837. *
  838. * @see #addListSelectionListener
  839. * @see #removeListSelectionListener
  840. * @see EventListenerList
  841. */
  842. protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
  843. boolean isAdjusting)
  844. {
  845. Object[] listeners = listenerList.getListenerList();
  846. ListSelectionEvent e = null;
  847. for (int i = listeners.length - 2; i >= 0; i -= 2) {
  848. if (listeners[i] == ListSelectionListener.class) {
  849. if (e == null) {
  850. e = new ListSelectionEvent(this, firstIndex, lastIndex,
  851. isAdjusting);
  852. }
  853. ((ListSelectionListener)listeners[i+1]).valueChanged(e);
  854. }
  855. }
  856. }
  857. /* A ListSelectionListener that forwards ListSelectionEvents from
  858. * the selectionModel to the JList ListSelectionListeners. The
  859. * forwarded events only differ from the originals in that their
  860. * source is the JList instead of the selectionModel itself.
  861. */
  862. private class ListSelectionHandler implements ListSelectionListener, Serializable
  863. {
  864. public void valueChanged(ListSelectionEvent e) {
  865. fireSelectionValueChanged(e.getFirstIndex(),
  866. e.getLastIndex(),
  867. e.getValueIsAdjusting());
  868. }
  869. }
  870. /**
  871. * Adds a listener to the list that's notified each time a change
  872. * to the selection occurs. Listeners added directly to the
  873. * <code>JList</code>
  874. * will have their <code>ListSelectionEvent.getSource() ==
  875. * this JList</code>
  876. * (instead of the <code>ListSelectionModel</code>).
  877. *
  878. * @param listener the <code>ListSelectionListener</code> to add
  879. * @see #getSelectionModel
  880. */
  881. public void addListSelectionListener(ListSelectionListener listener)
  882. {
  883. if (selectionListener == null) {
  884. selectionListener = new ListSelectionHandler();
  885. getSelectionModel().addListSelectionListener(selectionListener);
  886. }
  887. listenerList.add(ListSelectionListener.class, listener);
  888. }
  889. /**
  890. * Removes a listener from the list that's notified each time a
  891. * change to the selection occurs.
  892. *
  893. * @param listener the <code>ListSelectionListener</code> to remove
  894. * @see #addListSelectionListener
  895. * @see #getSelectionModel
  896. */
  897. public void removeListSelectionListener(ListSelectionListener listener) {
  898. listenerList.remove(ListSelectionListener.class, listener);
  899. }
  900. /**
  901. * Sets the <code>selectionModel</code> for the list to a
  902. * non-<code>null</code> <code>ListSelectionModel</code>
  903. * implementation. The selection model handles the task of making single
  904. * selections, selections of contiguous ranges, and non-contiguous
  905. * selections.
  906. * <p>
  907. * This is a JavaBeans bound property.
  908. *
  909. * @param selectionModel the <code>ListSelectionModel</code> that
  910. * implements the selections
  911. * @exception IllegalArgumentException if <code>selectionModel</code>
  912. * is <code>null</code>
  913. * @see #getSelectionModel
  914. * @beaninfo
  915. * bound: true
  916. * description: The selection model, recording which cells are selected.
  917. */
  918. public void setSelectionModel(ListSelectionModel selectionModel) {
  919. if (selectionModel == null) {
  920. throw new IllegalArgumentException("selectionModel must be non null");
  921. }
  922. /* Remove the forwarding ListSelectionListener from the old
  923. * selectionModel, and add it to the new one, if neccessary.
  924. */
  925. if (selectionListener != null) {
  926. this.selectionModel.removeListSelectionListener(selectionListener);
  927. selectionModel.addListSelectionListener(selectionListener);
  928. }
  929. ListSelectionModel oldValue = this.selectionModel;
  930. this.selectionModel = selectionModel;
  931. firePropertyChange("selectionModel", oldValue, selectionModel);
  932. }
  933. /**
  934. * Determines whether single-item or multiple-item
  935. * selections are allowed.
  936. * The following <code>selectionMode</code> values are allowed:
  937. * <ul>
  938. * <li> <code>SINGLE_SELECTION</code>
  939. * Only one list index can be selected at a time. In this
  940. * mode the <code>setSelectionInterval</code> and
  941. * <code>addSelectionInterval</code>
  942. * methods are equivalent, and only the second index
  943. * argument is used.
  944. * <li> <code>SINGLE_INTERVAL_SELECTION</code>
  945. * One contiguous index interval can be selected at a time.
  946. * In this mode <code>setSelectionInterval</code> and
  947. * <code>addSelectionInterval</code>
  948. * are equivalent.
  949. * <li> <code>MULTIPLE_INTERVAL_SELECTION</code>
  950. * In this mode, there's no restriction on what can be selected.
  951. * This is the default.
  952. * </ul>
  953. *
  954. * @param selectionMode an integer specifying the type of selections
  955. * that are permissible
  956. * @see #getSelectionMode
  957. * @beaninfo
  958. * description: The selection mode.
  959. * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
  960. * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
  961. * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
  962. */
  963. public void setSelectionMode(int selectionMode) {
  964. getSelectionModel().setSelectionMode(selectionMode);
  965. }
  966. /**
  967. * Returns whether single-item or multiple-item selections are allowed.
  968. *
  969. * @return the value of the <code>selectionMode</code> property
  970. * @see #setSelectionMode
  971. */
  972. public int getSelectionMode() {
  973. return getSelectionModel().getSelectionMode();
  974. }
  975. /**
  976. * Returns the first index argument from the most recent
  977. * <code>addSelectionModel</code> or <code>setSelectionInterval</code> call.
  978. * This is a convenience method that just delegates to the
  979. * <code>selectionModel</code>.
  980. *
  981. * @return the index that most recently anchored an interval selection
  982. * @see ListSelectionModel#getAnchorSelectionIndex
  983. * @see #addSelectionInterval
  984. * @see #setSelectionInterval
  985. * @see #addListSelectionListener
  986. */
  987. public int getAnchorSelectionIndex() {
  988. return getSelectionModel().getAnchorSelectionIndex();
  989. }
  990. /**
  991. * Returns the second index argument from the most recent
  992. * <code>addSelectionInterval</code> or <code>setSelectionInterval</code>
  993. * call.
  994. * This is a convenience method that just delegates to the
  995. * <code>selectionModel</code>.
  996. *
  997. * @return the index that most recently ended a interval selection
  998. * @see ListSelectionModel#getLeadSelectionIndex
  999. * @see #addSelectionInterval
  1000. * @see #setSelectionInterval
  1001. * @see #addListSelectionListener
  1002. * @beaninfo
  1003. * description: The lead selection index.
  1004. */
  1005. public int getLeadSelectionIndex() {
  1006. return getSelectionModel().getLeadSelectionIndex();
  1007. }
  1008. /**
  1009. * Returns the smallest selected cell index.
  1010. * This is a convenience method that just delegates to the
  1011. * <code>selectionModel</code>.
  1012. *
  1013. * @return the smallest selected cell index
  1014. * @see ListSelectionModel#getMinSelectionIndex
  1015. * @see #addListSelectionListener
  1016. */
  1017. public int getMinSelectionIndex() {
  1018. return getSelectionModel().getMinSelectionIndex();
  1019. }
  1020. /**
  1021. * Returns the largest selected cell index.
  1022. * This is a convenience method that just delegates to the
  1023. * <code>selectionModel</code>.
  1024. *
  1025. * @return the largest selected cell index
  1026. * @see ListSelectionModel#getMaxSelectionIndex
  1027. * @see #addListSelectionListener
  1028. */
  1029. public int getMaxSelectionIndex() {
  1030. return getSelectionModel().getMaxSelectionIndex();
  1031. }
  1032. /**
  1033. * Returns true if the specified index is selected.
  1034. * This is a convenience method that just delegates to the
  1035. * <code>selectionModel</code>.
  1036. *
  1037. * @param index index to be queried for selection state
  1038. * @return true if the specified index is selected
  1039. * @see ListSelectionModel#isSelectedIndex
  1040. * @see #setSelectedIndex
  1041. * @see #addListSelectionListener
  1042. */
  1043. public boolean isSelectedIndex(int index) {
  1044. return getSelectionModel().isSelectedIndex(index);
  1045. }
  1046. /**
  1047. * Returns true if nothing is selected.
  1048. * This is a convenience method that just delegates to the
  1049. * <code>selectionModel</code>.
  1050. *
  1051. * @return true if nothing is selected
  1052. * @see ListSelectionModel#isSelectionEmpty
  1053. * @see #clearSelection
  1054. * @see #addListSelectionListener
  1055. */
  1056. public boolean isSelectionEmpty() {
  1057. return getSelectionModel().isSelectionEmpty();
  1058. }
  1059. /**
  1060. * Clears the selection - after calling this method
  1061. * <code>isSelectionEmpty</code> will return true.
  1062. * This is a convenience method that just delegates to the
  1063. * <code>selectionModel</code>.
  1064. *
  1065. * @see ListSelectionModel#clearSelection
  1066. * @see #isSelectionEmpty
  1067. * @see #addListSelectionListener
  1068. */
  1069. public void clearSelection() {
  1070. getSelectionModel().clearSelection();
  1071. }
  1072. /**
  1073. * Selects the specified interval. Both the anchor and lead indices are
  1074. * included. It's not neccessary for anchor to be less than lead.
  1075. * This is a convenience method that just delegates to the
  1076. * <code>selectionModel</code>.
  1077. *
  1078. * @param anchor the first index to select
  1079. * @param lead the last index to select
  1080. * @see ListSelectionModel#setSelectionInterval
  1081. * @see #addSelectionInterval
  1082. * @see #removeSelectionInterval
  1083. * @see #addListSelectionListener
  1084. */
  1085. public void setSelectionInterval(int anchor, int lead) {
  1086. getSelectionModel().setSelectionInterval(anchor, lead);
  1087. }
  1088. /**
  1089. * Sets the selection to be the union of the specified interval with current
  1090. * selection. Both the anchor and lead indices are
  1091. * included. It's not neccessary for anchor to be less than lead.
  1092. * This is a convenience method that just delegates to the
  1093. * <code>selectionModel</code>.
  1094. *
  1095. * @param anchor the first index to add to the selection
  1096. * @param lead the last index to add to the selection
  1097. * @see ListSelectionModel#addSelectionInterval
  1098. * @see #setSelectionInterval
  1099. * @see #removeSelectionInterval
  1100. * @see #addListSelectionListener
  1101. */
  1102. public void addSelectionInterval(int anchor, int lead) {
  1103. getSelectionModel().addSelectionInterval(anchor, lead);
  1104. }
  1105. /**
  1106. * Sets the selection to be the set difference of the specified interval
  1107. * and the current selection. Both the anchor and lead indices are
  1108. * removed. It's not neccessary for anchor to be less than lead.
  1109. * This is a convenience method that just delegates to the
  1110. * <code>selectionModel</code>.
  1111. *
  1112. * @param anchor the first index to remove from the selection
  1113. * @param lead the last index to remove from the selection
  1114. * @see ListSelectionModel#removeSelectionInterval
  1115. * @see #setSelectionInterval
  1116. * @see #addSelectionInterval
  1117. * @see #addListSelectionListener
  1118. */
  1119. public void removeSelectionInterval(int index0, int index1) {
  1120. getSelectionModel().removeSelectionInterval(index0, index1);
  1121. }
  1122. /**
  1123. * Sets the data model's <code>isAdjusting</code> property to true,
  1124. * so that a single event will be generated when all of the selection
  1125. * events have finished (for example, when the mouse is being
  1126. * dragged over the list in selection mode).
  1127. *
  1128. * @param b the boolean value for the property value
  1129. * @see ListSelectionModel#setValueIsAdjusting
  1130. */
  1131. public void setValueIsAdjusting(boolean b) {
  1132. getSelectionModel().setValueIsAdjusting(b);
  1133. }
  1134. /**
  1135. * Returns the value of the data model's <code>isAdjusting</code> property.
  1136. * This value is true if multiple changes are being made.
  1137. *
  1138. * @return true if multiple selection-changes are occurring, as
  1139. * when the mouse is being dragged over the list
  1140. * @see ListSelectionModel#getValueIsAdjusting
  1141. */
  1142. public boolean getValueIsAdjusting() {
  1143. return getSelectionModel().getValueIsAdjusting();
  1144. }
  1145. /**
  1146. * Returns an array of all of the selected indices in increasing
  1147. * order.
  1148. *
  1149. * @return all of the selected indices, in increasing order
  1150. * @see #removeSelectionInterval
  1151. * @see #addListSelectionListener
  1152. */
  1153. public int[] getSelectedIndices() {
  1154. ListSelectionModel sm = getSelectionModel();
  1155. int iMin = sm.getMinSelectionIndex();
  1156. int iMax = sm.getMaxSelectionIndex();
  1157. if ((iMin < 0) || (iMax < 0)) {
  1158. return new int[0];
  1159. }
  1160. int[] rvTmp = new int[1+ (iMax - iMin)];
  1161. int n = 0;
  1162. for(int i = iMin; i <= iMax; i++) {
  1163. if (sm.isSelectedIndex(i)) {
  1164. rvTmp[n++] = i;
  1165. }
  1166. }
  1167. int[] rv = new int[n];
  1168. System.arraycopy(rvTmp, 0, rv, 0, n);
  1169. return rv;
  1170. }
  1171. /**
  1172. * Selects a single cell.
  1173. *
  1174. * @param index the index of the one cell to select
  1175. * @see ListSelectionModel#setSelectionInterval
  1176. * @see #isSelectedIndex
  1177. * @see #addListSelectionListener
  1178. * @beaninfo
  1179. * description: The index of the selected cell.
  1180. */
  1181. public void setSelectedIndex(int index) {
  1182. getSelectionModel().setSelectionInterval(index, index);
  1183. }
  1184. /**
  1185. * Selects a set of cells.
  1186. *
  1187. * @param indices an array of the indices of the cells to select
  1188. * @see ListSelectionModel#addSelectionInterval
  1189. * @see #isSelectedIndex
  1190. * @see #addListSelectionListener
  1191. */
  1192. public void setSelectedIndices(int[] indices) {
  1193. ListSelectionModel sm = getSelectionModel();
  1194. sm.clearSelection();
  1195. for(int i = 0; i < indices.length; i++) {
  1196. sm.addSelectionInterval(indices[i], indices[i]);
  1197. }
  1198. }
  1199. /**
  1200. * Returns an array of the values for the selected cells.
  1201. * The returned values are sorted in increasing index order.
  1202. *
  1203. * @return the selected values
  1204. * @see #isSelectedIndex
  1205. * @see #getModel
  1206. * @see #addListSelectionListener
  1207. */
  1208. public Object[] getSelectedValues() {
  1209. ListSelectionModel sm = getSelectionModel();
  1210. ListModel dm = getModel();
  1211. int iMin = sm.getMinSelectionIndex();
  1212. int iMax = sm.getMaxSelectionIndex();
  1213. if ((iMin < 0) || (iMax < 0)) {
  1214. return new Object[0];
  1215. }
  1216. Object[] rvTmp = new Object[1+ (iMax - iMin)];
  1217. int n = 0;
  1218. for(int i = iMin; i <= iMax; i++) {
  1219. if (sm.isSelectedIndex(i)) {
  1220. rvTmp[n++] = dm.getElementAt(i);
  1221. }
  1222. }
  1223. Object[] rv = new Object[n];
  1224. System.arraycopy(rvTmp, 0, rv, 0, n);
  1225. return rv;
  1226. }
  1227. /**
  1228. * Returns the first selected index; returns -1 if there is no
  1229. * selected item.
  1230. *
  1231. * @return the value of <code>getMinSelectionIndex</code>
  1232. * @see #getMinSelectionIndex
  1233. * @see #addListSelectionListener
  1234. */
  1235. public int getSelectedIndex() {
  1236. return getMinSelectionIndex();
  1237. }
  1238. /**
  1239. * Returns the first selected value, or <code>null</code> if the
  1240. * selection is empty.
  1241. *
  1242. * @return the first selected value
  1243. * @see #getMinSelectionIndex
  1244. * @see #getModel
  1245. * @see #addListSelectionListener
  1246. */
  1247. public Object getSelectedValue() {
  1248. int i = getMinSelectionIndex();
  1249. return (i == -1) ? null : getModel().getElementAt(i);
  1250. }
  1251. // PENDING(hmuller) this should move to BasicComboBoxUI
  1252. /**
  1253. * Selects the specified object from the list.
  1254. *
  1255. * @param anObject the object to select
  1256. * @param shouldScroll true if the list should scroll to display
  1257. * the selected object, if one exists; otherwise false
  1258. */
  1259. public void setSelectedValue(Object anObject,boolean shouldScroll) {
  1260. if(anObject == null)
  1261. setSelectedIndex(-1);
  1262. else if(!anObject.equals(getSelectedValue())) {
  1263. int i,c;
  1264. ListModel dm = getModel();
  1265. for(i=0,c=dm.getSize();i<c;i++)
  1266. if(anObject.equals(dm.getElementAt(i))){
  1267. setSelectedIndex(i);
  1268. if(shouldScroll)
  1269. ensureIndexIsVisible(i);
  1270. repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
  1271. return;
  1272. }
  1273. setSelectedIndex(-1);
  1274. }
  1275. repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
  1276. }
  1277. /**
  1278. * --- The Scrollable Implementation ---
  1279. */
  1280. private void checkScrollableParameters(Rectangle visibleRect, int orientation) {
  1281. if (visibleRect == null) {
  1282. throw new IllegalArgumentException("visibleRect must be non-null");
  1283. }
  1284. switch (orientation) {
  1285. case SwingConstants.VERTICAL:
  1286. case SwingConstants.HORIZONTAL:
  1287. break;
  1288. default:
  1289. throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
  1290. }
  1291. }
  1292. /**
  1293. * Computes the size of the viewport needed to display
  1294. * <code>visibleRowCount</code>
  1295. * rows. This is trivial if
  1296. * <code>fixedCellWidth</code> and <code>fixedCellHeight</code>
  1297. * were specified. Note that they can be specified implicitly with
  1298. * the <code>prototypeCellValue</code> property.
  1299. * If <code>fixedCellWidth</code> wasn't specified,
  1300. * it's computed by finding the widest list element.
  1301. * If <code>fixedCellHeight</code>
  1302. * wasn't specified then we resort to heuristics:
  1303. * <ul>
  1304. * <li>
  1305. * If the model isn't empty we just multiply the height of the first row
  1306. * by <code>visibleRowCount</code>.
  1307. * <li>
  1308. * If the model is empty, (<code>JList.getModel().getSize() == 0</code>),
  1309. * then we just allocate 16 pixels per visible row, and 256 pixels
  1310. * for the width (unless <code>fixedCellWidth</code> was set),
  1311. * and hope for the best.
  1312. * </ul>
  1313. *
  1314. * @return a dimension containing the size of the viewport needed
  1315. * to display <code>visibleRowCount</code> rows
  1316. * @see #getPreferredScrollableViewportSize
  1317. * @see #setPrototypeCellValue
  1318. */
  1319. public Dimension getPreferredScrollableViewportSize()
  1320. {
  1321. Insets insets = getInsets();
  1322. int dx = insets.left + insets.right;
  1323. int dy = insets.top + insets.bottom;
  1324. int visibleRowCount = getVisibleRowCount();
  1325. int fixedCellWidth = getFixedCellWidth();
  1326. int fixedCellHeight = getFixedCellHeight();
  1327. if ((fixedCellWidth > 0) && (fixedCellHeight > 0)) {
  1328. int width = fixedCellWidth + dx;
  1329. int height = (visibleRowCount * fixedCellHeight) + dy;
  1330. return new Dimension(width, height);
  1331. }
  1332. else if (getModel().getSize() > 0) {
  1333. int width = getPreferredSize().width;
  1334. Rectangle r = getCellBounds(0, 0);
  1335. int height = (visibleRowCount * r.height) + dy;
  1336. return new Dimension(width, height);
  1337. }
  1338. else {
  1339. fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth : 256;
  1340. fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight : 16;
  1341. return new Dimension(fixedCellWidth, fixedCellHeight * visibleRowCount);
  1342. }
  1343. }
  1344. /**
  1345. * Returns the distance to scroll to expose the next or previous
  1346. * row (for vertical scrolling) or character (for horizontal scrolling).
  1347. * For horizontal scrolling: return the lists font size or 1 if the font
  1348. * is <code>null</code>.
  1349. * We're using the font size instead of the width of some canonical string,
  1350. * e.g. "m", because it's cheaper.
  1351. * <p>
  1352. * For verticaL scrolling: if we're scrolling downwards (<code>direction</code> is
  1353. * greater than 0), and the first row is completely visible with respect
  1354. * to <code>visibleRect</code>, then return its height. If
  1355. * we're scrolling downwards and the first row is only partially visible,
  1356. * return the height of the visible part of the first row. Similarly
  1357. * if we're scrolling upwards we return the height of the row above
  1358. * the first row, unless the first row is partially visible.
  1359. * <p>
  1360. * Note that the value of <code>visibleRect</code> must be the equal to
  1361. * <code>this.getVisibleRect()</code>.
  1362. *
  1363. * @param visibleRect the visible rectangle
  1364. * @param orientation HORIZONTAL or VERTICAL
  1365. * @param direction if <= 0, then scroll UP; if > 0, then scroll DOWN
  1366. * @return the distance, in pixels, to scroll to expose the
  1367. * next or previous unit
  1368. * @see Scrollable#getScrollableUnitIncrement
  1369. * @throws IllegalArgumentException if visibleRect is <code>null<code>, or
  1370. * orientation isn't one of SwingConstants.VERTICAL,
  1371. * SwingConstants.HORIZONTAL.
  1372. *
  1373. */
  1374. public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
  1375. {
  1376. checkScrollableParameters(visibleRect, orientation);
  1377. if (orientation == SwingConstants.HORIZONTAL) {
  1378. Font f = getFont();
  1379. return (f != null) ? f.getSize() : 1;
  1380. }
  1381. else {
  1382. int row = getFirstVisibleIndex();
  1383. if (row == -1) {
  1384. return 0;
  1385. }
  1386. else {
  1387. /* Scroll Down */
  1388. if (direction > 0) {
  1389. Rectangle r = getCellBounds(row, row);
  1390. return (r == null) ? 0 : r.height - (visibleRect.y - r.y);
  1391. }
  1392. /* Scroll Up */
  1393. else {
  1394. Rectangle r = getCellBounds(row, row);
  1395. /* The first row is completely visible and it's row 0.
  1396. * We're done.
  1397. */
  1398. if ((r.y == visibleRect.y) && (row == 0)) {
  1399. return 0;
  1400. }
  1401. /* The first row is completely visible, return the
  1402. * height of the previous row.
  1403. */
  1404. else if (r.y == visibleRect.y) {
  1405. Rectangle prevR = getCellBounds(row - 1, row - 1);
  1406. return (prevR== null) ? 0 : prevR.height;
  1407. }
  1408. /* The first row is partially visible, return the
  1409. * height of hidden part.
  1410. */
  1411. else {
  1412. return visibleRect.y - r.y;
  1413. }
  1414. }
  1415. }
  1416. }
  1417. }
  1418. /**
  1419. * Returns the block increment amount.
  1420. *
  1421. * @param visibleRect the visible rectangle
  1422. * @param orientation HORIZONTAL or VERTICAL
  1423. * @param direction if <= 0, then scroll UP; if > 0, then scroll DOWN
  1424. * @return the visibleRect.height or visibleRect.width per the orientation
  1425. * @see Scrollable#getScrollableUnitIncrement
  1426. * @throws IllegalArgumentException if visibleRect is <code>null</code>, or
  1427. * orientation isn't one of SwingConstants.VERTICAL,
  1428. * SwingConstants.HORIZONTAL.
  1429. *
  1430. */
  1431. public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
  1432. checkScrollableParameters(visibleRect, orientation);
  1433. return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width;
  1434. }
  1435. /**
  1436. * Returns true if this <code>JList</code> is displayed in a
  1437. * <code>JViewport</code> and the viewport is wider than
  1438. * <code>JList</code>'s preferred width; otherwise returns false.
  1439. * If false, then don't track the viewport's width. This allows horizontal
  1440. * scrolling if the <code>JViewport</code> is itself embedded in a
  1441. * <code>JScrollPane</code>.
  1442. *
  1443. * @return true if viewport is wider than the <code>JList</code>'s
  1444. * preferred width, otherwise false
  1445. * @see Scrollable#getScrollableTracksViewportWidth
  1446. */
  1447. public boolean getScrollableTracksViewportWidth() {
  1448. if (getParent() instanceof JViewport) {
  1449. return (((JViewpor