GNU Classpath (0.17) | ||
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(), 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: //FIXME: Need to implement. 600: return null; 601: } 602: 603: /** 604: * Returns the list index of the lower right or lower left corner of the 605: * {@link #visibleRect} property, depending on the {@link 606: * #componentOrientation} property. 607: * 608: * @return The index of the first visible list cell, or <code>-1</code> 609: * if none is visible. 610: */ 611: public int getLastVisibleIndex() 612: { 613: ComponentOrientation or = getComponentOrientation(); 614: Rectangle r = getVisibleRect(); 615: r.translate(0, (int) r.getHeight()); 616: if (or == ComponentOrientation.LEFT_TO_RIGHT) 617: r.translate((int) r.getWidth(), 0); 618: return getUI().locationToIndex(this, r.getLocation()); 619: } 620: 621: /** 622: * Returns the indices of values in the {@link #model} property which are 623: * selected. 624: * 625: * @return An array of model indices, each of which is selected according 626: * to the {@link #selection} property 627: */ 628: public int[] getSelectedIndices() 629: { 630: int lo, hi, n, i, j; 631: if (selectionModel.isSelectionEmpty()) 632: return new int[0]; 633: lo = selectionModel.getMinSelectionIndex(); 634: hi = selectionModel.getMaxSelectionIndex(); 635: n = 0; 636: for (i = lo; i <= hi; ++i) 637: if (selectionModel.isSelectedIndex(i)) 638: n++; 639: int [] v = new int[n]; 640: j = 0; 641: for (i = lo; i < hi; ++i) 642: if (selectionModel.isSelectedIndex(i)) 643: v[j++] = i; 644: return v; 645: } 646: 647: /** 648: * Indicates whether the list element at a given index value is 649: * currently selected. 650: * 651: * @param a The index to check 652: * @return <code>true</code> if <code>a</code> is the index of a selected 653: * list element 654: */ 655: public boolean isSelectedIndex(int a) 656: { 657: return selectionModel.isSelectedIndex(a); 658: } 659: 660: /** 661: * Returns the first value in the list's {@link #model} property which is 662: * selected, according to the list's {@link #selectionModel} property. 663: * This is equivalent to calling 664: * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check 665: * for the special index value of <code>-1</code> which returns null 666: * <code>null</code>. 667: * 668: * @return The first selected element, or <code>null</code> if no element 669: * is selected. 670: * 671: * @see getSelectedValues 672: */ 673: public Object getSelectedValue() 674: { 675: int index = getSelectedIndex(); 676: if (index == -1) 677: return null; 678: return getModel().getElementAt(index); 679: } 680: 681: /** 682: * Returns all the values in the list's {@link #model} property which 683: * are selected, according to the list's {@link #selectionModel} property. 684: * 685: * @return An array containing all the selected values 686: * 687: * @see getSelectedValue 688: */ 689: public Object[] getSelectedValues() 690: { 691: int [] idx = getSelectedIndices(); 692: Object [] v = new Object[idx.length]; 693: for (int i = 0; i < idx.length; ++i) 694: v[i] = getModel().getElementAt(i); 695: return v; 696: } 697: 698: /** 699: * Gets the value of the {@link #selectionBackground} property. 700: * 701: * @return The current value of the property 702: */ 703: public Color getSelectionBackground() 704: { 705: return selectionBackground; 706: } 707: 708: /** 709: * Sets the value of the {@link #selectionBackground} property. 710: * 711: * @param c The new value of the property 712: */ 713: public void setSelectionBackground(Color c) 714: { 715: if (selectionBackground == c) 716: return; 717: 718: Color old = selectionBackground; 719: selectionBackground = c; 720: firePropertyChange("selectionBackground", old, c); 721: repaint(); 722: } 723: 724: /** 725: * Gets the value of the {@link #selectionForeground} property. 726: * 727: * @return The current value of the property 728: */ 729: public Color getSelectionForeground() 730: { 731: return selectionForeground; 732: } 733: 734: /** 735: * Sets the value of the {@link #selectionForeground} property. 736: * 737: * @param c The new value of the property 738: */ 739: public void setSelectionForeground(Color c) 740: { 741: if (selectionForeground == c) 742: return; 743: 744: Color old = selectionForeground; 745: selectionForeground = c; 746: firePropertyChange("selectionForeground", old, c); 747: } 748: 749: /** 750: * Sets the selection to cover only the specified value, if it 751: * exists in the model. 752: * 753: * @param obj The object to select 754: * @param scroll Whether to scroll the list to make the newly selected 755: * value visible 756: * 757: * @see #ensureIndexIsVisible 758: */ 759: 760: public void setSelectedValue(Object obj, boolean scroll) 761: { 762: for (int i = 0; i < model.getSize(); ++i) 763: { 764: if (model.getElementAt(i).equals(obj)) 765: { 766: setSelectedIndex(i); 767: if (scroll) 768: ensureIndexIsVisible(i); 769: break; 770: } 771: } 772: } 773: 774: /** 775: * Scrolls this list to make the specified cell visible. This 776: * only works if the list is contained within a viewport. 777: * 778: * @param i The list index to make visible 779: * 780: * @see JComponent#scrollRectToVisible 781: */ 782: public void ensureIndexIsVisible(int i) 783: { 784: scrollRectToVisible(getUI().getCellBounds(this, i, i)); 785: } 786: 787: /** 788: * Sets the {@link #model} property of the list to a new anonymous 789: * {@link AbstractListModel} subclass which accesses the provided Object 790: * array directly. 791: * 792: * @param listData The object array to build a new list model on 793: * @see #setModel 794: */ 795: public void setListData(final Object[] listData) 796: { 797: setModel(new AbstractListModel() 798: { 799: public int getSize() 800: { 801: return listData.length; 802: } 803: 804: public Object getElementAt(int i) 805: { 806: return listData[i]; 807: } 808: }); 809: } 810: 811: /** 812: * Sets the {@link #model} property of the list to a new anonymous {@link 813: * AbstractListModel} subclass which accesses the provided vector 814: * directly. 815: * 816: * @param listData The object array to build a new list model on 817: * @see #setModel 818: */ 819: public void setListData(final Vector listData) 820: { 821: setModel(new AbstractListModel() 822: { 823: public int getSize() 824: { 825: return listData.size(); 826: } 827: 828: public Object getElementAt(int i) 829: { 830: return listData.elementAt(i); 831: } 832: }); 833: } 834: 835: /** 836: * Gets the value of the {@link #cellRenderer} property. 837: * 838: * @return The current value of the property 839: */ 840: public ListCellRenderer getCellRenderer() 841: { 842: return cellRenderer; 843: } 844: 845: /** 846: * Sets the value of the {@link #celLRenderer} property. 847: * 848: * @param renderer The new property value 849: */ 850: public void setCellRenderer(ListCellRenderer renderer) 851: { 852: if (cellRenderer == renderer) 853: return; 854: 855: ListCellRenderer old = cellRenderer; 856: cellRenderer = renderer; 857: firePropertyChange("cellRenderer", old, renderer); 858: revalidate(); 859: repaint(); 860: } 861: 862: /** 863: * Gets the value of the {@link #model} property. 864: * 865: * @return The current value of the property 866: */ 867: public ListModel getModel() 868: { 869: return model; 870: } 871: 872: /** 873: * Sets the value of the {@link #model} property. The list's {@link 874: * #listListener} is unsubscribed from the existing model, if it exists, 875: * and re-subscribed to the new model. 876: * 877: * @param model The new property value 878: */ 879: public void setModel(ListModel model) 880: { 881: if (this.model == model) 882: return; 883: 884: if (this.model != null) 885: this.model.removeListDataListener(listListener); 886: 887: ListModel old = this.model; 888: this.model = model; 889: 890: if (this.model != null) 891: this.model.addListDataListener(listListener); 892: 893: firePropertyChange("model", old, model); 894: revalidate(); 895: repaint(); 896: } 897: 898: 899: public ListSelectionModel getSelectionModel() 900: { 901: return selectionModel; 902: } 903: 904: /** 905: * Sets the value of the {@link #selectionModel} property. The list's 906: * {@link #listListener} is unsubscribed from the existing selection 907: * model, if it exists, and re-subscribed to the new selection model. 908: * 909: * @param model The new property value 910: */ 911: public void setSelectionModel(ListSelectionModel model) 912: { 913: if (selectionModel == model) 914: return; 915: 916: if (selectionModel != null) 917: selectionModel.removeListSelectionListener(listListener); 918: 919: ListSelectionModel old = selectionModel; 920: selectionModel = model; 921: 922: if (selectionModel != null) 923: selectionModel.addListSelectionListener(listListener); 924: 925: firePropertyChange("selectionModel", old, model); 926: revalidate(); 927: repaint(); 928: } 929: 930: /** 931: * Gets the value of the UI property. 932: * 933: * @return The current property value 934: */ 935: public ListUI getUI() 936: { 937: return (ListUI) ui; 938: } 939: 940: /** 941: * Sets the value of the UI property. 942: * 943: * @param ui The new property value 944: */ 945: public void setUI(ListUI ui) 946: { 947: super.setUI(ui); 948: } 949: 950: /** 951: * Calls {@link #setUI} with the {@link ListUI} subclass 952: * returned from calling {@link UIManager#getUI}. 953: */ 954: public void updateUI() 955: { 956: setUI((ListUI) UIManager.getUI(this)); 957: } 958: 959: /** 960: * Return the class identifier for the list's UI property. This should 961: * be the constant string <code>"ListUI"</code>, and map to an 962: * appropriate UI class in the {@link UIManager}. 963: * 964: * @return The class identifier 965: */ 966: public String getUIClassID() 967: { 968: return "ListUI"; 969: } 970: 971: 972: /** 973: * Returns the current value of the {@link #prototypeCellValue} 974: * property. This property holds a reference to a "prototype" data value 975: * -- typically a String -- which is used to calculate the {@link 976: * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the 977: * {@link #cellRenderer} property to acquire a component to render the 978: * prototype. 979: * 980: * @return The current prototype cell value 981: * @see #setPrototypeCellValue 982: */ 983: public Object getPrototypeCellValue() 984: { 985: return prototypeCellValue; 986: } 987: 988: /** 989: * <p>Set the {@link #prototypeCellValue} property. This property holds a 990: * reference to a "prototype" data value -- typically a String -- which 991: * is used to calculate the {@link #fixedCellWidth} and {@link 992: * #fixedCellHeight} properties, using the {@link #cellRenderer} property 993: * to acquire a component to render the prototype.</p> 994: * 995: * <p>It is important that you <em>not</em> set this value to a 996: * component. It has to be a <em>data value</em> such as the objects you 997: * would find in the list's model. Setting it to a component will have 998: * undefined (and undesirable) affects. </p> 999: * 1000: * @param obj The new prototype cell value 1001: * @see #getPrototypeCellValue 1002: */ 1003: public void setPrototypeCellValue(Object obj) 1004: { 1005: if (prototypeCellValue == obj) 1006: return; 1007: 1008: Object old = prototypeCellValue; 1009: Component comp = getCellRenderer() 1010: .getListCellRendererComponent(this, obj, 0, false, false); 1011: Dimension d = comp.getPreferredSize(); 1012: fixedCellWidth = d.width; 1013: fixedCellHeight = d.height; 1014: prototypeCellValue = obj; 1015: firePropertyChange("prototypeCellValue", old, obj); 1016: } 1017: 1018: public AccessibleContext getAccessibleContext() 1019: { 1020: return null; 1021: } 1022: 1023: /** 1024: * Returns a size indicating how much space this list would like to 1025: * consume, when contained in a scrollable viewport. This is part of the 1026: * {@link Scrollable} interface, which interacts with {@link 1027: * ScrollPaneLayout} and {@link Viewport} to define scrollable objects. 1028: * 1029: * @return The preferred size 1030: */ 1031: public Dimension getPreferredScrollableViewportSize() 1032: { 1033: 1034: Dimension retVal = getPreferredSize(); 1035: if (getLayoutOrientation() == VERTICAL) 1036: { 1037: if (fixedCellHeight != -1) 1038: { 1039: if (fixedCellWidth != -1) 1040: { 1041: int size = getModel().getSize(); 1042: retVal = new Dimension(fixedCellWidth, size * fixedCellHeight); 1043: } // TODO: add else clause (preferredSize is ok for now) 1044: } // TODO: add else clause (preferredSize is ok for now) 1045: } 1046: return retVal; 1047: } 1048: 1049: /** 1050: * <p>Return the number of pixels the list must scroll in order to move a 1051: * "unit" of the list into the provided visible rectangle. When the 1052: * provided direction is positive, the call describes a "downwards" 1053: * scroll, which will be exposing a cell at a <em>greater</em> index in 1054: * the list than those elements currently showing. Then the provided 1055: * direction is negative, the call describes an "upwards" scroll, which 1056: * will be exposing a cell at a <em>lesser</em> index in the list than 1057: * those elements currently showing.</p> 1058: * 1059: * <p>If the provided orientation is <code>HORIZONTAL</code>, the above 1060: * comments refer to "rightwards" for positive direction, and "leftwards" 1061: * for negative.</p> 1062: * 1063: * 1064: * @param visibleRect The rectangle to scroll an element into 1065: * @param orientation One of the numeric consants <code>VERTICAL</code> 1066: * or <code>HORIZONTAL</code> 1067: * @param direction An integer indicating the scroll direction: positive means 1068: * forwards (down, right), negative means backwards (up, left) 1069: * 1070: * @return The scrollable unit increment, in pixels 1071: */ 1072: public int getScrollableUnitIncrement(Rectangle visibleRect, 1073: int orientation, int direction) 1074: { 1075: ListUI lui = this.getUI(); 1076: if (orientation == SwingConstants.VERTICAL) 1077: { 1078: if (direction > 0) 1079: { 1080: // Scrolling down 1081: Point bottomLeft = new Point(visibleRect.x, 1082: visibleRect.y + visibleRect.height); 1083: int curIdx = lui.locationToIndex(this, bottomLeft); 1084: Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); 1085: if (curBounds.y + curBounds.height == bottomLeft.y) 1086: { 1087: // we are at the exact bottom of the current cell, so we 1088: // are being asked to scroll to the end of the next one 1089: if (curIdx + 1 < model.getSize()) 1090: { 1091: // there *is* a next item in the list 1092: Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1); 1093: return nxtBounds.height; 1094: } 1095: else 1096: { 1097: // no next item, no advance possible 1098: return 0; 1099: } 1100: } 1101: else 1102: { 1103: // we are part way through an existing cell, so we are being 1104: // asked to scroll to the bottom of it 1105: return (curBounds.y + curBounds.height) - bottomLeft.y; 1106: } 1107: } 1108: else 1109: { 1110: // scrolling up 1111: Point topLeft = new Point(visibleRect.x, visibleRect.y); 1112: int curIdx = lui.locationToIndex(this, topLeft); 1113: Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); 1114: if (curBounds.y == topLeft.y) 1115: { 1116: // we are at the exact top of the current cell, so we 1117: // are being asked to scroll to the top of the previous one 1118: if (curIdx > 0) 1119: { 1120: // there *is* a previous item in the list 1121: Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1); 1122: return -nxtBounds.height; 1123: } 1124: else 1125: { 1126: // no previous item, no advance possible 1127: return 0; 1128: } 1129: } 1130: else 1131: { 1132: // we are part way through an existing cell, so we are being 1133: // asked to scroll to the top of it 1134: return curBounds.y - topLeft.y; 1135: } 1136: } 1137: } 1138: 1139: // FIXME: handle horizontal scrolling (also wrapping?) 1140: return 1; 1141: } 1142: 1143: /** 1144: * <p>Return the number of pixels the list must scroll in order to move a 1145: * "block" of the list into the provided visible rectangle. When the 1146: * provided direction is positive, the call describes a "downwards" 1147: * scroll, which will be exposing a cell at a <em>greater</em> index in 1148: * the list than those elements currently showing. Then the provided 1149: * direction is negative, the call describes an "upwards" scroll, which 1150: * will be exposing a cell at a <em>lesser</em> index in the list than 1151: * those elements currently showing.</p> 1152: * 1153: * <p>If the provided orientation is <code>HORIZONTAL</code>, the above 1154: * comments refer to "rightwards" for positive direction, and "leftwards" 1155: * for negative.</p> 1156: * 1157: * 1158: * @param visibleRect The rectangle to scroll an element into 1159: * @param orientation One of the numeric consants <code>VERTICAL</code> 1160: * or <code>HORIZONTAL</code> 1161: * @param direction An integer indicating the scroll direction: positive means 1162: * forwards (down, right), negative means backwards (up, left) 1163: * 1164: * @return The scrollable unit increment, in pixels 1165: */ 1166: public int getScrollableBlockIncrement(Rectangle visibleRect, 1167: int orientation, int direction) 1168: { 1169: if (orientation == VERTICAL) 1170: return visibleRect.height * direction; 1171: else 1172: return visibleRect.width * direction; 1173: } 1174: 1175: /** 1176: * Gets the value of the {@link #scrollableTracksViewportWidth} property. 1177: * 1178: * @return <code>true</code> if the viewport is larger (horizontally) 1179: * than the list and the list should be expanded to fit the viewport; 1180: * <code>false</code> if the viewport is smaller than the list and the 1181: * list should scroll (horizontally) within the viewport 1182: */ 1183: public boolean getScrollableTracksViewportWidth() 1184: { 1185: Component parent = getParent(); 1186: boolean retVal = false; 1187: if (parent instanceof JViewport) 1188: { 1189: JViewport viewport = (JViewport) parent; 1190: Dimension pref = getPreferredSize(); 1191: if (viewport.getSize().width > pref.width) 1192: retVal = true; 1193: if ((getLayoutOrientation() == HORIZONTAL_WRAP) 1194: && (getVisibleRowCount() <= 0)) 1195: retVal = true; 1196: } 1197: return retVal; 1198: } 1199: 1200: /** 1201: * Gets the value of the {@link #scrollableTracksViewportWidth} property. 1202: * 1203: * @return <code>true</code> if the viewport is larger (vertically) 1204: * than the list and the list should be expanded to fit the viewport; 1205: * <code>false</code> if the viewport is smaller than the list and the 1206: * list should scroll (vertically) within the viewport 1207: */ 1208: public boolean getScrollableTracksViewportHeight() 1209: { 1210: Component parent = getParent(); 1211: boolean retVal = false; 1212: if (parent instanceof JViewport) 1213: { 1214: JViewport viewport = (JViewport) parent; 1215: Dimension pref = getPreferredSize(); 1216: if (viewport.getSize().height > pref.height) 1217: retVal = true; 1218: if ((getLayoutOrientation() == VERTICAL_WRAP) 1219: && (getVisibleRowCount() <= 0)) 1220: retVal = true; 1221: } 1222: return retVal; 1223: } 1224: 1225: public int getAnchorSelectionIndex() 1226: { 1227: return selectionModel.getAnchorSelectionIndex(); 1228: } 1229: 1230: public int getLeadSelectionIndex() 1231: { 1232: return selectionModel.getLeadSelectionIndex(); 1233: } 1234: 1235: public int getMinSelectionIndex() 1236: { 1237: return selectionModel.getMaxSelectionIndex(); 1238: } 1239: 1240: public int getMaxSelectionIndex() 1241: { 1242: return selectionModel.getMaxSelectionIndex(); 1243: } 1244: 1245: public void clearSelection() 1246: { 1247: selectionModel.clearSelection(); 1248: } 1249: 1250: public void setSelectionInterval(int anchor, int lead) 1251: { 1252: selectionModel.setSelectionInterval(anchor, lead); 1253: } 1254: 1255: public void addSelectionInterval(int anchor, int lead) 1256: { 1257: selectionModel.addSelectionInterval(anchor, lead); 1258: } 1259: 1260: public void removeSelectionInterval(int index0, int index1) 1261: { 1262: selectionModel.removeSelectionInterval(index0, index1); 1263: } 1264: 1265: /** 1266: * Returns the value of the <code>valueIsAdjusting</code> property. 1267: * 1268: * @return the value 1269: */ 1270: public boolean getValueIsAdjusting() 1271: { 1272: return valueIsAdjusting; 1273: } 1274: 1275: /** 1276: * Sets the <code>valueIsAdjusting</code> property. 1277: * 1278: * @param isAdjusting the new value 1279: */ 1280: public void setValueIsAdjusting(boolean isAdjusting) 1281: { 1282: valueIsAdjusting = isAdjusting; 1283: } 1284: 1285: /** 1286: * Return the value of the <code>dragEnabled</code> property. 1287: * 1288: * @return the value 1289: * 1290: * @since 1.4 1291: */ 1292: public boolean getDragEnabled() 1293: { 1294: return dragEnabled; 1295: } 1296: 1297: /** 1298: * Set the <code>dragEnabled</code> property. 1299: * 1300: * @param enabled new value 1301: * 1302: * @since 1.4 1303: */ 1304: public void setDragEnabled(boolean enabled) 1305: { 1306: dragEnabled = enabled; 1307: } 1308: 1309: /** 1310: * Returns the layout orientation. 1311: * 1312: * @return the orientation, one of <code>JList.VERTICAL</code>, 1313: * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> 1314: * 1315: * @since 1.4 1316: */ 1317: public int getLayoutOrientation() 1318: { 1319: return layoutOrientation; 1320: } 1321: 1322: /** 1323: * Sets the layout orientation. 1324: * 1325: * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>, 1326: * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> 1327: * 1328: * @since 1.4 1329: */ 1330: public void setLayoutOrientation(int orientation) 1331: { 1332: if (layoutOrientation == orientation) 1333: return; 1334: 1335: int old = layoutOrientation; 1336: layoutOrientation = orientation; 1337: firePropertyChange("layoutOrientation", old, orientation); 1338: } 1339: 1340: /** 1341: * Returns the bounds of the rectangle that encloses both list cells 1342: * with index0 and index1. 1343: * 1344: * @param index0 the index of the first cell 1345: * @param index1 the index of the second cell 1346: * 1347: * @return the bounds of the rectangle that encloses both list cells 1348: * with index0 and index1, <code>null</code> if one of the indices is 1349: * not valid 1350: */ 1351: public Rectangle getCellBounds(int index0, int index1) 1352: { 1353: return ((ListUI) ui).getCellBounds(this, index0, index1); 1354: } 1355: 1356: /** 1357: * Returns the next list element (beginning from <code>startIndex</code> 1358: * that starts with <code>prefix</code>. Searching is done in the direction 1359: * specified by <code>bias</code>. 1360: * 1361: * @param prefix the prefix to search for in the cell values 1362: * @param startIndex the index where to start searching from 1363: * @param bias the search direction, either {@link Position.Bias.Forward} 1364: * or {@link Position.Bias.Backward} 1365: * 1366: * @return the index of the found element or -1 if no such element has 1367: * been found 1368: * 1369: * @throws IllegalArgumentException if prefix is <code>null</code> or 1370: * startIndex is not valid 1371: * 1372: * @since 1.4 1373: */ 1374: public int getNextMatch(String prefix, int startIndex, Position.Bias bias) 1375: { 1376: if (prefix == null) 1377: throw new IllegalArgumentException("The argument 'prefix' must not be" 1378: + " null."); 1379: if (startIndex < 0) 1380: throw new IllegalArgumentException("The argument 'startIndex' must not" 1381: + " be less than zero."); 1382: 1383: int size = model.getSize(); 1384: if (startIndex > model.getSize()) 1385: throw new IllegalArgumentException("The argument 'startIndex' must not" 1386: + " be greater than the number of" 1387: + " elements in the ListModel."); 1388: 1389: int index = -1; 1390: if (bias == Position.Bias.Forward) 1391: { 1392: for (int i = startIndex; i < size; i++) 1393: { 1394: String item = model.getElementAt(i).toString(); 1395: if (item.startsWith(prefix)) 1396: { 1397: index = i; 1398: break; 1399: } 1400: } 1401: } 1402: else 1403: { 1404: for (int i = startIndex; i >= 0; i--) 1405: { 1406: String item = model.getElementAt(i).toString(); 1407: if (item.startsWith(prefix)) 1408: { 1409: index = i; 1410: break; 1411: } 1412: } 1413: } 1414: return index; 1415: } 1416: }
GNU Classpath (0.17) |