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 gnu.java.lang.CPStringBuilder; 042 043 import java.awt.Color; 044 import java.awt.Component; 045 import java.awt.ComponentOrientation; 046 import java.awt.Cursor; 047 import java.awt.Dimension; 048 import java.awt.Font; 049 import java.awt.FontMetrics; 050 import java.awt.Point; 051 import java.awt.Rectangle; 052 import java.awt.event.FocusListener; 053 import java.beans.PropertyChangeEvent; 054 import java.beans.PropertyChangeListener; 055 import java.util.Locale; 056 import java.util.Vector; 057 058 import javax.accessibility.Accessible; 059 import javax.accessibility.AccessibleComponent; 060 import javax.accessibility.AccessibleContext; 061 import javax.accessibility.AccessibleRole; 062 import javax.accessibility.AccessibleSelection; 063 import javax.accessibility.AccessibleState; 064 import javax.accessibility.AccessibleStateSet; 065 import javax.swing.event.ListDataEvent; 066 import javax.swing.event.ListDataListener; 067 import javax.swing.event.ListSelectionEvent; 068 import javax.swing.event.ListSelectionListener; 069 import javax.swing.plaf.ListUI; 070 import javax.swing.text.Position; 071 072 /** 073 * <p>This class is a facade over three separate objects: {@link 074 * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and 075 * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list" 076 * concept, with independently replacable (possibly client-provided) models 077 * for its contents and its current selection. In addition, each element in 078 * the list is rendered via a strategy class {@link 079 * javax.swing.ListCellRenderer}.</p> 080 * 081 * <p>Lists have many properties, some of which are stored in this class 082 * while others are delegated to the list's model or selection. The 083 * following properties are available:</p> 084 * 085 * <table> 086 * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> 087 * <tr><td>accessibleContext </td><td>list </td><td>no </td></tr> 088 * <tr><td>anchorSelectionIndex </td><td>selection</td><td>no </td></tr> 089 * <tr><td>cellRenderer </td><td>list </td><td>yes </td></tr> 090 * <tr><td>dragEnabled </td><td>list </td><td>no </td></tr> 091 * <tr><td>firstVisibleIndex </td><td>list </td><td>no </td></tr> 092 * <tr><td>fixedCellHeight </td><td>list </td><td>yes </td></tr> 093 * <tr><td>fixedCellWidth </td><td>list </td><td>yes </td></tr> 094 * <tr><td>lastVisibleIndex </td><td>list </td><td>no </td></tr> 095 * <tr><td>layoutOrientation </td><td>list </td><td>yes </td></tr> 096 * <tr><td>leadSelectionIndex </td><td>selection</td><td>no </td></tr> 097 * <tr><td>maxSelectionIndex </td><td>selection</td><td>no </td></tr> 098 * <tr><td>minSelectionIndex </td><td>selection</td><td>no </td></tr> 099 * <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 }