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