Source for javax.swing.JList

   1: /* JList.java --
   2:    Copyright (C) 2002, 2003, 2004, 2005, 2006,  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.ComponentOrientation;
  44: import java.awt.Cursor;
  45: import java.awt.Dimension;
  46: import java.awt.Font;
  47: import java.awt.FontMetrics;
  48: import java.awt.Point;
  49: import java.awt.Rectangle;
  50: import java.awt.event.FocusListener;
  51: import java.beans.PropertyChangeEvent;
  52: import java.beans.PropertyChangeListener;
  53: import java.util.Locale;
  54: import java.util.Vector;
  55: 
  56: import javax.accessibility.Accessible;
  57: import javax.accessibility.AccessibleComponent;
  58: import javax.accessibility.AccessibleContext;
  59: import javax.accessibility.AccessibleRole;
  60: import javax.accessibility.AccessibleSelection;
  61: import javax.accessibility.AccessibleState;
  62: import javax.accessibility.AccessibleStateSet;
  63: import javax.swing.event.ListDataEvent;
  64: import javax.swing.event.ListDataListener;
  65: import javax.swing.event.ListSelectionEvent;
  66: import javax.swing.event.ListSelectionListener;
  67: import javax.swing.plaf.ListUI;
  68: import javax.swing.text.Position;
  69: 
  70: /**
  71:  * <p>This class is a facade over three separate objects: {@link
  72:  * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and
  73:  * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list"
  74:  * concept, with independently replacable (possibly client-provided) models
  75:  * for its contents and its current selection. In addition, each element in
  76:  * the list is rendered via a strategy class {@link
  77:  * javax.swing.ListCellRenderer}.</p>
  78:  *
  79:  * <p>Lists have many properties, some of which are stored in this class
  80:  * while others are delegated to the list's model or selection. The
  81:  * following properties are available:</p>
  82:  *
  83:  * <table>
  84:  * <tr><th>Property                       </th><th>Stored in</th><th>Bound?</th></tr>
  85:  * <tr><td>accessibleContext              </td><td>list     </td><td>no    </td></tr>
  86:  * <tr><td>anchorSelectionIndex           </td><td>selection</td><td>no    </td></tr>
  87:  * <tr><td>cellRenderer                   </td><td>list     </td><td>yes   </td></tr>
  88:  * <tr><td>dragEnabled                    </td><td>list     </td><td>no    </td></tr>
  89:  * <tr><td>firstVisibleIndex              </td><td>list     </td><td>no    </td></tr>
  90:  * <tr><td>fixedCellHeight                </td><td>list     </td><td>yes   </td></tr>
  91:  * <tr><td>fixedCellWidth                 </td><td>list     </td><td>yes   </td></tr>
  92:  * <tr><td>lastVisibleIndex               </td><td>list     </td><td>no    </td></tr>
  93:  * <tr><td>layoutOrientation              </td><td>list     </td><td>yes   </td></tr>
  94:  * <tr><td>leadSelectionIndex             </td><td>selection</td><td>no    </td></tr>
  95:  * <tr><td>maxSelectionIndex              </td><td>selection</td><td>no    </td></tr>
  96:  * <tr><td>minSelectionIndex              </td><td>selection</td><td>no    </td></tr>
  97:  * <tr><td>model                          </td><td>list     </td><td>yes   </td></tr>
  98:  * <tr><td>opaque                         </td><td>list     </td><td>no    </td></tr>
  99:  * <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 &lt; 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: }