GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* JList.java -- 2: Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.Color; 42: import java.awt.Component; 43: import java.awt.ComponentOrientation; 44: import java.awt.Dimension; 45: import java.awt.Point; 46: import java.awt.Rectangle; 47: import java.util.Vector; 48: 49: import javax.accessibility.Accessible; 50: import javax.accessibility.AccessibleContext; 51: import javax.swing.event.ListDataEvent; 52: import javax.swing.event.ListDataListener; 53: import javax.swing.event.ListSelectionEvent; 54: import javax.swing.event.ListSelectionListener; 55: import javax.swing.plaf.ListUI; 56: import javax.swing.text.Position; 57: 58: /** 59: * <p>This class is a facade over three separate objects: {@link 60: * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and 61: * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list" 62: * concept, with independently replacable (possibly client-provided) models 63: * for its contents and its current selection. In addition, each element in 64: * the list is rendered via a strategy class {@link 65: * javax.swing.ListCellRenderer}.</p> 66: * 67: * <p>Lists have many properties, some of which are stored in this class 68: * while others are delegated to the list's model or selection. The 69: * following properties are available:</p> 70: * 71: * <table> 72: * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> 73: * <tr><td>accessibleContext </td><td>list </td><td>no </td></tr> 74: * <tr><td>anchorSelectionIndex </td><td>selection</td><td>no </td></tr> 75: * <tr><td>cellRenderer </td><td>list </td><td>yes </td></tr> 76: * <tr><td>dragEnabled </td><td>list </td><td>no </td></tr> 77: * <tr><td>firstVisibleIndex </td><td>list </td><td>no </td></tr> 78: * <tr><td>fixedCellHeight </td><td>list </td><td>yes </td></tr> 79: * <tr><td>fixedCellWidth </td><td>list </td><td>yes </td></tr> 80: * <tr><td>lastVisibleIndex </td><td>list </td><td>no </td></tr> 81: * <tr><td>layoutOrientation </td><td>list </td><td>yes </td></tr> 82: * <tr><td>leadSelectionIndex </td><td>selection</td><td>no </td></tr> 83: * <tr><td>maxSelectionIndex </td><td>selection</td><td>no </td></tr> 84: * <tr><td>minSelectionIndex </td><td>selection</td><td>no </td></tr> 85: * <tr><td>model </td><td>list </td><td>yes </td></tr> 86: * <tr><td>opaque </td><td>list </td><td>no </td></tr> 87: * <tr><td>preferredScrollableViewportSize</td><td>list </td><td>no </td></tr> 88: * <tr><td>prototypeCellValue </td><td>list </td><td>yes </td></tr> 89: * <tr><td>scrollableTracksViewportHeight </td><td>list </td><td>no </td></tr> 90: * <tr><td>scrollableTracksViewportWidth </td><td>list </td><td>no </td></tr> 91: * <tr><td>selectedIndex </td><td>selection</td><td>no </td></tr> 92: * <tr><td>selectedIndices </td><td>selection</td><td>no </td></tr> 93: * <tr><td>selectedValue </td><td>model </td><td>no </td></tr> 94: * <tr><td>selectedValues </td><td>model </td><td>no </td></tr> 95: * <tr><td>selectionBackground </td><td>list </td><td>yes </td></tr> 96: * <tr><td>selectionEmpty </td><td>selection</td><td>no </td></tr> 97: * <tr><td>selectionForeground </td><td>list </td><td>yes </td></tr> 98: * <tr><td>selectionMode </td><td>selection</td><td>no </td></tr> 99: * <tr><td>selectionModel </td><td>list </td><td>yes </td></tr> 100: * <tr><td>UI </td><td>list </td><td>yes </td></tr> 101: * <tr><td>UIClassID </td><td>list </td><td>no </td></tr> 102: * <tr><td>valueIsAdjusting </td><td>list </td><td>no </td></tr> 103: * <tr><td>visibleRowCount </td><td>list </td><td>no </td></tr> 104: * </table> 105: * 106: * @author Graydon Hoare (graydon@redhat.com) 107: */ 108: 109: public class JList extends JComponent implements Accessible, Scrollable 110: { 111: private static final long serialVersionUID = 4406629526391098046L; 112: 113: /** 114: * Constant value used in "layoutOrientation" property. This value means 115: * that cells are laid out in a single vertical column. This is the default. 116: */ 117: public static final int VERTICAL = 0; 118: 119: /** 120: * Constant value used in "layoutOrientation" property. This value means 121: * that cells are laid out in multiple columns "newspaper style", filling 122: * vertically first, then horizontally. 123: */ 124: public static final int VERTICAL_WRAP = 1; 125: 126: /** 127: * Constant value used in "layoutOrientation" property. This value means 128: * that cells are laid out in multiple columns "newspaper style", 129: * filling horizontally first, then vertically. 130: */ 131: public static final int HORIZONTAL_WRAP = 2; 132: 133: /** 134: * This property indicates whether "drag and drop" functions are enabled 135: * on the list. 136: */ 137: boolean dragEnabled; 138: 139: /** This property provides a strategy for rendering cells in the list. */ 140: ListCellRenderer cellRenderer; 141: 142: /** 143: * This property indicates an fixed width to assign to all cells in the 144: * list. If its value is <code>-1</code>, no width has been 145: * assigned. This value can be set explicitly, or implicitly by setting 146: * the {@link #prototypeCellValue} property. 147: */ 148: int fixedCellWidth; 149: 150: /** 151: * This property indicates an fixed height to assign to all cells in the 152: * list. If its value is <code>-1</code>, no height has been 153: * assigned. This value can be set explicitly, or implicitly by setting 154: * the {@link #prototypeCellValue} property. 155: */ 156: int fixedCellHeight; 157: 158: /** 159: * This property holds the current layout orientation of the list, which 160: * is one of the integer constants {@link #VERTICAL}, {@link 161: * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}. 162: */ 163: int layoutOrientation; 164: 165: /** This property holds the data elements displayed by the list. */ 166: ListModel model; 167: 168: /** 169: * <p>This property holds a reference to a "prototype" data value -- 170: * typically a String -- which is used to calculate the {@link 171: * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the 172: * {@link #cellRenderer} property to acquire a component to render the 173: * prototype.</p> 174: * 175: * <p>It is important that you <em>not</em> set this value to a 176: * component. It has to be a <em>data value</em> such as the objects you 177: * would find in the list's model. Setting it to a component will have 178: * undefined (and undesirable) affects. </p> 179: */ 180: Object prototypeCellValue; 181: 182: /** 183: * This property specifies a foreground color for the selected cells in 184: * the list. When {@link ListCellRenderer.getListCellRendererComponent} 185: * is called with a selected cell object, the component returned will 186: * have its "foreground" set to this color. 187: */ 188: Color selectionBackground; 189: 190: /** 191: * This property specifies a background color for the selected cells in 192: * the list. When {@link ListCellRenderer.getListCellRendererComponent} 193: * is called with a selected cell object, the component returned will 194: * have its "background" property set to this color. 195: */ 196: Color selectionForeground; 197: 198: /** 199: * This property holds a description of which data elements in the {@link 200: * #model} property should be considered "selected", when displaying and 201: * interacting with the list. 202: */ 203: ListSelectionModel selectionModel; 204: 205: 206: /** 207: * This property indicates that the list's selection is currently 208: * "adjusting" -- perhaps due to a user actively dragging the mouse over 209: * multiple list elements -- and is therefore likely to change again in 210: * the near future. A {@link ListSelectionListener} might choose to delay 211: * updating its view of the list's selection until this property is 212: * false, meaning that the adjustment has completed. 213: */ 214: boolean valueIsAdjusting; 215: 216: /** 217: * This property indicates a <em>preference</em> for the number of rows 218: * displayed in the list, and will scale the 219: * {@link #preferredScrollableViewportSize} property accordingly. The actual 220: * number of displayed rows, when the list is placed in a real {@link 221: * Viewport} or other component, may be greater or less than this number. 222: */ 223: int visibleRowCount; 224: 225: /** 226: * Fire a {@link ListSelectionEvent} to all the registered ListSelectionListeners. 227: */ 228: protected void fireSelectionValueChanged(int firstIndex, int lastIndex, boolean isAdjusting) 229: { 230: ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting); 231: ListSelectionListener listeners[] = getListSelectionListeners(); 232: for (int i = 0; i < listeners.length; ++i) 233: { 234: listeners[i].valueChanged(evt); 235: } 236: } 237: 238: /** 239: * This private listener propagates {@link ListSelectionEvent} events 240: * from the list's "selectionModel" property to the list's {@link 241: * ListSelectionListener} listeners. It also listens to {@link 242: * ListDataEvent} events from the list's {@link #model} property. If this 243: * class receives either type of event, it triggers repainting of the 244: * list. 245: */ 246: private class ListListener 247: implements ListSelectionListener, ListDataListener 248: { 249: // ListDataListener events 250: public void contentsChanged(ListDataEvent event) 251: { 252: JList.this.revalidate(); 253: JList.this.repaint(); 254: } 255: public void intervalAdded(ListDataEvent event) 256: { 257: JList.this.revalidate(); 258: JList.this.repaint(); 259: } 260: public void intervalRemoved(ListDataEvent event) 261: { 262: JList.this.revalidate(); 263: JList.this.repaint(); 264: } 265: // ListSelectionListener events 266: public void valueChanged(ListSelectionEvent event) 267: { 268: JList.this.fireSelectionValueChanged(event.getFirstIndex(), 269: event.getLastIndex(), 270: event.getValueIsAdjusting()); 271: JList.this.repaint(); 272: } 273: }; 274: 275: /** 276: * Shared ListListener instance, subscribed to both the current {@link 277: * #model} and {@link #selectionModel} properties of the list. 278: */ 279: ListListener listListener; 280: 281: 282: /** 283: * Creates a new JList object. 284: */ 285: public JList() 286: { 287: init(); 288: } 289: 290: /** 291: * Creates a new JList object. 292: * 293: * @param listData Initial data to populate the list with 294: */ 295: public JList(Object[] listData) 296: { 297: init(); 298: setListData(listData); 299: } 300: 301: /** 302: * Creates a new JList object. 303: * 304: * @param listData Initial data to populate the list with 305: */ 306: public JList(Vector listData) 307: { 308: init(); 309: setListData(listData); 310: } 311: 312: /** 313: * Creates a new JList object. 314: * 315: * @param listData Initial data to populate the list with 316: */ 317: public JList(ListModel listData) 318: { 319: init(); 320: setModel(listData); 321: } 322: 323: void init() 324: { 325: dragEnabled = false; 326: fixedCellHeight = -1; 327: fixedCellWidth = -1; 328: layoutOrientation = VERTICAL; 329: opaque = true; 330: valueIsAdjusting = false; 331: visibleRowCount = 8; 332: 333: cellRenderer = new DefaultListCellRenderer(); 334: listListener = new ListListener(); 335: 336: setModel(new DefaultListModel()); 337: setSelectionModel(createSelectionModel()); 338: 339: updateUI(); 340: } 341: 342: /** 343: * Creates the default <code>ListSelectionModel</code>. 344: * 345: * @return the <code>ListSelectionModel</code> 346: */ 347: protected ListSelectionModel createSelectionModel() 348: { 349: return new DefaultListSelectionModel(); 350: } 351: 352: /** 353: * Gets the value of the {@link #fixedCellHeight} property. This property 354: * may be <code>-1</code> to indicate that no cell height has been 355: * set. This property is also set implicitly when the 356: * {@link #prototypeCellValue} property is set. 357: * 358: * @return The current value of the property 359: * 360: * @see #fixedCellHeight 361: * @see #setFixedCellHeight 362: * @see #setPrototypeCellValue 363: */ 364: public int getFixedCellHeight() 365: { 366: return fixedCellHeight; 367: } 368: 369: /** 370: * Sets the value of the {@link #fixedCellHeight} property. This property 371: * may be <code>-1</code> to indicate that no cell height has been 372: * set. This property is also set implicitly when the {@link 373: * #prototypeCellValue} property is set, but setting it explicitly 374: * overrides the height computed from {@link #prototypeCellValue}. 375: * 376: * @see #getFixedCellHeight 377: * @see #getPrototypeCellValue 378: */ 379: public void setFixedCellHeight(int h) 380: { 381: if (fixedCellHeight == h) 382: return; 383: 384: int old = fixedCellHeight; 385: fixedCellHeight = h; 386: firePropertyChange("fixedCellWidth", old, h); 387: } 388: 389: 390: /** 391: * Gets the value of the {@link #fixedCellWidth} property. This property 392: * may be <code>-1</code> to indicate that no cell width has been 393: * set. This property is also set implicitly when the {@link 394: * #prototypeCellValue} property is set. 395: * 396: * @return The current value of the property 397: * 398: * @see #setFixedCellWidth 399: * @see #setPrototypeCellValue 400: */ 401: public int getFixedCellWidth() 402: { 403: return fixedCellWidth; 404: } 405: 406: /** 407: * Sets the value of the {@link #fixedCellWidth} property. This property 408: * may be <code>-1</code> to indicate that no cell width has been 409: * set. This property is also set implicitly when the {@link 410: * #prototypeCellValue} property is set, but setting it explicitly 411: * overrides the width computed from {@link #prototypeCellValue}. 412: * 413: * @see #getFixedCellHeight 414: * @see #getPrototypeCellValue 415: */ 416: public void setFixedCellWidth(int w) 417: { 418: if (fixedCellWidth == w) 419: return; 420: 421: int old = fixedCellWidth; 422: fixedCellWidth = w; 423: firePropertyChange("fixedCellWidth", old, w); 424: } 425: 426: /** 427: * Gets the value of the {@link #visibleRowCount} property. 428: * 429: * @return the current value of the property. 430: */ 431: 432: public int getVisibleRowCount() 433: { 434: return visibleRowCount; 435: } 436: 437: /** 438: * Sets the value of the {@link #visibleRowCount} property. 439: * 440: * @param visibleRowCount The new property value 441: */ 442: public void setVisibleRowCount(int vc) 443: { 444: visibleRowCount = vc; 445: revalidate(); 446: repaint(); 447: } 448: 449: /** 450: * Adds a {@link ListSelectionListener} to the listener list for this 451: * list. The listener will be called back with a {@link 452: * ListSelectionEvent} any time the list's {@link #selectionModel} 453: * property changes. The source of such events will be the JList, 454: * not the selection model. 455: * 456: * @param listener The new listener to add 457: */ 458: public void addListSelectionListener(ListSelectionListener listener) 459: { 460: listenerList.add (ListSelectionListener.class, listener); 461: } 462: 463: /** 464: * Removes a {@link ListSelectionListener} from the listener list for 465: * this list. The listener will no longer be called when the list's 466: * {@link #selectionModel} changes. 467: * 468: * @param listener The listener to remove 469: */ 470: public void removeListSelectionListener(ListSelectionListener listener) 471: { 472: listenerList.remove(ListSelectionListener.class, listener); 473: } 474: 475: /** 476: * Returns an array of all ListSelectionListeners subscribed to this 477: * list. 478: * 479: * @return The current subscribed listeners 480: * 481: * @since 1.4 482: */ 483: public ListSelectionListener[] getListSelectionListeners() 484: { 485: return (ListSelectionListener[]) getListeners(ListSelectionListener.class); 486: } 487: 488: public int getSelectionMode() 489: { 490: return selectionModel.getSelectionMode(); 491: } 492: 493: /** 494: * Sets the list's "selectionMode" property, which simply mirrors the 495: * same property on the list's {@link #selectionModel} property. This 496: * property should be one of the integer constants 497: * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>, 498: * or <code>MULTIPLE_INTERVAL_SELECTION</code> from the {@link 499: * ListSelectionModel} interface. 500: * 501: * @param a The new selection mode 502: */ 503: public void setSelectionMode(int a) 504: { 505: selectionModel.setSelectionMode(a); 506: } 507: 508: /** 509: * Adds the interval <code>[a,a]</code> to the set of selections managed 510: * by this list's {@link #selectionModel} property. Depending on the 511: * selection mode, this may cause existing selections to become invalid, 512: * or may simply expand the set of selections. 513: * 514: * @param a A number in the half-open range <code>[0, x)</code> where 515: * <code>x = getModel.getSize()</code>, indicating the index of an 516: * element in the list to select. 517: * 518: * @see #setSelectionMode 519: * @see #selectionModel 520: */ 521: public void setSelectedIndex(int a) 522: { 523: selectionModel.setSelectionInterval(a, a); 524: } 525: 526: /** 527: * For each element <code>a[i]</code> of the provided array 528: * <code>a</code>, calls {@link #setSelectedIndex} on <code>a[i]</code>. 529: * 530: * @see #setSelectionMode 531: * @see #selectionModel 532: */ 533: public void setSelectedIndices(int [] a) 534: { 535: for (int i = 0; i < a.length; ++i) 536: setSelectedIndex(a[i]); 537: } 538: 539: /** 540: * Returns the minimum index of an element in the list which is currently 541: * selected. 542: * 543: * @return A number in the half-open range <code>[0, x)</code> where 544: * <code>x = getModel.getSize()</code>, indicating the minimum index of 545: * an element in the list for which the element is selected, or 546: * <code>-1</code> if no elements are selected 547: */ 548: public int getSelectedIndex() 549: { 550: return selectionModel.getMinSelectionIndex(); 551: } 552: 553: /** 554: * Returns <code>true</code> if the model's selection is empty, otherwise 555: * <code>false</code>. 556: * 557: * @return The return value of {@link ListSelectionModel#isSelectionEmpty} 558: */ 559: public boolean isSelectionEmpty() 560: { 561: return selectionModel.isSelectionEmpty(); 562: } 563: 564: /** 565: * Returns the list index of the upper left or upper right corner of the 566: * {@link #visibleRect} property, depending on the {@link 567: * #componentOrientation} property. 568: * 569: * @return The index of the first visible list cell, or <code>-1</code> 570: * if none is visible. 571: */ 572: public int getFirstVisibleIndex() 573: { 574: ComponentOrientation or = getComponentOrientation(); 575: Rectangle r = getVisibleRect(); 576: if (or == ComponentOrientation.RIGHT_TO_LEFT) 577: r.translate((int) r.getWidth() - 1, 0); 578: return getUI().locationToIndex(this, r.getLocation()); 579: } 580: 581: 582: /** 583: * Returns index of the cell to which specified location is closest to 584: * @param location for which to look for in the list 585: * 586: * @return index of the cell to which specified location is closest to. 587: */ 588: public int locationToIndex(Point location) { 589: return getUI().locationToIndex(this, location); 590: } 591: 592: /** 593: * Returns location of the cell located at the specified index in the list. 594: * @param index of the cell for which location will be determined 595: * 596: * @return location of the cell located at the specified index in the list. 597: */ 598: public Point indexToLocation(int index){ 599: return getCellBounds(index, index).getLocation(); 600: } 601: 602: /** 603: * Returns the list index of the lower right or lower left corner of the 604: * {@link #visibleRect} property, depending on the {@link 605: * #componentOrientation} property. 606: * 607: * @return The index of the last visible list cell, or <code>-1</code> 608: * if none is visible. 609: */ 610: public int getLastVisibleIndex() 611: { 612: ComponentOrientation or = getComponentOrientation(); 613: Rectangle r = getVisibleRect(); 614: r.translate(0, (int) r.getHeight() - 1); 615: if (or == ComponentOrientation.LEFT_TO_RIGHT) 616: r.translate((int) r.getWidth() - 1, 0); 617: if (getUI().locationToIndex(this, r.getLocation()) == -1 618: && indexToLocation(getModel().getSize() - 1).y < r.y) 619: return getModel().getSize() - 1; 620: return getUI().locationToIndex(this, r.getLocation()); 621: } 622: 623: /** 624: * Returns the indices of values in the {@link #model} property which are 625: * selected. 626: * 627: * @return An array of model indices, each of which is selected according 628: * to the {@link #selection} property 629: */ 630: public int[] getSelectedIndices() 631: { 632: int lo, hi, n, i, j; 633: if (selectionModel.isSelectionEmpty()) 634: return new int[0]; 635: lo = selectionModel.getMinSelectionIndex(); 636: hi = selectionModel.getMaxSelectionIndex(); 637: n = 0; 638: for (i = lo; i <= hi; ++i) 639: if (selectionModel.isSelectedIndex(i)) 640: n++; 641: int [] v = new int[n]; 642: j = 0; 643: for (i = lo; i < hi; ++i) 644: if (selectionModel.isSelectedIndex(i)) 645: v[j++] = i; 646: return v; 647: } 648: 649: /** 650: * Indicates whether the list element at a given index value is 651: * currently selected. 652: * 653: * @param a The index to check 654: * @return <code>true</code> if <code>a</code> is the index of a selected 655: * list element 656: */ 657: public boolean isSelectedIndex(int a) 658: { 659: return selectionModel.isSelectedIndex(a); 660: } 661: 662: /** 663: * Returns the first value in the list's {@link #model} property which is 664: * selected, according to the list's {@link #selectionModel} property. 665: * This is equivalent to calling 666: * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check 667: * for the special index value of <code>-1</code> which returns null 668: * <code>null</code>. 669: * 670: * @return The first selected element, or <code>null</code> if no element 671: * is selected. 672: * 673: * @see getSelectedValues 674: */ 675: public Object getSelectedValue() 676: { 677: int index = getSelectedIndex(); 678: if (index == -1) 679: return null; 680: return getModel().getElementAt(index); 681: } 682: 683: /** 684: * Returns all the values in the list's {@link #model} property which 685: * are selected, according to the list's {@link #selectionModel} property. 686: * 687: * @return An array containing all the selected values 688: * 689: * @see getSelectedValue 690: */ 691: public Object[] getSelectedValues() 692: { 693: int [] idx = getSelectedIndices(); 694: Object [] v = new Object[idx.length]; 695: for (int i = 0; i < idx.length; ++i) 696: v[i] = getModel().getElementAt(i); 697: return v; 698: } 699: 700: /** 701: * Gets the value of the {@link #selectionBackground} property. 702: * 703: * @return The current value of the property 704: */ 705: public Color getSelectionBackground() 706: { 707: return selectionBackground; 708: } 709: 710: /** 711: * Sets the value of the {@link #selectionBackground} property. 712: * 713: * @param c The new value of the property 714: */ 715: public void setSelectionBackground(Color c) 716: { 717: if (selectionBackground == c) 718: return; 719: 720: Color old = selectionBackground; 721: selectionBackground = c; 722: firePropertyChange("selectionBackground", old, c); 723: repaint(); 724: } 725: 726: /** 727: * Gets the value of the {@link #selectionForeground} property. 728: * 729: * @return The current value of the property 730: */ 731: public Color getSelectionForeground() 732: { 733: return selectionForeground; 734: } 735: 736: /** 737: * Sets the value of the {@link #selectionForeground} property. 738: * 739: * @param c The new value of the property 740: */ 741: public void setSelectionForeground(Color c) 742: { 743: if (selectionForeground == c) 744: return; 745: 746: Color old = selectionForeground; 747: selectionForeground = c; 748: firePropertyChange("selectionForeground", old, c); 749: } 750: 751: /** 752: * Sets the selection to cover only the specified value, if it 753: * exists in the model. 754: * 755: * @param obj The object to select 756: * @param scroll Whether to scroll the list to make the newly selected 757: * value visible 758: * 759: * @see #ensureIndexIsVisible 760: */ 761: 762: public void setSelectedValue(Object obj, boolean scroll) 763: { 764: for (int i = 0; i < model.getSize(); ++i) 765: { 766: if (model.getElementAt(i).equals(obj)) 767: { 768: setSelectedIndex(i); 769: if (scroll) 770: ensureIndexIsVisible(i); 771: break; 772: } 773: } 774: } 775: 776: /** 777: * Scrolls this list to make the specified cell visible. This 778: * only works if the list is contained within a viewport. 779: * 780: * @param i The list index to make visible 781: * 782: * @see JComponent#scrollRectToVisible 783: */ 784: public void ensureIndexIsVisible(int i) 785: { 786: scrollRectToVisible(getUI().getCellBounds(this, i, i)); 787: } 788: 789: /** 790: * Sets the {@link #model} property of the list to a new anonymous 791: * {@link AbstractListModel} subclass which accesses the provided Object 792: * array directly. 793: * 794: * @param listData The object array to build a new list model on 795: * @see #setModel 796: */ 797: public void setListData(final Object[] listData) 798: { 799: setModel(new AbstractListModel() 800: { 801: public int getSize() 802: { 803: return listData.length; 804: } 805: 806: public Object getElementAt(int i) 807: { 808: return listData[i]; 809: } 810: }); 811: } 812: 813: /** 814: * Sets the {@link #model} property of the list to a new anonymous {@link 815: * AbstractListModel} subclass which accesses the provided vector 816: * directly. 817: * 818: * @param listData The object array to build a new list model on 819: * @see #setModel 820: */ 821: public void setListData(final Vector listData) 822: { 823: setModel(new AbstractListModel() 824: { 825: public int getSize() 826: { 827: return listData.size(); 828: } 829: 830: public Object getElementAt(int i) 831: { 832: return listData.elementAt(i); 833: } 834: }); 835: } 836: 837: /** 838: * Gets the value of the {@link #cellRenderer} property. 839: * 840: * @return The current value of the property 841: */ 842: public ListCellRenderer getCellRenderer() 843: { 844: return cellRenderer; 845: } 846: 847: /** 848: * Sets the value of the {@link #celLRenderer} property. 849: * 850: * @param renderer The new property value 851: */ 852: public void setCellRenderer(ListCellRenderer renderer) 853: { 854: if (cellRenderer == renderer) 855: return; 856: 857: ListCellRenderer old = cellRenderer; 858: cellRenderer = renderer; 859: firePropertyChange("cellRenderer", old, renderer); 860: revalidate(); 861: repaint(); 862: } 863: 864: /** 865: * Gets the value of the {@link #model} property. 866: * 867: * @return The current value of the property 868: */ 869: public ListModel getModel() 870: { 871: return model; 872: } 873: 874: /** 875: * Sets the value of the {@link #model} property. The list's {@link 876: * #listListener} is unsubscribed from the existing model, if it exists, 877: * and re-subscribed to the new model. 878: * 879: * @param model The new property value 880: */ 881: public void setModel(ListModel model) 882: { 883: if (this.model == model) 884: return; 885: 886: if (this.model != null) 887: this.model.removeListDataListener(listListener); 888: 889: ListModel old = this.model; 890: this.model = model; 891: 892: if (this.model != null) 893: this.model.addListDataListener(listListener); 894: 895: firePropertyChange("model", old, model); 896: revalidate(); 897: repaint(); 898: } 899: 900: 901: public ListSelectionModel getSelectionModel() 902: { 903: return selectionModel; 904: } 905: 906: /** 907: * Sets the value of the {@link #selectionModel} property. The list's 908: * {@link #listListener} is unsubscribed from the existing selection 909: * model, if it exists, and re-subscribed to the new selection model. 910: * 911: * @param model The new property value 912: */ 913: public void setSelectionModel(ListSelectionModel model) 914: { 915: if (selectionModel == model) 916: return; 917: 918: if (selectionModel != null) 919: selectionModel.removeListSelectionListener(listListener); 920: 921: ListSelectionModel old = selectionModel; 922: selectionModel = model; 923: 924: if (selectionModel != null) 925: selectionModel.addListSelectionListener(listListener); 926: 927: firePropertyChange("selectionModel", old, model); 928: revalidate(); 929: repaint(); 930: } 931: 932: /** 933: * Gets the value of the UI property. 934: * 935: * @return The current property value 936: */ 937: public ListUI getUI() 938: { 939: return (ListUI) ui; 940: } 941: 942: /** 943: * Sets the value of the UI property. 944: * 945: * @param ui The new property value 946: */ 947: public void setUI(ListUI ui) 948: { 949: super.setUI(ui); 950: } 951: 952: /** 953: * Calls {@link #setUI} with the {@link ListUI} subclass 954: * returned from calling {@link UIManager#getUI}. 955: */ 956: public void updateUI() 957: { 958: setUI((ListUI) UIManager.getUI(this)); 959: } 960: 961: /** 962: * Return the class identifier for the list's UI property. This should 963: * be the constant string <code>"ListUI"</code>, and map to an 964: * appropriate UI class in the {@link UIManager}. 965: * 966: * @return The class identifier 967: */ 968: public String getUIClassID() 969: { 970: return "ListUI"; 971: } 972: 973: 974: /** 975: * Returns the current value of the {@link #prototypeCellValue} 976: * property. This property holds a reference to a "prototype" data value 977: * -- typically a String -- which is used to calculate the {@link 978: * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the 979: * {@link #cellRenderer} property to acquire a component to render the 980: * prototype. 981: * 982: * @return The current prototype cell value 983: * @see #setPrototypeCellValue 984: */ 985: public Object getPrototypeCellValue() 986: { 987: return prototypeCellValue; 988: } 989: 990: /** 991: * <p>Set the {@link #prototypeCellValue} property. This property holds a 992: * reference to a "prototype" data value -- typically a String -- which 993: * is used to calculate the {@link #fixedCellWidth} and {@link 994: * #fixedCellHeight} properties, using the {@link #cellRenderer} property 995: * to acquire a component to render the prototype.</p> 996: * 997: * <p>It is important that you <em>not</em> set this value to a 998: * component. It has to be a <em>data value</em> such as the objects you 999: * would find in the list's model. Setting it to a component will have 1000: * undefined (and undesirable) affects. </p> 1001: * 1002: * @param obj The new prototype cell value 1003: * @see #getPrototypeCellValue 1004: */ 1005: public void setPrototypeCellValue(Object obj) 1006: { 1007: if (prototypeCellValue == obj) 1008: return; 1009: 1010: Object old = prototypeCellValue; 1011: Component comp = getCellRenderer() 1012: .getListCellRendererComponent(this, obj, 0, false, false); 1013: Dimension d = comp.getPreferredSize(); 1014: fixedCellWidth = d.width; 1015: fixedCellHeight = d.height; 1016: prototypeCellValue = obj; 1017: firePropertyChange("prototypeCellValue", old, obj); 1018: } 1019: 1020: public AccessibleContext getAccessibleContext() 1021: { 1022: return null; 1023: } 1024: 1025: /** 1026: * Returns a size indicating how much space this list would like to 1027: * consume, when contained in a scrollable viewport. This is part of the 1028: * {@link Scrollable} interface, which interacts with {@link 1029: * ScrollPaneLayout} and {@link Viewport} to define scrollable objects. 1030: * 1031: * @return The preferred size 1032: */ 1033: public Dimension getPreferredScrollableViewportSize() 1034: { 1035: //If the layout orientation is not VERTICAL, then this will 1036: //return the value from getPreferredSize. The current ListUI is 1037: //expected to override getPreferredSize to return an appropriate value. 1038: if (getLayoutOrientation() != VERTICAL) 1039: return getPreferredSize(); 1040: 1041: if (fixedCellHeight != -1 && fixedCellWidth != -1) 1042: return new Dimension(fixedCellWidth, getModel().getSize() * 1043: fixedCellHeight); 1044: 1045: int prefWidth, prefHeight; 1046: if (fixedCellWidth != -1) 1047: prefWidth = fixedCellWidth; 1048: else 1049: { 1050: prefWidth = 0; 1051: int size = getModel().getSize(); 1052: for (int i = 0; i < size; i++) 1053: if (getCellBounds(i, i).width > prefWidth) 1054: prefWidth = getCellBounds(i, i).width; 1055: } 1056: 1057: if (getModel().getSize() == 0 && fixedCellWidth == -1) 1058: return new Dimension(256, 16 * getVisibleRowCount()); 1059: else if (getModel().getSize() == 0) 1060: return new Dimension (fixedCellWidth, 16 * getVisibleRowCount()); 1061: 1062: if (fixedCellHeight != -1) 1063: prefHeight = fixedCellHeight; 1064: else 1065: { 1066: prefHeight = getVisibleRowCount() * getCellBounds 1067: (getFirstVisibleIndex(), getFirstVisibleIndex()).height; 1068: } 1069: return new Dimension (prefWidth, prefHeight); 1070: } 1071: 1072: /** 1073: * <p>Return the number of pixels the list must scroll in order to move a 1074: * "unit" of the list into the provided visible rectangle. When the 1075: * provided direction is positive, the call describes a "downwards" 1076: * scroll, which will be exposing a cell at a <em>greater</em> index in 1077: * the list than those elements currently showing. Then the provided 1078: * direction is negative, the call describes an "upwards" scroll, which 1079: * will be exposing a cell at a <em>lesser</em> index in the list than 1080: * those elements currently showing.</p> 1081: * 1082: * <p>If the provided orientation is <code>HORIZONTAL</code>, the above 1083: * comments refer to "rightwards" for positive direction, and "leftwards" 1084: * for negative.</p> 1085: * 1086: * 1087: * @param visibleRect The rectangle to scroll an element into 1088: * @param orientation One of the numeric consants <code>VERTICAL</code> 1089: * or <code>HORIZONTAL</code> 1090: * @param direction An integer indicating the scroll direction: positive means 1091: * forwards (down, right), negative means backwards (up, left) 1092: * 1093: * @return The scrollable unit increment, in pixels 1094: */ 1095: public int getScrollableUnitIncrement(Rectangle visibleRect, 1096: int orientation, int direction) 1097: { 1098: ListUI lui = this.getUI(); 1099: if (orientation == SwingConstants.VERTICAL) 1100: { 1101: if (direction > 0) 1102: { 1103: // Scrolling down 1104: Point bottomLeft = new Point(visibleRect.x, 1105: visibleRect.y + visibleRect.height); 1106: int curIdx = lui.locationToIndex(this, bottomLeft); 1107: Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); 1108: if (curBounds.y + curBounds.height == bottomLeft.y) 1109: { 1110: // we are at the exact bottom of the current cell, so we 1111: // are being asked to scroll to the end of the next one 1112: if (curIdx + 1 < model.getSize()) 1113: { 1114: // there *is* a next item in the list 1115: Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1); 1116: return nxtBounds.height; 1117: } 1118: else 1119: { 1120: // no next item, no advance possible 1121: return 0; 1122: } 1123: } 1124: else 1125: { 1126: // we are part way through an existing cell, so we are being 1127: // asked to scroll to the bottom of it 1128: return (curBounds.y + curBounds.height) - bottomLeft.y; 1129: } 1130: } 1131: else 1132: { 1133: // scrolling up 1134: Point topLeft = new Point(visibleRect.x, visibleRect.y); 1135: int curIdx = lui.locationToIndex(this, topLeft); 1136: Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); 1137: if (curBounds.y == topLeft.y) 1138: { 1139: // we are at the exact top of the current cell, so we 1140: // are being asked to scroll to the top of the previous one 1141: if (curIdx > 0) 1142: { 1143: // there *is* a previous item in the list 1144: Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1); 1145: return -nxtBounds.height; 1146: } 1147: else 1148: { 1149: // no previous item, no advance possible 1150: return 0; 1151: } 1152: } 1153: else 1154: { 1155: // we are part way through an existing cell, so we are being 1156: // asked to scroll to the top of it 1157: return curBounds.y - topLeft.y; 1158: } 1159: } 1160: } 1161: 1162: // FIXME: handle horizontal scrolling (also wrapping?) 1163: return 1; 1164: } 1165: 1166: /** 1167: * <p>Return the number of pixels the list must scroll in order to move a 1168: * "block" of the list into the provided visible rectangle. When the 1169: * provided direction is positive, the call describes a "downwards" 1170: * scroll, which will be exposing a cell at a <em>greater</em> index in 1171: * the list than those elements currently showing. Then the provided 1172: * direction is negative, the call describes an "upwards" scroll, which 1173: * will be exposing a cell at a <em>lesser</em> index in the list than 1174: * those elements currently showing.</p> 1175: * 1176: * <p>If the provided orientation is <code>HORIZONTAL</code>, the above 1177: * comments refer to "rightwards" for positive direction, and "leftwards" 1178: * for negative.</p> 1179: * 1180: * 1181: * @param visibleRect The rectangle to scroll an element into 1182: * @param orientation One of the numeric consants <code>VERTICAL</code> 1183: * or <code>HORIZONTAL</code> 1184: * @param direction An integer indicating the scroll direction: positive means 1185: * forwards (down, right), negative means backwards (up, left) 1186: * 1187: * @return The scrollable unit increment, in pixels 1188: */ 1189: public int getScrollableBlockIncrement(Rectangle visibleRect, 1190: int orientation, int direction) 1191: { 1192: if (orientation == VERTICAL) 1193: return visibleRect.height * direction; 1194: else 1195: return visibleRect.width * direction; 1196: } 1197: 1198: /** 1199: * Gets the value of the {@link #scrollableTracksViewportWidth} property. 1200: * 1201: * @return <code>true</code> if the viewport is larger (horizontally) 1202: * than the list and the list should be expanded to fit the viewport; 1203: * <code>false</code> if the viewport is smaller than the list and the 1204: * list should scroll (horizontally) within the viewport 1205: */ 1206: public boolean getScrollableTracksViewportWidth() 1207: { 1208: Component parent = getParent(); 1209: boolean retVal = false; 1210: if (parent instanceof JViewport) 1211: { 1212: JViewport viewport = (JViewport) parent; 1213: Dimension pref = getPreferredSize(); 1214: if (viewport.getSize().width > pref.width) 1215: retVal = true; 1216: if ((getLayoutOrientation() == HORIZONTAL_WRAP) 1217: && (getVisibleRowCount() <= 0)) 1218: retVal = true; 1219: } 1220: return retVal; 1221: } 1222: 1223: /** 1224: * Gets the value of the {@link #scrollableTracksViewportWidth} property. 1225: * 1226: * @return <code>true</code> if the viewport is larger (vertically) 1227: * than the list and the list should be expanded to fit the viewport; 1228: * <code>false</code> if the viewport is smaller than the list and the 1229: * list should scroll (vertically) within the viewport 1230: */ 1231: public boolean getScrollableTracksViewportHeight() 1232: { 1233: Component parent = getParent(); 1234: boolean retVal = false; 1235: if (parent instanceof JViewport) 1236: { 1237: JViewport viewport = (JViewport) parent; 1238: Dimension pref = getPreferredSize(); 1239: if (viewport.getSize().height > pref.height) 1240: retVal = true; 1241: if ((getLayoutOrientation() == VERTICAL_WRAP) 1242: && (getVisibleRowCount() <= 0)) 1243: retVal = true; 1244: } 1245: return retVal; 1246: } 1247: 1248: public int getAnchorSelectionIndex() 1249: { 1250: return selectionModel.getAnchorSelectionIndex(); 1251: } 1252: 1253: public int getLeadSelectionIndex() 1254: { 1255: return selectionModel.getLeadSelectionIndex(); 1256: } 1257: 1258: public int getMinSelectionIndex() 1259: { 1260: return selectionModel.getMaxSelectionIndex(); 1261: } 1262: 1263: public int getMaxSelectionIndex() 1264: { 1265: return selectionModel.getMaxSelectionIndex(); 1266: } 1267: 1268: public void clearSelection() 1269: { 1270: selectionModel.clearSelection(); 1271: } 1272: 1273: public void setSelectionInterval(int anchor, int lead) 1274: { 1275: selectionModel.setSelectionInterval(anchor, lead); 1276: } 1277: 1278: public void addSelectionInterval(int anchor, int lead) 1279: { 1280: selectionModel.addSelectionInterval(anchor, lead); 1281: } 1282: 1283: public void removeSelectionInterval(int index0, int index1) 1284: { 1285: selectionModel.removeSelectionInterval(index0, index1); 1286: } 1287: 1288: /** 1289: * Returns the value of the <code>valueIsAdjusting</code> property. 1290: * 1291: * @return the value 1292: */ 1293: public boolean getValueIsAdjusting() 1294: { 1295: return valueIsAdjusting; 1296: } 1297: 1298: /** 1299: * Sets the <code>valueIsAdjusting</code> property. 1300: * 1301: * @param isAdjusting the new value 1302: */ 1303: public void setValueIsAdjusting(boolean isAdjusting) 1304: { 1305: valueIsAdjusting = isAdjusting; 1306: } 1307: 1308: /** 1309: * Return the value of the <code>dragEnabled</code> property. 1310: * 1311: * @return the value 1312: * 1313: * @since 1.4 1314: */ 1315: public boolean getDragEnabled() 1316: { 1317: return dragEnabled; 1318: } 1319: 1320: /** 1321: * Set the <code>dragEnabled</code> property. 1322: * 1323: * @param enabled new value 1324: * 1325: * @since 1.4 1326: */ 1327: public void setDragEnabled(boolean enabled) 1328: { 1329: dragEnabled = enabled; 1330: } 1331: 1332: /** 1333: * Returns the layout orientation. 1334: * 1335: * @return the orientation, one of <code>JList.VERTICAL</code>, 1336: * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> 1337: * 1338: * @since 1.4 1339: */ 1340: public int getLayoutOrientation() 1341: { 1342: return layoutOrientation; 1343: } 1344: 1345: /** 1346: * Sets the layout orientation. 1347: * 1348: * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>, 1349: * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> 1350: * 1351: * @since 1.4 1352: */ 1353: public void setLayoutOrientation(int orientation) 1354: { 1355: if (layoutOrientation == orientation) 1356: return; 1357: 1358: int old = layoutOrientation; 1359: layoutOrientation = orientation; 1360: firePropertyChange("layoutOrientation", old, orientation); 1361: } 1362: 1363: /** 1364: * Returns the bounds of the rectangle that encloses both list cells 1365: * with index0 and index1. 1366: * 1367: * @param index0 the index of the first cell 1368: * @param index1 the index of the second cell 1369: * 1370: * @return the bounds of the rectangle that encloses both list cells 1371: * with index0 and index1, <code>null</code> if one of the indices is 1372: * not valid 1373: */ 1374: public Rectangle getCellBounds(int index0, int index1) 1375: { 1376: return ((ListUI) ui).getCellBounds(this, index0, index1); 1377: } 1378: 1379: /** 1380: * Returns the next list element (beginning from <code>startIndex</code> 1381: * that starts with <code>prefix</code>. Searching is done in the direction 1382: * specified by <code>bias</code>. 1383: * 1384: * @param prefix the prefix to search for in the cell values 1385: * @param startIndex the index where to start searching from 1386: * @param bias the search direction, either {@link Position.Bias.Forward} 1387: * or {@link Position.Bias.Backward} 1388: * 1389: * @return the index of the found element or -1 if no such element has 1390: * been found 1391: * 1392: * @throws IllegalArgumentException if prefix is <code>null</code> or 1393: * startIndex is not valid 1394: * 1395: * @since 1.4 1396: */ 1397: public int getNextMatch(String prefix, int startIndex, Position.Bias bias) 1398: { 1399: if (prefix == null) 1400: throw new IllegalArgumentException("The argument 'prefix' must not be" 1401: + " null."); 1402: if (startIndex < 0) 1403: throw new IllegalArgumentException("The argument 'startIndex' must not" 1404: + " be less than zero."); 1405: 1406: int size = model.getSize(); 1407: if (startIndex > model.getSize()) 1408: throw new IllegalArgumentException("The argument 'startIndex' must not" 1409: + " be greater than the number of" 1410: + " elements in the ListModel."); 1411: 1412: int index = -1; 1413: if (bias == Position.Bias.Forward) 1414: { 1415: for (int i = startIndex; i < size; i++) 1416: { 1417: String item = model.getElementAt(i).toString(); 1418: if (item.startsWith(prefix)) 1419: { 1420: index = i; 1421: break; 1422: } 1423: } 1424: } 1425: else 1426: { 1427: for (int i = startIndex; i >= 0; i--) 1428: { 1429: String item = model.getElementAt(i).toString(); 1430: if (item.startsWith(prefix)) 1431: { 1432: index = i; 1433: break; 1434: } 1435: } 1436: } 1437: return index; 1438: } 1439: }
GNU Classpath (0.18) |