Frames | No Frames |
1: /* JList.java -- 2: Copyright (C) 2002, 2003, 2004, 2005, 2006, 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 gnu.java.lang.CPStringBuilder; 42: 43: import java.awt.Color; 44: import java.awt.Component; 45: import java.awt.ComponentOrientation; 46: import java.awt.Cursor; 47: import java.awt.Dimension; 48: import java.awt.Font; 49: import java.awt.FontMetrics; 50: import java.awt.Point; 51: import java.awt.Rectangle; 52: import java.awt.event.FocusListener; 53: import java.beans.PropertyChangeEvent; 54: import java.beans.PropertyChangeListener; 55: import java.util.Locale; 56: import java.util.Vector; 57: 58: import javax.accessibility.Accessible; 59: import javax.accessibility.AccessibleComponent; 60: import javax.accessibility.AccessibleContext; 61: import javax.accessibility.AccessibleRole; 62: import javax.accessibility.AccessibleSelection; 63: import javax.accessibility.AccessibleState; 64: import javax.accessibility.AccessibleStateSet; 65: import javax.swing.event.ListDataEvent; 66: import javax.swing.event.ListDataListener; 67: import javax.swing.event.ListSelectionEvent; 68: import javax.swing.event.ListSelectionListener; 69: import javax.swing.plaf.ListUI; 70: import javax.swing.text.Position; 71: 72: /** 73: * <p>This class is a facade over three separate objects: {@link 74: * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and 75: * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list" 76: * concept, with independently replacable (possibly client-provided) models 77: * for its contents and its current selection. In addition, each element in 78: * the list is rendered via a strategy class {@link 79: * javax.swing.ListCellRenderer}.</p> 80: * 81: * <p>Lists have many properties, some of which are stored in this class 82: * while others are delegated to the list's model or selection. The 83: * following properties are available:</p> 84: * 85: * <table> 86: * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> 87: * <tr><td>accessibleContext </td><td>list </td><td>no </td></tr> 88: * <tr><td>anchorSelectionIndex </td><td>selection</td><td>no </td></tr> 89: * <tr><td>cellRenderer </td><td>list </td><td>yes </td></tr> 90: * <tr><td>dragEnabled </td><td>list </td><td>no </td></tr> 91: * <tr><td>firstVisibleIndex </td><td>list </td><td>no </td></tr> 92: * <tr><td>fixedCellHeight </td><td>list </td><td>yes </td></tr> 93: * <tr><td>fixedCellWidth </td><td>list </td><td>yes </td></tr> 94: * <tr><td>lastVisibleIndex </td><td>list </td><td>no </td></tr> 95: * <tr><td>layoutOrientation </td><td>list </td><td>yes </td></tr> 96: * <tr><td>leadSelectionIndex </td><td>selection</td><td>no </td></tr> 97: * <tr><td>maxSelectionIndex </td><td>selection</td><td>no </td></tr> 98: * <tr><td>minSelectionIndex </td><td>selection</td><td>no </td></tr> 99: * <tr><td>model </td><td>list </td><td>yes </td></tr> 100: * <tr><td>opaque </td><td>list </td><td>no </td></tr> 101: * <tr><td>preferredScrollableViewportSize</td><td>list </td><td>no </td></tr> 102: * <tr><td>prototypeCellValue </td><td>list </td><td>yes </td></tr> 103: * <tr><td>scrollableTracksViewportHeight </td><td>list </td><td>no </td></tr> 104: * <tr><td>scrollableTracksViewportWidth </td><td>list </td><td>no </td></tr> 105: * <tr><td>selectedIndex </td><td>selection</td><td>no </td></tr> 106: * <tr><td>selectedIndices </td><td>selection</td><td>no </td></tr> 107: * <tr><td>selectedValue </td><td>model </td><td>no </td></tr> 108: * <tr><td>selectedValues </td><td>model </td><td>no </td></tr> 109: * <tr><td>selectionBackground </td><td>list </td><td>yes </td></tr> 110: * <tr><td>selectionEmpty </td><td>selection</td><td>no </td></tr> 111: * <tr><td>selectionForeground </td><td>list </td><td>yes </td></tr> 112: * <tr><td>selectionMode </td><td>selection</td><td>no </td></tr> 113: * <tr><td>selectionModel </td><td>list </td><td>yes </td></tr> 114: * <tr><td>UI </td><td>list </td><td>yes </td></tr> 115: * <tr><td>UIClassID </td><td>list </td><td>no </td></tr> 116: * <tr><td>valueIsAdjusting </td><td>list </td><td>no </td></tr> 117: * <tr><td>visibleRowCount </td><td>list </td><td>no </td></tr> 118: * </table> 119: * 120: * @author Graydon Hoare (graydon@redhat.com) 121: */ 122: 123: public class JList extends JComponent implements Accessible, Scrollable 124: { 125: 126: /** 127: * Provides accessibility support for <code>JList</code>. 128: */ 129: protected class AccessibleJList extends AccessibleJComponent 130: implements AccessibleSelection, PropertyChangeListener, 131: ListSelectionListener, ListDataListener 132: { 133: 134: /** 135: * Provides accessibility support for list elements in <code>JList</code>s. 136: */ 137: protected class AccessibleJListChild extends AccessibleContext 138: implements Accessible, AccessibleComponent 139: { 140: 141: /** 142: * The parent list. 143: */ 144: JList parent; 145: 146: /** 147: * The index in the list for that child. 148: */ 149: int listIndex; 150: 151: /** 152: * The cursor for this list child. 153: */ 154: // TODO: Testcases show that this class somehow stores state about the 155: // cursor. I cannot make up though how that could affect 156: // the actual list. 157: Cursor cursor = Cursor.getDefaultCursor(); 158: 159: /** 160: * Creates a new instance of <code>AccessibleJListChild</code>. 161: * 162: * @param list the list of which this is an accessible child 163: * @param index the list index for this child 164: */ 165: public AccessibleJListChild(JList list, int index) 166: { 167: parent = list; 168: listIndex = index; 169: } 170: 171: /** 172: * Returns the accessible context of this object. Returns 173: * <code>this</code> since <code>AccessibleJListChild</code>s are their 174: * own accessible contexts. 175: * 176: * @return the accessible context of this object, <code>this</code> 177: */ 178: public AccessibleContext getAccessibleContext() 179: { 180: return this; 181: } 182: 183: /** 184: * Returns the background color for this list child. This returns the 185: * background of the <code>JList</code> itself since the background 186: * cannot be set on list children individually 187: * 188: * @return the background color for this list child 189: */ 190: public Color getBackground() 191: { 192: return parent.getBackground(); 193: } 194: 195: /** 196: * Calling this method has no effect, since the background color cannot be 197: * set on list children individually. 198: * 199: * @param color not used here. 200: */ 201: public void setBackground(Color color) 202: { 203: // Calling this method has no effect, since the background color cannot 204: // be set on list children individually. 205: } 206: 207: /** 208: * Returns the foreground color for this list child. This returns the 209: * background of the <code>JList</code> itself since the foreground 210: * cannot be set on list children individually. 211: * 212: * @return the background color for this list child 213: */ 214: public Color getForeground() 215: { 216: return parent.getForeground(); 217: } 218: 219: /** 220: * Calling this method has no effect, since the foreground color cannot be 221: * set on list children individually. 222: * 223: * @param color not used here. 224: */ 225: public void setForeground(Color color) 226: { 227: // Calling this method has no effect, since the foreground color cannot 228: // be set on list children individually. 229: } 230: 231: /** 232: * Returns the cursor for this list child. 233: * 234: * @return the cursor for this list child 235: */ 236: public Cursor getCursor() 237: { 238: // TODO: Testcases show that this method returns the cursor that has 239: // been set by setCursor. I cannot make up though how that could affect 240: // the actual list. 241: return cursor; 242: } 243: 244: /** 245: * Sets the cursor for this list child. 246: */ 247: public void setCursor(Cursor cursor) 248: { 249: this.cursor = cursor; 250: // TODO: Testcases show that this method returns the cursor that has 251: // been set by setCursor. I cannot make up though how that could affect 252: // the actual list. 253: } 254: 255: /** 256: * Returns the font of the <code>JList</code> since it is not possible to 257: * set fonts for list children individually. 258: * 259: * @return the font of the <code>JList</code> 260: */ 261: public Font getFont() 262: { 263: return parent.getFont(); 264: } 265: 266: /** 267: * Does nothing since it is not possible to set the font on list children 268: * individually. 269: * 270: * @param font not used here 271: */ 272: public void setFont(Font font) 273: { 274: // Does nothing since it is not possible to set the font on list 275: // children individually. 276: } 277: 278: /** 279: * Returns the font metrics for the specified font. This method forwards 280: * to the parent <code>JList</code>. 281: * 282: * @param font the font for which the font metrics is queried 283: * 284: * @return the font metrics for the specified font 285: */ 286: public FontMetrics getFontMetrics(Font font) 287: { 288: return parent.getFontMetrics(font); 289: } 290: 291: /** 292: * Returns <code>true</code> if the parent <code>JList</code> is enabled, 293: * <code>false</code> otherwise. The list children cannot have an enabled 294: * flag set individually. 295: * 296: * @return <code>true</code> if the parent <code>JList</code> is enabled, 297: * <code>false</code> otherwise 298: */ 299: public boolean isEnabled() 300: { 301: return parent.isEnabled(); 302: } 303: 304: /** 305: * Does nothing since the enabled flag cannot be set for list children 306: * individually. 307: * 308: * @param b not used here 309: */ 310: public void setEnabled(boolean b) 311: { 312: // Does nothing since the enabled flag cannot be set for list children 313: // individually. 314: } 315: 316: /** 317: * Returns <code>true</code> if this list child is visible, 318: * <code>false</code> otherwise. The value of this property depends 319: * on {@link JList#getFirstVisibleIndex()} and 320: * {@link JList#getLastVisibleIndex()}. 321: * 322: * @return <code>true</code> if this list child is visible, 323: * <code>false</code> otherwise 324: */ 325: public boolean isVisible() 326: { 327: return listIndex >= parent.getFirstVisibleIndex() 328: && listIndex <= parent.getLastVisibleIndex(); 329: } 330: 331: /** 332: * The value of the visible property cannot be modified, so this method 333: * does nothing. 334: * 335: * @param b not used here 336: */ 337: public void setVisible(boolean b) 338: { 339: // The value of the visible property cannot be modified, so this method 340: // does nothing. 341: } 342: 343: /** 344: * Returns <code>true</code> if this list child is currently showing on 345: * screen and <code>false</code> otherwise. The list child is showing if 346: * it is visible and if it's parent JList is currently showing. 347: * 348: * @return <code>true</code> if this list child is currently showing on 349: * screen and <code>false</code> otherwise 350: */ 351: public boolean isShowing() 352: { 353: return isVisible() && parent.isShowing(); 354: } 355: 356: /** 357: * Returns <code>true</code> if this list child covers the screen location 358: * <code>point</code> (relative to the <code>JList</code> coordinate 359: * system, <code>false</code> otherwise. 360: * 361: * @return <code>true</code> if this list child covers the screen location 362: * <code>point</code> , <code>false</code> otherwise 363: */ 364: public boolean contains(Point point) 365: { 366: return getBounds().contains(point); 367: } 368: 369: /** 370: * Returns the absolute screen location of this list child. 371: * 372: * @return the absolute screen location of this list child 373: */ 374: public Point getLocationOnScreen() 375: { 376: Point loc = getLocation(); 377: SwingUtilities.convertPointToScreen(loc, parent); 378: return loc; 379: } 380: 381: /** 382: * Returns the screen location of this list child relative to it's parent. 383: * 384: * @return the location of this list child relative to it's parent 385: * 386: * @see JList#indexToLocation(int) 387: */ 388: public Point getLocation() 389: { 390: return parent.indexToLocation(listIndex); 391: } 392: 393: /** 394: * Does nothing since the screen location cannot be set on list children 395: * explictitly. 396: * 397: * @param point not used here 398: */ 399: public void setLocation(Point point) 400: { 401: // Does nothing since the screen location cannot be set on list children 402: // explictitly. 403: } 404: 405: /** 406: * Returns the bounds of this list child. 407: * 408: * @return the bounds of this list child 409: * 410: * @see JList#getCellBounds(int, int) 411: */ 412: public Rectangle getBounds() 413: { 414: return parent.getCellBounds(listIndex, listIndex); 415: } 416: 417: /** 418: * Does nothing since the bounds cannot be set on list children 419: * individually. 420: * 421: * @param rectangle not used here 422: */ 423: public void setBounds(Rectangle rectangle) 424: { 425: // Does nothing since the bounds cannot be set on list children 426: // individually. 427: } 428: 429: /** 430: * Returns the size of this list child. 431: * 432: * @return the size of this list child 433: */ 434: public Dimension getSize() 435: { 436: Rectangle b = getBounds(); 437: return b.getSize(); 438: } 439: 440: /** 441: * Does nothing since the size cannot be set on list children 442: * individually. 443: * 444: * @param dimension not used here 445: */ 446: public void setSize(Dimension dimension) 447: { 448: // Does nothing since the size cannot be set on list children 449: // individually. 450: } 451: 452: /** 453: * Returns <code>null</code> because list children do not have children 454: * themselves 455: * 456: * @return <code>null</code> 457: */ 458: public Accessible getAccessibleAt(Point point) 459: { 460: return null; 461: } 462: 463: /** 464: * Returns <code>true</code> since list children are focus traversable. 465: * 466: * @return true 467: */ 468: public boolean isFocusTraversable() 469: { 470: // TODO: Is this 100% ok? 471: return true; 472: } 473: 474: /** 475: * Requests focus on the parent list. List children cannot request focus 476: * individually. 477: */ 478: public void requestFocus() 479: { 480: // TODO: Is this 100% ok? 481: parent.requestFocus(); 482: } 483: 484: /** 485: * Adds a focus listener to the parent list. List children do not have 486: * their own focus management. 487: * 488: * @param listener the focus listener to add 489: */ 490: public void addFocusListener(FocusListener listener) 491: { 492: // TODO: Is this 100% ok? 493: parent.addFocusListener(listener); 494: } 495: 496: /** 497: * Removes a focus listener from the parent list. List children do not 498: * have their own focus management. 499: * 500: * @param listener the focus listener to remove 501: */ 502: public void removeFocusListener(FocusListener listener) 503: { 504: // TODO: Is this 100% 505: parent.removeFocusListener(listener); 506: } 507: 508: /** 509: * Returns the accessible role of this list item, which is 510: * {@link AccessibleRole#LABEL}. 511: * 512: * @return {@link AccessibleRole#LABEL} 513: */ 514: public AccessibleRole getAccessibleRole() 515: { 516: return AccessibleRole.LABEL; 517: } 518: 519: /** 520: * Returns the accessible state set of this list item. 521: * 522: * @return the accessible state set of this list item 523: */ 524: public AccessibleStateSet getAccessibleStateSet() 525: { 526: AccessibleStateSet states = new AccessibleStateSet(); 527: if (isVisible()) 528: states.add(AccessibleState.VISIBLE); 529: if (isShowing()) 530: states.add(AccessibleState.SHOWING); 531: if (isFocusTraversable()) 532: states.add(AccessibleState.FOCUSABLE); 533: // TODO: How should the active state be handled? The API docs 534: // suggest that this state is set on the activated list child, 535: // that is the one that is drawn with a box. However, I don't know how 536: // to implement this. 537: 538: // TODO: We set the selectable state here because list children are 539: // selectable. Is there a way to disable single children? 540: if (parent.isEnabled()) 541: states.add(AccessibleState.SELECTABLE); 542: 543: if (parent.isSelectedIndex(listIndex)) 544: states.add(AccessibleState.SELECTED); 545: 546: // TODO: Handle more states here? 547: return states; 548: } 549: 550: /** 551: * Returns the index of this list child within it's parent list. 552: * 553: * @return the index of this list child within it's parent list 554: */ 555: public int getAccessibleIndexInParent() 556: { 557: return listIndex; 558: } 559: 560: /** 561: * Returns <code>0</code> since list children don't have children 562: * themselves. 563: * 564: * @return <code>0</code> 565: */ 566: public int getAccessibleChildrenCount() 567: { 568: return 0; 569: } 570: 571: /** 572: * Returns <code>null</code> since list children don't have children 573: * themselves. 574: * 575: * @return <code>null</code> 576: */ 577: public Accessible getAccessibleChild(int i) 578: { 579: return null; 580: } 581: 582: /** 583: * Returns the locale of this component. This call is forwarded to the 584: * parent list since list children don't have a separate locale setting. 585: * 586: * @return the locale of this component 587: */ 588: public Locale getLocale() 589: { 590: return parent.getLocale(); 591: } 592: 593: /** 594: * This method does 595: * nothing, list children are transient accessible objects which means 596: * that they don't fire property change events. 597: * 598: * @param l not used here 599: */ 600: public void addPropertyChangeListener(PropertyChangeListener l) 601: { 602: // Do nothing here. 603: } 604: 605: /** 606: * This method does 607: * nothing, list children are transient accessible objects which means 608: * that they don't fire property change events. 609: * 610: * @param l not used here 611: */ 612: public void removePropertyChangeListener(PropertyChangeListener l) 613: { 614: // Do nothing here. 615: } 616: 617: // TODO: Implement the remaining methods of this class. 618: } 619: 620: /** 621: * Create a new AccessibleJList. 622: */ 623: public AccessibleJList() 624: { 625: // Nothing to do here. 626: } 627: 628: /** 629: * Returns the number of selected accessible children. 630: * 631: * @return the number of selected accessible children 632: */ 633: public int getAccessibleSelectionCount() 634: { 635: return getSelectedIndices().length; 636: } 637: 638: /** 639: * Returns the n-th selected accessible child. 640: * 641: * @param n the index of the selected child to return 642: * 643: * @return the n-th selected accessible child 644: */ 645: public Accessible getAccessibleSelection(int n) 646: { 647: return new AccessibleJListChild(JList.this, getSelectedIndices()[n]); 648: } 649: 650: /** 651: * Returns <code>true</code> if the n-th child is selected, 652: * <code>false</code> otherwise. 653: * 654: * @param n the index of the child of which the selected state is queried 655: * 656: * @return <code>true</code> if the n-th child is selected, 657: * <code>false</code> otherwise 658: */ 659: public boolean isAccessibleChildSelected(int n) 660: { 661: return isSelectedIndex(n); 662: } 663: 664: /** 665: * Adds the accessible item with the specified index to the selected items. 666: * If multiple selections are supported, the item is added to the selection, 667: * otherwise the item replaces the current selection. 668: * 669: * @param i the index of the item to add to the selection 670: */ 671: public void addAccessibleSelection(int i) 672: { 673: addSelectionInterval(i, i); 674: } 675: 676: /** 677: * Removes the accessible item with the specified index to the selection. 678: * 679: * @param i the index of the item to be removed from the selection 680: */ 681: public void removeAccessibleSelection(int i) 682: { 683: removeSelectionInterval(i, i); 684: } 685: 686: /** 687: * Remove all selection items from the selection. 688: */ 689: public void clearAccessibleSelection() 690: { 691: clearSelection(); 692: } 693: 694: /** 695: * Selects all items if multiple selections are supported. 696: * Otherwise do nothing. 697: */ 698: public void selectAllAccessibleSelection() 699: { 700: addSelectionInterval(0, getModel().getSize()); 701: } 702: 703: /** 704: * Receices notification when the list selection is changed. This method 705: * fires two property change events, the first with 706: * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second 707: * with {@link AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}. 708: * 709: * @param event the list selection event 710: */ 711: public void valueChanged(ListSelectionEvent event) 712: { 713: firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, 714: Boolean.TRUE); 715: firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE, 716: Boolean.TRUE); 717: } 718: 719: /** 720: * Receives notification when items have changed in the 721: * <code>JList</code>. This method fires a property change event with 722: * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. 723: * 724: * @param event the list data event 725: */ 726: public void contentsChanged(ListDataEvent event) 727: { 728: firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, 729: Boolean.TRUE); 730: } 731: 732: /** 733: * Receives notification when items are inserted into the 734: * <code>JList</code>. This method fires a property change event with 735: * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. 736: * 737: * @param event the list data event 738: */ 739: public void intervalAdded(ListDataEvent event) 740: { 741: firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, 742: Boolean.TRUE); 743: } 744: 745: /** 746: * Receives notification when items are removed from the 747: * <code>JList</code>. This method fires a property change event with 748: * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. 749: * 750: * @param event the list data event 751: */ 752: public void intervalRemoved(ListDataEvent event) 753: { 754: firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, 755: Boolean.TRUE); 756: } 757: 758: 759: /** 760: * Receives notification about changes of the <code>JList</code>'s 761: * properties. This is used to re-register this object as listener to 762: * the data model and selection model when the data model or selection model 763: * changes. 764: * 765: * @param e the property change event 766: */ 767: public void propertyChange(PropertyChangeEvent e) 768: { 769: String propertyName = e.getPropertyName(); 770: if (propertyName.equals("model")) 771: { 772: ListModel oldModel = (ListModel) e.getOldValue(); 773: oldModel.removeListDataListener(this); 774: ListModel newModel = (ListModel) e.getNewValue(); 775: newModel.addListDataListener(this); 776: } 777: else if (propertyName.equals("selectionModel")) 778: { 779: ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue(); 780: oldModel.removeListSelectionListener(this); 781: ListSelectionModel newModel = (ListSelectionModel) e.getNewValue(); 782: oldModel.addListSelectionListener(this); 783: } 784: } 785: 786: /** 787: * Return the state set of the <code>JList</code>. 788: * 789: * @return the state set of the <code>JList</code> 790: */ 791: public AccessibleStateSet getAccessibleStateSet() 792: { 793: // TODO: Figure out if there is possibly more state that must be 794: // handled here. 795: AccessibleStateSet s = super.getAccessibleStateSet(); 796: if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) 797: s.add(AccessibleState.MULTISELECTABLE); 798: return s; 799: } 800: 801: /** 802: * Returns the accessible role for <code>JList</code>, 803: * {@link AccessibleRole#LIST}. 804: * 805: * @return the accessible role for <code>JList</code> 806: */ 807: public AccessibleRole getAccessibleRole() 808: { 809: return AccessibleRole.LIST; 810: } 811: 812: /** 813: * Returns the accessible child at the visual location <code>p</code> 814: * (relative to the upper left corner of the <code>JList</code>). If there 815: * is no child at that location, this returns <code>null</code>. 816: * 817: * @param p the screen location for which to return the accessible child 818: * 819: * @return the accessible child at the specified location, or 820: * <code>null</code> if there is no child at that location 821: */ 822: public Accessible getAccessibleAt(Point p) 823: { 824: int childIndex = locationToIndex(p); 825: return getAccessibleChild(childIndex); 826: } 827: 828: /** 829: * Returns the number of accessible children in the <code>JList</code>. 830: * 831: * @return the number of accessible children in the <code>JList</code> 832: */ 833: public int getAccessibleChildrenCount() 834: { 835: return getModel().getSize(); 836: } 837: 838: /** 839: * Returns the n-th accessible child of this <code>JList</code>. This will 840: * be an instance of {@link AccessibleJListChild}. If there is no child 841: * at that index, <code>null</code> is returned. 842: * 843: * @param n the index of the child to return 844: * 845: * @return the n-th accessible child of this <code>JList</code> 846: */ 847: public Accessible getAccessibleChild(int n) 848: { 849: if (getModel().getSize() <= n) 850: return null; 851: return new AccessibleJListChild(JList.this, n); 852: } 853: } 854: 855: private static final long serialVersionUID = 4406629526391098046L; 856: 857: /** 858: * Constant value used in "layoutOrientation" property. This value means 859: * that cells are laid out in a single vertical column. This is the default. 860: */ 861: public static final int VERTICAL = 0; 862: 863: /** 864: * Constant value used in "layoutOrientation" property. This value means 865: * that cells are laid out in multiple columns "newspaper style", filling 866: * vertically first, then horizontally. 867: */ 868: public static final int VERTICAL_WRAP = 1; 869: 870: /** 871: * Constant value used in "layoutOrientation" property. This value means 872: * that cells are laid out in multiple columns "newspaper style", 873: * filling horizontally first, then vertically. 874: */ 875: public static final int HORIZONTAL_WRAP = 2; 876: 877: /** 878: * This property indicates whether "drag and drop" functions are enabled 879: * on the list. 880: */ 881: boolean dragEnabled; 882: 883: /** This property provides a strategy for rendering cells in the list. */ 884: ListCellRenderer cellRenderer; 885: 886: /** 887: * This property indicates an fixed width to assign to all cells in the 888: * list. If its value is <code>-1</code>, no width has been 889: * assigned. This value can be set explicitly, or implicitly by setting 890: * the {@link #prototypeCellValue} property. 891: */ 892: int fixedCellWidth; 893: 894: /** 895: * This property indicates an fixed height to assign to all cells in the 896: * list. If its value is <code>-1</code>, no height has been 897: * assigned. This value can be set explicitly, or implicitly by setting 898: * the {@link #prototypeCellValue} property. 899: */ 900: int fixedCellHeight; 901: 902: /** 903: * This property holds the current layout orientation of the list, which 904: * is one of the integer constants {@link #VERTICAL}, {@link 905: * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}. 906: */ 907: int layoutOrientation; 908: 909: /** This property holds the data elements displayed by the list. */ 910: ListModel model; 911: 912: /** 913: * <p>This property holds a reference to a "prototype" data value -- 914: * typically a String -- which is used to calculate the {@link 915: * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the 916: * {@link #cellRenderer} property to acquire a component to render the 917: * prototype.</p> 918: * 919: * <p>It is important that you <em>not</em> set this value to a 920: * component. It has to be a <em>data value</em> such as the objects you 921: * would find in the list's model. Setting it to a component will have 922: * undefined (and undesirable) affects. </p> 923: */ 924: Object prototypeCellValue; 925: 926: /** 927: * This property specifies a foreground color for the selected cells in 928: * the list. When {@link ListCellRenderer#getListCellRendererComponent} 929: * is called with a selected cell object, the component returned will 930: * have its "foreground" set to this color. 931: */ 932: Color selectionBackground; 933: 934: /** 935: * This property specifies a background color for the selected cells in 936: * the list. When {@link ListCellRenderer#getListCellRendererComponent} 937: * is called with a selected cell object, the component returned will 938: * have its "background" property set to this color. 939: */ 940: Color selectionForeground; 941: 942: /** 943: * This property holds a description of which data elements in the {@link 944: * #model} property should be considered "selected", when displaying and 945: * interacting with the list. 946: */ 947: ListSelectionModel selectionModel; 948: 949: /** 950: * This property indicates a <em>preference</em> for the number of rows 951: * displayed in the list, and will scale the 952: * {@link #getPreferredScrollableViewportSize} property accordingly. The actual 953: * number of displayed rows, when the list is placed in a real {@link 954: * JViewport} or other component, may be greater or less than this number. 955: */ 956: int visibleRowCount; 957: 958: /** 959: * Fire a {@link ListSelectionEvent} to all the registered 960: * ListSelectionListeners. 961: * 962: * @param firstIndex the lowest index covering the selection change. 963: * @param lastIndex the highest index covering the selection change. 964: * @param isAdjusting a flag indicating if this event is one in a series 965: * of events updating the selection. 966: */ 967: protected void fireSelectionValueChanged(int firstIndex, int lastIndex, 968: boolean isAdjusting) 969: { 970: ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex, 971: lastIndex, isAdjusting); 972: ListSelectionListener listeners[] = getListSelectionListeners(); 973: for (int i = 0; i < listeners.length; ++i) 974: { 975: listeners[i].valueChanged(evt); 976: } 977: } 978: 979: /** 980: * This private listener propagates {@link ListSelectionEvent} events 981: * from the list's "selectionModel" property to the list's {@link 982: * ListSelectionListener} listeners. It also listens to {@link 983: * ListDataEvent} events from the list's {@link #model} property. If this 984: * class receives either type of event, it triggers repainting of the 985: * list. 986: */ 987: private class ListListener 988: implements ListSelectionListener, ListDataListener 989: { 990: // ListDataListener events 991: public void contentsChanged(ListDataEvent event) 992: { 993: JList.this.revalidate(); 994: JList.this.repaint(); 995: } 996: public void intervalAdded(ListDataEvent event) 997: { 998: JList.this.revalidate(); 999: JList.this.repaint(); 1000: } 1001: public void intervalRemoved(ListDataEvent event) 1002: { 1003: JList.this.revalidate(); 1004: JList.this.repaint(); 1005: } 1006: // ListSelectionListener events 1007: public void valueChanged(ListSelectionEvent event) 1008: { 1009: JList.this.fireSelectionValueChanged(event.getFirstIndex(), 1010: event.getLastIndex(), 1011: event.getValueIsAdjusting()); 1012: JList.this.repaint(); 1013: } 1014: } 1015: 1016: /** 1017: * Shared ListListener instance, subscribed to both the current {@link 1018: * #model} and {@link #selectionModel} properties of the list. 1019: */ 1020: ListListener listListener; 1021: 1022: 1023: /** 1024: * Creates a new <code>JList</code> object. 1025: */ 1026: public JList() 1027: { 1028: init(new DefaultListModel()); 1029: } 1030: 1031: /** 1032: * Creates a new <code>JList</code> object. 1033: * 1034: * @param items the initial list items. 1035: */ 1036: public JList(Object[] items) 1037: { 1038: init(createListModel(items)); 1039: } 1040: 1041: /** 1042: * Creates a new <code>JList</code> object. 1043: * 1044: * @param items the initial list items. 1045: */ 1046: public JList(Vector<?> items) 1047: { 1048: init(createListModel(items)); 1049: } 1050: 1051: /** 1052: * Creates a new <code>JList</code> object. 1053: * 1054: * @param model a model containing the list items (<code>null</code> not 1055: * permitted). 1056: * 1057: * @throws IllegalArgumentException if <code>model</code> is 1058: * <code>null</code>. 1059: */ 1060: public JList(ListModel model) 1061: { 1062: init(model); 1063: } 1064: 1065: /** 1066: * Initializes the list. 1067: * 1068: * @param m the list model (<code>null</code> not permitted). 1069: */ 1070: private void init(ListModel m) 1071: { 1072: if (m == null) 1073: throw new IllegalArgumentException("Null model not permitted."); 1074: dragEnabled = false; 1075: fixedCellHeight = -1; 1076: fixedCellWidth = -1; 1077: layoutOrientation = VERTICAL; 1078: opaque = true; 1079: visibleRowCount = 8; 1080: 1081: cellRenderer = new DefaultListCellRenderer(); 1082: listListener = new ListListener(); 1083: 1084: model = m; 1085: if (model != null) 1086: model.addListDataListener(listListener); 1087: 1088: selectionModel = createSelectionModel(); 1089: if (selectionModel != null) 1090: { 1091: selectionModel.addListSelectionListener(listListener); 1092: selectionModel.setSelectionMode 1093: (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 1094: } 1095: setLayout(null); 1096: 1097: updateUI(); 1098: } 1099: 1100: /** 1101: * Creates the default <code>ListSelectionModel</code>. 1102: * 1103: * @return the <code>ListSelectionModel</code> 1104: */ 1105: protected ListSelectionModel createSelectionModel() 1106: { 1107: return new DefaultListSelectionModel(); 1108: } 1109: 1110: /** 1111: * Gets the value of the {@link #fixedCellHeight} property. This property 1112: * may be <code>-1</code> to indicate that no cell height has been 1113: * set. This property is also set implicitly when the 1114: * {@link #prototypeCellValue} property is set. 1115: * 1116: * @return The current value of the property 1117: * 1118: * @see #fixedCellHeight 1119: * @see #setFixedCellHeight 1120: * @see #setPrototypeCellValue 1121: */ 1122: public int getFixedCellHeight() 1123: { 1124: return fixedCellHeight; 1125: } 1126: 1127: /** 1128: * Sets the value of the {@link #fixedCellHeight} property. This property 1129: * may be <code>-1</code> to indicate that no cell height has been 1130: * set. This property is also set implicitly when the {@link 1131: * #prototypeCellValue} property is set, but setting it explicitly 1132: * overrides the height computed from {@link #prototypeCellValue}. 1133: * 1134: * @param h the height. 1135: * 1136: * @see #getFixedCellHeight 1137: * @see #getPrototypeCellValue 1138: */ 1139: public void setFixedCellHeight(int h) 1140: { 1141: if (fixedCellHeight == h) 1142: return; 1143: 1144: int old = fixedCellHeight; 1145: fixedCellHeight = h; 1146: firePropertyChange("fixedCellHeight", old, h); 1147: } 1148: 1149: 1150: /** 1151: * Gets the value of the {@link #fixedCellWidth} property. This property 1152: * may be <code>-1</code> to indicate that no cell width has been 1153: * set. This property is also set implicitly when the {@link 1154: * #prototypeCellValue} property is set. 1155: * 1156: * @return The current value of the property 1157: * 1158: * @see #setFixedCellWidth 1159: * @see #setPrototypeCellValue 1160: */ 1161: public int getFixedCellWidth() 1162: { 1163: return fixedCellWidth; 1164: } 1165: 1166: /** 1167: * Sets the value of the {@link #fixedCellWidth} property. This property 1168: * may be <code>-1</code> to indicate that no cell width has been 1169: * set. This property is also set implicitly when the {@link 1170: * #prototypeCellValue} property is set, but setting it explicitly 1171: * overrides the width computed from {@link #prototypeCellValue}. 1172: * 1173: * @param w the width. 1174: * 1175: * @see #getFixedCellHeight 1176: * @see #getPrototypeCellValue 1177: */ 1178: public void setFixedCellWidth(int w) 1179: { 1180: if (fixedCellWidth == w) 1181: return; 1182: 1183: int old = fixedCellWidth; 1184: fixedCellWidth = w; 1185: firePropertyChange("fixedCellWidth", old, w); 1186: } 1187: 1188: /** 1189: * Gets the value of the {@link #visibleRowCount} property. The default 1190: * value is 8. 1191: * 1192: * @return the current value of the property. 1193: * 1194: * @see #setVisibleRowCount(int) 1195: */ 1196: public int getVisibleRowCount() 1197: { 1198: return visibleRowCount; 1199: } 1200: 1201: /** 1202: * Sets the value of the {@link #visibleRowCount} property. 1203: * 1204: * @param vc The new property value 1205: * 1206: * @see #getVisibleRowCount() 1207: */ 1208: public void setVisibleRowCount(int vc) 1209: { 1210: if (visibleRowCount != vc) 1211: { 1212: int oldValue = visibleRowCount; 1213: visibleRowCount = Math.max(vc, 0); 1214: firePropertyChange("visibleRowCount", oldValue, vc); 1215: revalidate(); 1216: repaint(); 1217: } 1218: } 1219: 1220: /** 1221: * Adds a {@link ListSelectionListener} to the listener list for this 1222: * list. The listener will be called back with a {@link 1223: * ListSelectionEvent} any time the list's {@link #selectionModel} 1224: * property changes. The source of such events will be the JList, 1225: * not the selection model. 1226: * 1227: * @param listener The new listener to add 1228: */ 1229: public void addListSelectionListener(ListSelectionListener listener) 1230: { 1231: listenerList.add (ListSelectionListener.class, listener); 1232: } 1233: 1234: /** 1235: * Removes a {@link ListSelectionListener} from the listener list for 1236: * this list. The listener will no longer be called when the list's 1237: * {@link #selectionModel} changes. 1238: * 1239: * @param listener The listener to remove 1240: */ 1241: public void removeListSelectionListener(ListSelectionListener listener) 1242: { 1243: listenerList.remove(ListSelectionListener.class, listener); 1244: } 1245: 1246: /** 1247: * Returns an array of all ListSelectionListeners subscribed to this 1248: * list. 1249: * 1250: * @return The current subscribed listeners 1251: * 1252: * @since 1.4 1253: */ 1254: public ListSelectionListener[] getListSelectionListeners() 1255: { 1256: return (ListSelectionListener[]) getListeners(ListSelectionListener.class); 1257: } 1258: 1259: /** 1260: * Returns the selection mode for the list (one of: 1261: * {@link ListSelectionModel#SINGLE_SELECTION}, 1262: * {@link ListSelectionModel#SINGLE_INTERVAL_SELECTION} and 1263: * {@link ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}). 1264: * 1265: * @return The selection mode. 1266: * 1267: * @see #setSelectionMode(int) 1268: */ 1269: public int getSelectionMode() 1270: { 1271: return selectionModel.getSelectionMode(); 1272: } 1273: 1274: /** 1275: * Sets the list's "selectionMode" property, which simply mirrors the 1276: * same property on the list's {@link #selectionModel} property. This 1277: * property should be one of the integer constants 1278: * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>, 1279: * or <code>MULTIPLE_INTERVAL_SELECTION</code> from the {@link 1280: * ListSelectionModel} interface. 1281: * 1282: * @param a The new selection mode 1283: */ 1284: public void setSelectionMode(int a) 1285: { 1286: selectionModel.setSelectionMode(a); 1287: } 1288: 1289: /** 1290: * Adds the interval <code>[a,a]</code> to the set of selections managed 1291: * by this list's {@link #selectionModel} property. Depending on the 1292: * selection mode, this may cause existing selections to become invalid, 1293: * or may simply expand the set of selections. 1294: * 1295: * @param a A number in the half-open range <code>[0, x)</code> where 1296: * <code>x = getModel.getSize()</code>, indicating the index of an 1297: * element in the list to select. When < 0 the selection is cleared. 1298: * 1299: * @see #setSelectionMode 1300: * @see #selectionModel 1301: */ 1302: public void setSelectedIndex(int a) 1303: { 1304: if (a < 0) 1305: selectionModel.clearSelection(); 1306: else 1307: selectionModel.setSelectionInterval(a, a); 1308: } 1309: 1310: /** 1311: * For each element <code>a[i]</code> of the provided array 1312: * <code>a</code>, calls {@link #setSelectedIndex} on <code>a[i]</code>. 1313: * 1314: * @param a an array of selected indices (<code>null</code> not permitted). 1315: * 1316: * @throws NullPointerException if <code>a</code> is <code>null</code>. 1317: * @see #setSelectionMode 1318: * @see #selectionModel 1319: */ 1320: public void setSelectedIndices(int [] a) 1321: { 1322: for (int i = 0; i < a.length; ++i) 1323: setSelectedIndex(a[i]); 1324: } 1325: 1326: /** 1327: * Returns the minimum index of an element in the list which is currently 1328: * selected. 1329: * 1330: * @return A number in the half-open range <code>[0, x)</code> where 1331: * <code>x = getModel.getSize()</code>, indicating the minimum index of 1332: * an element in the list for which the element is selected, or 1333: * <code>-1</code> if no elements are selected 1334: */ 1335: public int getSelectedIndex() 1336: { 1337: return selectionModel.getMinSelectionIndex(); 1338: } 1339: 1340: /** 1341: * Returns <code>true</code> if the model's selection is empty, otherwise 1342: * <code>false</code>. 1343: * 1344: * @return The return value of {@link ListSelectionModel#isSelectionEmpty} 1345: */ 1346: public boolean isSelectionEmpty() 1347: { 1348: return selectionModel.isSelectionEmpty(); 1349: } 1350: 1351: /** 1352: * Returns the list index of the upper left or upper right corner of the 1353: * visible rectangle of this list, depending on the {@link 1354: * Component#getComponentOrientation} property. 1355: * 1356: * @return The index of the first visible list cell, or <code>-1</code> 1357: * if none is visible. 1358: */ 1359: public int getFirstVisibleIndex() 1360: { 1361: ComponentOrientation or = getComponentOrientation(); 1362: Rectangle r = getVisibleRect(); 1363: if (or == ComponentOrientation.RIGHT_TO_LEFT) 1364: r.translate((int) r.getWidth() - 1, 0); 1365: return getUI().locationToIndex(this, r.getLocation()); 1366: } 1367: 1368: 1369: /** 1370: * Returns index of the cell to which specified location is closest to. If 1371: * the location is outside the bounds of the list, then the greatest index 1372: * in the list model is returned. If the list model is empty, then 1373: * <code>-1</code> is returned. 1374: * 1375: * @param location for which to look for in the list 1376: * 1377: * @return index of the cell to which specified location is closest to. 1378: */ 1379: public int locationToIndex(Point location) 1380: { 1381: return getUI().locationToIndex(this, location); 1382: } 1383: 1384: /** 1385: * Returns location of the cell located at the specified index in the list. 1386: * @param index of the cell for which location will be determined 1387: * 1388: * @return location of the cell located at the specified index in the list. 1389: */ 1390: public Point indexToLocation(int index) 1391: { 1392: return getUI().indexToLocation(this, index); 1393: } 1394: 1395: /** 1396: * Returns the list index of the lower right or lower left corner of the 1397: * visible rectangle of this list, depending on the {@link 1398: * Component#getComponentOrientation} property. 1399: * 1400: * @return The index of the last visible list cell, or <code>-1</code> 1401: * if none is visible. 1402: */ 1403: public int getLastVisibleIndex() 1404: { 1405: ComponentOrientation or = getComponentOrientation(); 1406: Rectangle r = getVisibleRect(); 1407: r.translate(0, (int) r.getHeight() - 1); 1408: if (or == ComponentOrientation.LEFT_TO_RIGHT) 1409: r.translate((int) r.getWidth() - 1, 0); 1410: if (getUI().locationToIndex(this, r.getLocation()) == -1 1411: && indexToLocation(getModel().getSize() - 1).y < r.y) 1412: return getModel().getSize() - 1; 1413: return getUI().locationToIndex(this, r.getLocation()); 1414: } 1415: 1416: /** 1417: * Returns the indices of values in the {@link #model} property which are 1418: * selected. 1419: * 1420: * @return An array of model indices, each of which is selected according 1421: * to the {@link #getSelectedValues} property 1422: */ 1423: public int[] getSelectedIndices() 1424: { 1425: int lo, hi, n, i, j; 1426: if (selectionModel.isSelectionEmpty()) 1427: return new int[0]; 1428: lo = selectionModel.getMinSelectionIndex(); 1429: hi = selectionModel.getMaxSelectionIndex(); 1430: n = 0; 1431: for (i = lo; i <= hi; ++i) 1432: if (selectionModel.isSelectedIndex(i)) 1433: n++; 1434: int [] v = new int[n]; 1435: j = 0; 1436: for (i = lo; i <= hi; ++i) 1437: if (selectionModel.isSelectedIndex(i)) 1438: v[j++] = i; 1439: return v; 1440: } 1441: 1442: /** 1443: * Indicates whether the list element at a given index value is 1444: * currently selected. 1445: * 1446: * @param a The index to check 1447: * @return <code>true</code> if <code>a</code> is the index of a selected 1448: * list element 1449: */ 1450: public boolean isSelectedIndex(int a) 1451: { 1452: return selectionModel.isSelectedIndex(a); 1453: } 1454: 1455: /** 1456: * Returns the first value in the list's {@link #model} property which is 1457: * selected, according to the list's {@link #selectionModel} property. 1458: * This is equivalent to calling 1459: * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check 1460: * for the special index value of <code>-1</code> which returns null 1461: * <code>null</code>. 1462: * 1463: * @return The first selected element, or <code>null</code> if no element 1464: * is selected. 1465: * 1466: * @see #getSelectedValues 1467: */ 1468: public Object getSelectedValue() 1469: { 1470: int index = getSelectedIndex(); 1471: if (index == -1) 1472: return null; 1473: return getModel().getElementAt(index); 1474: } 1475: 1476: /** 1477: * Returns all the values in the list's {@link #model} property which are 1478: * selected, according to the list's {@link #selectionModel} property. 1479: * 1480: * @return An array containing all the selected values 1481: * @see #setSelectedValue 1482: */ 1483: public Object[] getSelectedValues() 1484: { 1485: int[] idx = getSelectedIndices(); 1486: Object[] v = new Object[idx.length]; 1487: for (int i = 0; i < idx.length; ++i) 1488: v[i] = getModel().getElementAt(idx[i]); 1489: return v; 1490: } 1491: 1492: /** 1493: * Gets the value of the {@link #selectionBackground} property. 1494: * 1495: * @return The current value of the property 1496: */ 1497: public Color getSelectionBackground() 1498: { 1499: return selectionBackground; 1500: } 1501: 1502: /** 1503: * Sets the value of the {@link #selectionBackground} property. 1504: * 1505: * @param c The new value of the property 1506: */ 1507: public void setSelectionBackground(Color c) 1508: { 1509: if (selectionBackground == c) 1510: return; 1511: 1512: Color old = selectionBackground; 1513: selectionBackground = c; 1514: firePropertyChange("selectionBackground", old, c); 1515: repaint(); 1516: } 1517: 1518: /** 1519: * Gets the value of the {@link #selectionForeground} property. 1520: * 1521: * @return The current value of the property 1522: */ 1523: public Color getSelectionForeground() 1524: { 1525: return selectionForeground; 1526: } 1527: 1528: /** 1529: * Sets the value of the {@link #selectionForeground} property. 1530: * 1531: * @param c The new value of the property 1532: */ 1533: public void setSelectionForeground(Color c) 1534: { 1535: if (selectionForeground == c) 1536: return; 1537: 1538: Color old = selectionForeground; 1539: selectionForeground = c; 1540: firePropertyChange("selectionForeground", old, c); 1541: } 1542: 1543: /** 1544: * Sets the selection to cover only the specified value, if it 1545: * exists in the model. 1546: * 1547: * @param obj The object to select 1548: * @param scroll Whether to scroll the list to make the newly selected 1549: * value visible 1550: * 1551: * @see #ensureIndexIsVisible 1552: */ 1553: 1554: public void setSelectedValue(Object obj, boolean scroll) 1555: { 1556: for (int i = 0; i < model.getSize(); ++i) 1557: { 1558: if (model.getElementAt(i).equals(obj)) 1559: { 1560: setSelectedIndex(i); 1561: if (scroll) 1562: ensureIndexIsVisible(i); 1563: break; 1564: } 1565: } 1566: } 1567: 1568: /** 1569: * Scrolls this list to make the specified cell visible. This 1570: * only works if the list is contained within a viewport. 1571: * 1572: * @param i The list index to make visible 1573: * 1574: * @see JComponent#scrollRectToVisible 1575: */ 1576: public void ensureIndexIsVisible(int i) 1577: { 1578: Rectangle r = getUI().getCellBounds(this, i, i); 1579: if (r != null) 1580: scrollRectToVisible(r); 1581: } 1582: 1583: /** 1584: * Sets the {@link #model} property of the list to a new anonymous 1585: * {@link AbstractListModel} subclass which accesses the provided Object 1586: * array directly. 1587: * 1588: * @param listData The object array to build a new list model on 1589: * @see #setModel 1590: */ 1591: public void setListData(Object[] listData) 1592: { 1593: setModel(createListModel(listData)); 1594: } 1595: 1596: /** 1597: * Returns a {@link ListModel} backed by the specified array. 1598: * 1599: * @param items the list items (don't use <code>null</code>). 1600: * 1601: * @return A list model containing the specified items. 1602: */ 1603: private ListModel createListModel(final Object[] items) 1604: { 1605: return new AbstractListModel() 1606: { 1607: public int getSize() 1608: { 1609: return items.length; 1610: } 1611: public Object getElementAt(int i) 1612: { 1613: return items[i]; 1614: } 1615: }; 1616: } 1617: 1618: /** 1619: * Returns a {@link ListModel} backed by the specified vector. 1620: * 1621: * @param items the list items (don't use <code>null</code>). 1622: * 1623: * @return A list model containing the specified items. 1624: */ 1625: private ListModel createListModel(final Vector items) 1626: { 1627: return new AbstractListModel() 1628: { 1629: public int getSize() 1630: { 1631: return items.size(); 1632: } 1633: public Object getElementAt(int i) 1634: { 1635: return items.get(i); 1636: } 1637: }; 1638: } 1639: 1640: /** 1641: * Sets the {@link #model} property of the list to a new anonymous {@link 1642: * AbstractListModel} subclass which accesses the provided vector 1643: * directly. 1644: * 1645: * @param listData The object array to build a new list model on 1646: * @see #setModel 1647: */ 1648: public void setListData(final Vector<?> listData) 1649: { 1650: setModel(new AbstractListModel() 1651: { 1652: public int getSize() 1653: { 1654: return listData.size(); 1655: } 1656: 1657: public Object getElementAt(int i) 1658: { 1659: return listData.elementAt(i); 1660: } 1661: }); 1662: } 1663: 1664: /** 1665: * Gets the value of the {@link #cellRenderer} property. 1666: * 1667: * @return The current value of the property 1668: */ 1669: public ListCellRenderer getCellRenderer() 1670: { 1671: return cellRenderer; 1672: } 1673: 1674: /** 1675: * Sets the value of the {@link #getCellRenderer} property. 1676: * 1677: * @param renderer The new property value 1678: */ 1679: public void setCellRenderer(ListCellRenderer renderer) 1680: { 1681: if (cellRenderer == renderer) 1682: return; 1683: 1684: ListCellRenderer old = cellRenderer; 1685: cellRenderer = renderer; 1686: firePropertyChange("cellRenderer", old, renderer); 1687: revalidate(); 1688: repaint(); 1689: } 1690: 1691: /** 1692: * Gets the value of the {@link #model} property. 1693: * 1694: * @return The current value of the property 1695: */ 1696: public ListModel getModel() 1697: { 1698: return model; 1699: } 1700: 1701: /** 1702: * Sets the value of the {@link #model} property. The list's {@link 1703: * #listListener} is unsubscribed from the existing model, if it exists, 1704: * and re-subscribed to the new model. 1705: * 1706: * @param model the new model (<code>null</code> not permitted). 1707: * 1708: * @throws IllegalArgumentException if <code>model</code> is 1709: * <code>null</code>. 1710: */ 1711: public void setModel(ListModel model) 1712: { 1713: if (model == null) 1714: throw new IllegalArgumentException("Null 'model' argument."); 1715: if (this.model == model) 1716: return; 1717: 1718: if (this.model != null) 1719: this.model.removeListDataListener(listListener); 1720: 1721: ListModel old = this.model; 1722: this.model = model; 1723: 1724: if (this.model != null) 1725: this.model.addListDataListener(listListener); 1726: 1727: firePropertyChange("model", old, model); 1728: revalidate(); 1729: repaint(); 1730: } 1731: 1732: /** 1733: * Returns the selection model for the {@link JList} component. Note that 1734: * this class contains a range of convenience methods for configuring the 1735: * selection model:<br> 1736: * <ul> 1737: * <li>{@link #clearSelection()};</li> 1738: * <li>{@link #setSelectionMode(int)};</li> 1739: * <li>{@link #addSelectionInterval(int, int)};</li> 1740: * <li>{@link #setSelectedIndex(int)};</li> 1741: * <li>{@link #setSelectedIndices(int[])};</li> 1742: * <li>{@link #setSelectionInterval(int, int)}.</li> 1743: * </ul> 1744: * 1745: * @return The selection model. 1746: */ 1747: public ListSelectionModel getSelectionModel() 1748: { 1749: return selectionModel; 1750: } 1751: 1752: /** 1753: * Sets the value of the {@link #selectionModel} property. The list's 1754: * {@link #listListener} is unsubscribed from the existing selection 1755: * model, if it exists, and re-subscribed to the new selection model. 1756: * 1757: * @param model The new property value 1758: */ 1759: public void setSelectionModel(ListSelectionModel model) 1760: { 1761: if (selectionModel == model) 1762: return; 1763: 1764: if (selectionModel != null) 1765: selectionModel.removeListSelectionListener(listListener); 1766: 1767: ListSelectionModel old = selectionModel; 1768: selectionModel = model; 1769: 1770: if (selectionModel != null) 1771: selectionModel.addListSelectionListener(listListener); 1772: 1773: firePropertyChange("selectionModel", old, model); 1774: revalidate(); 1775: repaint(); 1776: } 1777: 1778: /** 1779: * Gets the value of the UI property. 1780: * 1781: * @return The current property value 1782: */ 1783: public ListUI getUI() 1784: { 1785: return (ListUI) ui; 1786: } 1787: 1788: /** 1789: * Sets the value of the UI property. 1790: * 1791: * @param ui The new property value 1792: */ 1793: public void setUI(ListUI ui) 1794: { 1795: super.setUI(ui); 1796: } 1797: 1798: /** 1799: * Calls {@link #setUI} with the {@link ListUI} subclass 1800: * returned from calling {@link UIManager#getUI}. 1801: */ 1802: public void updateUI() 1803: { 1804: setUI((ListUI) UIManager.getUI(this)); 1805: } 1806: 1807: /** 1808: * Return the class identifier for the list's UI property. This should 1809: * be the constant string <code>"ListUI"</code>, and map to an 1810: * appropriate UI class in the {@link UIManager}. 1811: * 1812: * @return The class identifier 1813: */ 1814: public String getUIClassID() 1815: { 1816: return "ListUI"; 1817: } 1818: 1819: 1820: /** 1821: * Returns the current value of the {@link #prototypeCellValue} 1822: * property. This property holds a reference to a "prototype" data value 1823: * -- typically a String -- which is used to calculate the {@link 1824: * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the 1825: * {@link #cellRenderer} property to acquire a component to render the 1826: * prototype. 1827: * 1828: * @return The current prototype cell value 1829: * @see #setPrototypeCellValue 1830: */ 1831: public Object getPrototypeCellValue() 1832: { 1833: return prototypeCellValue; 1834: } 1835: 1836: /** 1837: * <p>Set the {@link #prototypeCellValue} property. This property holds a 1838: * reference to a "prototype" data value -- typically a String -- which 1839: * is used to calculate the {@link #fixedCellWidth} and {@link 1840: * #fixedCellHeight} properties, using the {@link #cellRenderer} property 1841: * to acquire a component to render the prototype.</p> 1842: * 1843: * <p>It is important that you <em>not</em> set this value to a 1844: * component. It has to be a <em>data value</em> such as the objects you 1845: * would find in the list's model. Setting it to a component will have 1846: * undefined (and undesirable) affects. </p> 1847: * 1848: * @param obj The new prototype cell value 1849: * @see #getPrototypeCellValue 1850: */ 1851: public void setPrototypeCellValue(Object obj) 1852: { 1853: if (prototypeCellValue == obj) 1854: return; 1855: 1856: Object old = prototypeCellValue; 1857: Component comp = getCellRenderer() 1858: .getListCellRendererComponent(this, obj, 0, false, false); 1859: Dimension d = comp.getPreferredSize(); 1860: fixedCellWidth = d.width; 1861: fixedCellHeight = d.height; 1862: prototypeCellValue = obj; 1863: firePropertyChange("prototypeCellValue", old, obj); 1864: } 1865: 1866: public AccessibleContext getAccessibleContext() 1867: { 1868: return new AccessibleJList(); 1869: } 1870: 1871: /** 1872: * Returns a size indicating how much space this list would like to 1873: * consume, when contained in a scrollable viewport. This is part of the 1874: * {@link Scrollable} interface, which interacts with {@link 1875: * ScrollPaneLayout} and {@link JViewport} to define scrollable objects. 1876: * 1877: * @return The preferred size 1878: */ 1879: public Dimension getPreferredScrollableViewportSize() 1880: { 1881: //If the layout orientation is not VERTICAL, then this will 1882: //return the value from getPreferredSize. The current ListUI is 1883: //expected to override getPreferredSize to return an appropriate value. 1884: if (getLayoutOrientation() != VERTICAL) 1885: return getPreferredSize(); 1886: 1887: int size = getModel().getSize(); 1888: 1889: // Trivial case: if fixedCellWidth and fixedCellHeight were set 1890: // just use them 1891: if (fixedCellHeight != -1 && fixedCellWidth != -1) 1892: return new Dimension(fixedCellWidth, size * fixedCellHeight); 1893: 1894: // If the model is empty we use 16 * the number of visible rows 1895: // for the height and either fixedCellWidth (if set) or 256 1896: // for the width 1897: if (size == 0) 1898: { 1899: if (fixedCellWidth == -1) 1900: return new Dimension(256, 16 * getVisibleRowCount()); 1901: else 1902: return new Dimension(fixedCellWidth, 16 * getVisibleRowCount()); 1903: } 1904: 1905: // Calculate the width: if fixedCellWidth was set use that, otherwise 1906: // use the preferredWidth 1907: int prefWidth; 1908: if (fixedCellWidth != -1) 1909: prefWidth = fixedCellWidth; 1910: else 1911: prefWidth = getPreferredSize().width; 1912: 1913: // Calculate the height: if fixedCellHeight was set use that, otherwise 1914: // use the height of the first row multiplied by the number of visible 1915: // rows 1916: int prefHeight; 1917: if (fixedCellHeight != -1) 1918: prefHeight = fixedCellHeight; 1919: else 1920: prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height; 1921: 1922: return new Dimension (prefWidth, prefHeight); 1923: } 1924: 1925: /** 1926: * <p>Return the number of pixels the list must scroll in order to move a 1927: * "unit" of the list into the provided visible rectangle. When the 1928: * provided direction is positive, the call describes a "downwards" 1929: * scroll, which will be exposing a cell at a <em>greater</em> index in 1930: * the list than those elements currently showing. Then the provided 1931: * direction is negative, the call describes an "upwards" scroll, which 1932: * will be exposing a cell at a <em>lesser</em> index in the list than 1933: * those elements currently showing.</p> 1934: * 1935: * <p>If the provided orientation is <code>HORIZONTAL</code>, the above 1936: * comments refer to "rightwards" for positive direction, and "leftwards" 1937: * for negative.</p> 1938: * 1939: * 1940: * @param visibleRect The rectangle to scroll an element into 1941: * @param orientation One of the numeric consants <code>VERTICAL</code> 1942: * or <code>HORIZONTAL</code> 1943: * @param direction An integer indicating the scroll direction: positive means 1944: * forwards (down, right), negative means backwards (up, left) 1945: * 1946: * @return The scrollable unit increment, in pixels 1947: */ 1948: public int getScrollableUnitIncrement(Rectangle visibleRect, 1949: int orientation, int direction) 1950: { 1951: int unit = -1; 1952: if (orientation == SwingConstants.VERTICAL) 1953: { 1954: int row = getFirstVisibleIndex(); 1955: if (row == -1) 1956: unit = 0; 1957: else if (direction > 0) 1958: { 1959: // Scrolling down. 1960: Rectangle bounds = getCellBounds(row, row); 1961: if (bounds != null) 1962: unit = bounds.height - (visibleRect.y - bounds.y); 1963: else 1964: unit = 0; 1965: } 1966: else 1967: { 1968: // Scrolling up. 1969: Rectangle bounds = getCellBounds(row, row); 1970: // First row. 1971: if (row == 0 && bounds.y == visibleRect.y) 1972: unit = 0; // No need to scroll. 1973: else if (bounds.y == visibleRect.y) 1974: { 1975: // Scroll to previous row. 1976: Point loc = bounds.getLocation(); 1977: loc.y--; 1978: int prev = locationToIndex(loc); 1979: Rectangle prevR = getCellBounds(prev, prev); 1980: if (prevR == null || prevR.y >= bounds.y) 1981: unit = 0; // For multicolumn lists. 1982: else 1983: unit = prevR.height; 1984: } 1985: else 1986: unit = visibleRect.y - bounds.y; 1987: } 1988: } 1989: else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL) 1990: { 1991: // Horizontal scrolling. 1992: int i = locationToIndex(visibleRect.getLocation()); 1993: if (i != -1) 1994: { 1995: Rectangle b = getCellBounds(i, i); 1996: if (b != null) 1997: { 1998: if (b.x != visibleRect.x) 1999: { 2000: if (direction < 0) 2001: unit = Math.abs(b.x - visibleRect.x); 2002: else 2003: unit = b.width + b.x - visibleRect.x; 2004: } 2005: else 2006: unit = b.width; 2007: } 2008: } 2009: } 2010: 2011: if (unit == -1) 2012: { 2013: // This fallback seems to be used by the RI for the degenerate cases 2014: // not covered above. 2015: Font f = getFont(); 2016: unit = f != null ? f.getSize() : 1; 2017: } 2018: return unit; 2019: } 2020: 2021: /** 2022: * <p>Return the number of pixels the list must scroll in order to move a 2023: * "block" of the list into the provided visible rectangle. When the 2024: * provided direction is positive, the call describes a "downwards" 2025: * scroll, which will be exposing a cell at a <em>greater</em> index in 2026: * the list than those elements currently showing. Then the provided 2027: * direction is negative, the call describes an "upwards" scroll, which 2028: * will be exposing a cell at a <em>lesser</em> index in the list than 2029: * those elements currently showing.</p> 2030: * 2031: * <p>If the provided orientation is <code>HORIZONTAL</code>, the above 2032: * comments refer to "rightwards" for positive direction, and "leftwards" 2033: * for negative.</p> 2034: * 2035: * 2036: * @param visibleRect The rectangle to scroll an element into 2037: * @param orientation One of the numeric consants <code>VERTICAL</code> 2038: * or <code>HORIZONTAL</code> 2039: * @param direction An integer indicating the scroll direction: positive means 2040: * forwards (down, right), negative means backwards (up, left) 2041: * 2042: * @return The scrollable unit increment, in pixels 2043: */ 2044: public int getScrollableBlockIncrement(Rectangle visibleRect, 2045: int orientation, int direction) 2046: { 2047: int block = -1; 2048: if (orientation == SwingConstants.VERTICAL) 2049: { 2050: // Default block scroll. Special cases are handled below for 2051: // better usability. 2052: block = visibleRect.height; 2053: if (direction > 0) 2054: { 2055: // Scroll down. 2056: // Scroll so that after scrolling the last line aligns with 2057: // the lower boundary of the visible area. 2058: Point p = new Point(visibleRect.x, 2059: visibleRect.y + visibleRect.height - 1); 2060: int last = locationToIndex(p); 2061: if (last != -1) 2062: { 2063: Rectangle lastR = getCellBounds(last, last); 2064: if (lastR != null) 2065: { 2066: block = lastR.y - visibleRect.y; 2067: if (block == 0&& last < getModel().getSize() - 1) 2068: block = lastR.height; 2069: } 2070: } 2071: } 2072: else 2073: { 2074: // Scroll up. 2075: // Scroll so that after scrolling the first line aligns with 2076: // the upper boundary of the visible area. 2077: Point p = new Point(visibleRect.x, 2078: visibleRect.y - visibleRect.height); 2079: int newFirst = locationToIndex(p); 2080: if (newFirst != -1) 2081: { 2082: int first = getFirstVisibleIndex(); 2083: if (first == -1) 2084: first = locationToIndex(visibleRect.getLocation()); 2085: Rectangle newFirstR = getCellBounds(newFirst, newFirst); 2086: Rectangle firstR = getCellBounds(first, first); 2087: if (newFirstR != null && firstR != null) 2088: { 2089: // Search first item that would left the current first 2090: // item visible when scrolled to. 2091: while (newFirstR.y + visibleRect.height 2092: < firstR.y + firstR.height 2093: && newFirstR.y < firstR.y) 2094: { 2095: newFirst++; 2096: newFirstR = getCellBounds(newFirst, newFirst); 2097: } 2098: block = visibleRect.y - newFirstR.y; 2099: if (block <= 0 && newFirstR.y > 0) 2100: { 2101: newFirst--; 2102: newFirstR = getCellBounds(newFirst, newFirst); 2103: if (newFirstR != null) 2104: block = visibleRect.y - newFirstR.y; 2105: } 2106: } 2107: } 2108: } 2109: } 2110: else if (orientation == SwingConstants.HORIZONTAL 2111: && getLayoutOrientation() != VERTICAL) 2112: { 2113: // Default block increment. Special cases are handled below for 2114: // better usability. 2115: block = visibleRect.width; 2116: if (direction > 0) 2117: { 2118: // Scroll right. 2119: Point p = new Point(visibleRect.x + visibleRect.width + 1, 2120: visibleRect.y); 2121: int last = locationToIndex(p); 2122: if (last != -1) 2123: { 2124: Rectangle lastR = getCellBounds(last, last); 2125: if (lastR != null) 2126: { 2127: block = lastR.x - visibleRect.x; 2128: if (block < 0) 2129: block += lastR.width; 2130: else if (block == 0 && last < getModel().getSize() - 1) 2131: block = lastR.width; 2132: } 2133: } 2134: } 2135: else 2136: { 2137: // Scroll left. 2138: Point p = new Point(visibleRect.x - visibleRect.width, 2139: visibleRect.y); 2140: int first = locationToIndex(p); 2141: if (first != -1) 2142: { 2143: Rectangle firstR = getCellBounds(first, first); 2144: if (firstR != null) 2145: { 2146: if (firstR.x < visibleRect.x - visibleRect.width) 2147: { 2148: if (firstR.x + firstR.width > visibleRect.x) 2149: block = visibleRect.x - firstR.x; 2150: else 2151: block = visibleRect.x - firstR.x - firstR.width; 2152: } 2153: else 2154: block = visibleRect.x - firstR.x; 2155: } 2156: } 2157: } 2158: } 2159: 2160: return block; 2161: } 2162: 2163: /** 2164: * Gets the value of the <code>scrollableTracksViewportWidth</code> property. 2165: * 2166: * @return <code>true</code> if the viewport is larger (horizontally) 2167: * than the list and the list should be expanded to fit the viewport; 2168: * <code>false</code> if the viewport is smaller than the list and the 2169: * list should scroll (horizontally) within the viewport 2170: */ 2171: public boolean getScrollableTracksViewportWidth() 2172: { 2173: Component parent = getParent(); 2174: boolean retVal = false; 2175: if (parent instanceof JViewport) 2176: { 2177: JViewport viewport = (JViewport) parent; 2178: Dimension pref = getPreferredSize(); 2179: if (viewport.getSize().width > pref.width) 2180: retVal = true; 2181: if ((getLayoutOrientation() == HORIZONTAL_WRAP) 2182: && (getVisibleRowCount() <= 0)) 2183: retVal = true; 2184: } 2185: return retVal; 2186: } 2187: 2188: /** 2189: * Gets the value of the </code>scrollableTracksViewportWidth</code> property. 2190: * 2191: * @return <code>true</code> if the viewport is larger (vertically) 2192: * than the list and the list should be expanded to fit the viewport; 2193: * <code>false</code> if the viewport is smaller than the list and the 2194: * list should scroll (vertically) within the viewport 2195: */ 2196: public boolean getScrollableTracksViewportHeight() 2197: { 2198: Component parent = getParent(); 2199: boolean retVal = false; 2200: if (parent instanceof JViewport) 2201: { 2202: JViewport viewport = (JViewport) parent; 2203: Dimension pref = getPreferredSize(); 2204: if (viewport.getSize().height > pref.height) 2205: retVal = true; 2206: if ((getLayoutOrientation() == VERTICAL_WRAP) 2207: && (getVisibleRowCount() <= 0)) 2208: retVal = true; 2209: } 2210: return retVal; 2211: } 2212: 2213: /** 2214: * Returns the index of the anchor item in the current selection, or 2215: * <code>-1</code> if there is no anchor item. 2216: * 2217: * @return The item index. 2218: */ 2219: public int getAnchorSelectionIndex() 2220: { 2221: return selectionModel.getAnchorSelectionIndex(); 2222: } 2223: 2224: /** 2225: * Returns the index of the lead item in the current selection, or 2226: * <code>-1</code> if there is no lead item. 2227: * 2228: * @return The item index. 2229: */ 2230: public int getLeadSelectionIndex() 2231: { 2232: return selectionModel.getLeadSelectionIndex(); 2233: } 2234: 2235: /** 2236: * Returns the lowest item index in the current selection, or <code>-1</code> 2237: * if there is no selection. 2238: * 2239: * @return The index. 2240: * 2241: * @see #getMaxSelectionIndex() 2242: */ 2243: public int getMinSelectionIndex() 2244: { 2245: return selectionModel.getMinSelectionIndex(); 2246: } 2247: 2248: /** 2249: * Returns the highest item index in the current selection, or 2250: * <code>-1</code> if there is no selection. 2251: * 2252: * @return The index. 2253: * 2254: * @see #getMinSelectionIndex() 2255: */ 2256: public int getMaxSelectionIndex() 2257: { 2258: return selectionModel.getMaxSelectionIndex(); 2259: } 2260: 2261: /** 2262: * Clears the current selection. 2263: */ 2264: public void clearSelection() 2265: { 2266: selectionModel.clearSelection(); 2267: } 2268: 2269: /** 2270: * Sets the current selection to the items in the specified range (inclusive). 2271: * Note that <code>anchor</code> can be less than, equal to, or greater than 2272: * <code>lead</code>. 2273: * 2274: * @param anchor the index of the anchor item. 2275: * @param lead the index of the anchor item. 2276: */ 2277: public void setSelectionInterval(int anchor, int lead) 2278: { 2279: selectionModel.setSelectionInterval(anchor, lead); 2280: } 2281: 2282: /** 2283: * Adds the specified interval to the current selection. Note that 2284: * <code>anchor</code> can be less than, equal to, or greater than 2285: * <code>lead</code>. 2286: * 2287: * @param anchor the index of the anchor item. 2288: * @param lead the index of the lead item. 2289: */ 2290: public void addSelectionInterval(int anchor, int lead) 2291: { 2292: selectionModel.addSelectionInterval(anchor, lead); 2293: } 2294: 2295: /** 2296: * Removes the specified interval from the current selection. Note that 2297: * <code>index0</code> can be less than, equal to, or greater than 2298: * <code>index1</code>. 2299: * 2300: * @param index0 an index for one end of the range. 2301: * @param index1 an index for the other end of the range. 2302: */ 2303: public void removeSelectionInterval(int index0, int index1) 2304: { 2305: selectionModel.removeSelectionInterval(index0, index1); 2306: } 2307: 2308: /** 2309: * Returns the <code>valueIsAdjusting</code> flag from the list's selection 2310: * model. 2311: * 2312: * @return the value 2313: */ 2314: public boolean getValueIsAdjusting() 2315: { 2316: return selectionModel.getValueIsAdjusting(); 2317: } 2318: 2319: /** 2320: * Sets the <code>valueIsAdjusting</code> flag in the list's selection 2321: * model. 2322: * 2323: * @param isAdjusting the new value 2324: */ 2325: public void setValueIsAdjusting(boolean isAdjusting) 2326: { 2327: selectionModel.setValueIsAdjusting(isAdjusting); 2328: } 2329: 2330: /** 2331: * Return the value of the <code>dragEnabled</code> property. 2332: * 2333: * @return the value 2334: * 2335: * @since 1.4 2336: */ 2337: public boolean getDragEnabled() 2338: { 2339: return dragEnabled; 2340: } 2341: 2342: /** 2343: * Set the <code>dragEnabled</code> property. 2344: * 2345: * @param enabled new value 2346: * 2347: * @since 1.4 2348: */ 2349: public void setDragEnabled(boolean enabled) 2350: { 2351: dragEnabled = enabled; 2352: } 2353: 2354: /** 2355: * Returns the layout orientation, which will be one of {@link #VERTICAL}, 2356: * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value 2357: * is {@link #VERTICAL}. 2358: * 2359: * @return the orientation. 2360: * 2361: * @see #setLayoutOrientation(int) 2362: * @since 1.4 2363: */ 2364: public int getLayoutOrientation() 2365: { 2366: return layoutOrientation; 2367: } 2368: 2369: /** 2370: * Sets the layout orientation (this is a bound property with the name 2371: * 'layoutOrientation'). Valid orientations are {@link #VERTICAL}, 2372: * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. 2373: * 2374: * @param orientation the orientation. 2375: * 2376: * @throws IllegalArgumentException if <code>orientation</code> is not one 2377: * of the specified values. 2378: * @since 1.4 2379: * @see #getLayoutOrientation() 2380: */ 2381: public void setLayoutOrientation(int orientation) 2382: { 2383: if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP) 2384: throw new IllegalArgumentException(); 2385: if (layoutOrientation == orientation) 2386: return; 2387: 2388: int old = layoutOrientation; 2389: layoutOrientation = orientation; 2390: firePropertyChange("layoutOrientation", old, orientation); 2391: } 2392: 2393: /** 2394: * Returns the bounds of the rectangle that encloses both list cells 2395: * with index0 and index1. 2396: * 2397: * @param index0 the index of the first cell 2398: * @param index1 the index of the second cell 2399: * 2400: * @return the bounds of the rectangle that encloses both list cells 2401: * with index0 and index1, <code>null</code> if one of the indices is 2402: * not valid 2403: */ 2404: public Rectangle getCellBounds(int index0, int index1) 2405: { 2406: ListUI ui = getUI(); 2407: Rectangle bounds = null; 2408: if (ui != null) 2409: { 2410: bounds = ui.getCellBounds(this, index0, index1); 2411: } 2412: // When the UI is null, this method also returns null in the RI. 2413: return bounds; 2414: } 2415: 2416: /** 2417: * Returns the index of the next list element (beginning at 2418: * <code>startIndex</code> and moving in the specified direction through the 2419: * list, looping around if necessary) that starts with <code>prefix</code> 2420: * (ignoring case). 2421: * 2422: * @param prefix the prefix to search for in the cell values 2423: * @param startIndex the index where to start searching from 2424: * @param direction the search direction, either {@link Position.Bias#Forward} 2425: * or {@link Position.Bias#Backward} (<code>null</code> is interpreted 2426: * as {@link Position.Bias#Backward}. 2427: * 2428: * @return the index of the found element or -1 if no such element has 2429: * been found 2430: * 2431: * @throws IllegalArgumentException if prefix is <code>null</code> or 2432: * startIndex is not valid 2433: * 2434: * @since 1.4 2435: */ 2436: public int getNextMatch(String prefix, int startIndex, 2437: Position.Bias direction) 2438: { 2439: if (prefix == null) 2440: throw new IllegalArgumentException("The argument 'prefix' must not be" 2441: + " null."); 2442: if (startIndex < 0) 2443: throw new IllegalArgumentException("The argument 'startIndex' must not" 2444: + " be less than zero."); 2445: 2446: int size = model.getSize(); 2447: if (startIndex >= model.getSize()) 2448: throw new IllegalArgumentException("The argument 'startIndex' must not" 2449: + " be greater than the number of" 2450: + " elements in the ListModel."); 2451: 2452: int result = -1; 2453: int current = startIndex; 2454: int delta = -1; 2455: int itemCount = model.getSize(); 2456: boolean finished = false; 2457: prefix = prefix.toUpperCase(); 2458: 2459: if (direction == Position.Bias.Forward) 2460: delta = 1; 2461: while (!finished) 2462: { 2463: String itemStr = model.getElementAt(current).toString().toUpperCase(); 2464: if (itemStr.startsWith(prefix)) 2465: return current; 2466: current = (current + delta); 2467: if (current == -1) 2468: current += itemCount; 2469: else 2470: current = current % itemCount; 2471: finished = current == startIndex; 2472: } 2473: return result; 2474: } 2475: 2476: /** 2477: * Returns a string describing the attributes for the <code>JList</code> 2478: * component, for use in debugging. The return value is guaranteed to be 2479: * non-<code>null</code>, but the format of the string may vary between 2480: * implementations. 2481: * 2482: * @return A string describing the attributes of the <code>JList</code>. 2483: */ 2484: protected String paramString() 2485: { 2486: CPStringBuilder sb = new CPStringBuilder(super.paramString()); 2487: sb.append(",fixedCellHeight=").append(getFixedCellHeight()); 2488: sb.append(",fixedCellWidth=").append(getFixedCellWidth()); 2489: sb.append(",selectionBackground="); 2490: if (getSelectionBackground() != null) 2491: sb.append(getSelectionBackground()); 2492: sb.append(",selectionForeground="); 2493: if (getSelectionForeground() != null) 2494: sb.append(getSelectionForeground()); 2495: sb.append(",visibleRowCount=").append(getVisibleRowCount()); 2496: sb.append(",layoutOrientation=").append(getLayoutOrientation()); 2497: return sb.toString(); 2498: } 2499: }