Source for javax.swing.JList

   1: /* JList.java --
   2:    Copyright (C) 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.ComponentOrientation;
  44: import java.awt.Dimension;
  45: import java.awt.Point;
  46: import java.awt.Rectangle;
  47: import java.util.Vector;
  48: 
  49: import javax.accessibility.Accessible;
  50: import javax.accessibility.AccessibleContext;
  51: import javax.swing.event.ListDataEvent;
  52: import javax.swing.event.ListDataListener;
  53: import javax.swing.event.ListSelectionEvent;
  54: import javax.swing.event.ListSelectionListener;
  55: import javax.swing.plaf.ListUI;
  56: import javax.swing.text.Position;
  57: 
  58: /**
  59:  * <p>This class is a facade over three separate objects: {@link
  60:  * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and
  61:  * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list"
  62:  * concept, with independently replacable (possibly client-provided) models
  63:  * for its contents and its current selection. In addition, each element in
  64:  * the list is rendered via a strategy class {@link
  65:  * javax.swing.ListCellRenderer}.</p>
  66:  *
  67:  * <p>Lists have many properties, some of which are stored in this class
  68:  * while others are delegated to the list's model or selection. The
  69:  * following properties are available:</p>
  70:  *
  71:  * <table>
  72:  * <tr><th>Property                       </th><th>Stored in</th><th>Bound?</th></tr>
  73:  * <tr><td>accessibleContext              </td><td>list     </td><td>no    </td></tr>
  74:  * <tr><td>anchorSelectionIndex           </td><td>selection</td><td>no    </td></tr>
  75:  * <tr><td>cellRenderer                   </td><td>list     </td><td>yes   </td></tr>
  76:  * <tr><td>dragEnabled                    </td><td>list     </td><td>no    </td></tr>
  77:  * <tr><td>firstVisibleIndex              </td><td>list     </td><td>no    </td></tr>
  78:  * <tr><td>fixedCellHeight                </td><td>list     </td><td>yes   </td></tr>
  79:  * <tr><td>fixedCellWidth                 </td><td>list     </td><td>yes   </td></tr>
  80:  * <tr><td>lastVisibleIndex               </td><td>list     </td><td>no    </td></tr>
  81:  * <tr><td>layoutOrientation              </td><td>list     </td><td>yes   </td></tr>
  82:  * <tr><td>leadSelectionIndex             </td><td>selection</td><td>no    </td></tr>
  83:  * <tr><td>maxSelectionIndex              </td><td>selection</td><td>no    </td></tr>
  84:  * <tr><td>minSelectionIndex              </td><td>selection</td><td>no    </td></tr>
  85:  * <tr><td>model                          </td><td>list     </td><td>yes   </td></tr>
  86:  * <tr><td>opaque                         </td><td>list     </td><td>no    </td></tr>
  87:  * <tr><td>preferredScrollableViewportSize</td><td>list     </td><td>no    </td></tr>
  88:  * <tr><td>prototypeCellValue             </td><td>list     </td><td>yes   </td></tr>
  89:  * <tr><td>scrollableTracksViewportHeight </td><td>list     </td><td>no    </td></tr>
  90:  * <tr><td>scrollableTracksViewportWidth  </td><td>list     </td><td>no    </td></tr>
  91:  * <tr><td>selectedIndex                  </td><td>selection</td><td>no    </td></tr>
  92:  * <tr><td>selectedIndices                </td><td>selection</td><td>no    </td></tr>
  93:  * <tr><td>selectedValue                  </td><td>model    </td><td>no    </td></tr>
  94:  * <tr><td>selectedValues                 </td><td>model    </td><td>no    </td></tr>
  95:  * <tr><td>selectionBackground            </td><td>list     </td><td>yes   </td></tr>
  96:  * <tr><td>selectionEmpty                 </td><td>selection</td><td>no    </td></tr>
  97:  * <tr><td>selectionForeground            </td><td>list     </td><td>yes   </td></tr>
  98:  * <tr><td>selectionMode                  </td><td>selection</td><td>no    </td></tr>
  99:  * <tr><td>selectionModel                 </td><td>list     </td><td>yes   </td></tr>
 100:  * <tr><td>UI                             </td><td>list     </td><td>yes   </td></tr>
 101:  * <tr><td>UIClassID                      </td><td>list     </td><td>no    </td></tr>
 102:  * <tr><td>valueIsAdjusting               </td><td>list     </td><td>no    </td></tr>
 103:  * <tr><td>visibleRowCount                </td><td>list     </td><td>no    </td></tr>
 104:  * </table> 
 105:  *
 106:  * @author Graydon Hoare (graydon@redhat.com)
 107:  */
 108: 
 109: public class JList extends JComponent implements Accessible, Scrollable
 110: {
 111:   private static final long serialVersionUID = 4406629526391098046L;
 112: 
 113:   /** 
 114:    * Constant value used in "layoutOrientation" property. This value means
 115:    * that cells are laid out in a single vertical column. This is the default. 
 116:    */
 117:   public static final int VERTICAL = 0;
 118: 
 119:   /** 
 120:    * Constant value used in "layoutOrientation" property. This value means
 121:    * that cells are laid out in multiple columns "newspaper style", filling
 122:    * vertically first, then horizontally. 
 123:    */
 124:   public static final int VERTICAL_WRAP = 1;
 125:   
 126:   /** 
 127:    * Constant value used in "layoutOrientation" property. This value means
 128:    * that cells are laid out in multiple columns "newspaper style",
 129:    * filling horizontally first, then vertically. 
 130:    */
 131:   public static final int HORIZONTAL_WRAP = 2;
 132: 
 133:   /**
 134:    * This property indicates whether "drag and drop" functions are enabled
 135:    * on the list.
 136:    */
 137:   boolean dragEnabled;
 138: 
 139:   /** This property provides a strategy for rendering cells in the list. */
 140:   ListCellRenderer cellRenderer;
 141: 
 142:   /**
 143:    * This property indicates an fixed width to assign to all cells in the
 144:    * list. If its value is <code>-1</code>, no width has been
 145:    * assigned. This value can be set explicitly, or implicitly by setting
 146:    * the {@link #prototypeCellValue} property.
 147:    */
 148:   int fixedCellWidth;
 149:   
 150:   /**
 151:    * This property indicates an fixed height to assign to all cells in the
 152:    * list. If its value is <code>-1</code>, no height has been
 153:    * assigned. This value can be set explicitly, or implicitly by setting
 154:    * the {@link #prototypeCellValue} property.
 155:    */
 156:   int fixedCellHeight;
 157: 
 158:   /** 
 159:    * This property holds the current layout orientation of the list, which
 160:    * is one of the integer constants {@link #VERTICAL}, {@link
 161:    * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}. 
 162:    */
 163:   int layoutOrientation;
 164:   
 165:   /** This property holds the data elements displayed by the list. */
 166:   ListModel model;
 167: 
 168:   /**
 169:    * <p>This property holds a reference to a "prototype" data value --
 170:    * typically a String -- which is used to calculate the {@link
 171:    * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
 172:    * {@link #cellRenderer} property to acquire a component to render the
 173:    * prototype.</p>
 174:    *
 175:    * <p>It is important that you <em>not</em> set this value to a
 176:    * component. It has to be a <em>data value</em> such as the objects you
 177:    * would find in the list's model. Setting it to a component will have
 178:    * undefined (and undesirable) affects. </p>
 179:    */
 180:   Object prototypeCellValue;
 181: 
 182:   /** 
 183:    * This property specifies a foreground color for the selected cells in
 184:    * the list. When {@link ListCellRenderer.getListCellRendererComponent}
 185:    * is called with a selected cell object, the component returned will
 186:    * have its "foreground" set to this color.
 187:    */
 188:   Color selectionBackground;
 189: 
 190:   /** 
 191:    * This property specifies a background color for the selected cells in
 192:    * the list. When {@link ListCellRenderer.getListCellRendererComponent}
 193:    * is called with a selected cell object, the component returned will
 194:    * have its "background" property set to this color.
 195:    */
 196:   Color selectionForeground;
 197: 
 198:   /** 
 199:    * This property holds a description of which data elements in the {@link
 200:    * #model} property should be considered "selected", when displaying and
 201:    * interacting with the list.
 202:    */
 203:   ListSelectionModel selectionModel;
 204: 
 205: 
 206:   /**
 207:    * This property indicates that the list's selection is currently
 208:    * "adjusting" -- perhaps due to a user actively dragging the mouse over
 209:    * multiple list elements -- and is therefore likely to change again in
 210:    * the near future. A {@link ListSelectionListener} might choose to delay
 211:    * updating its view of the list's selection until this property is
 212:    * false, meaning that the adjustment has completed.
 213:    */
 214:   boolean valueIsAdjusting;
 215: 
 216:   /** 
 217:    * This property indicates a <em>preference</em> for the number of rows
 218:    * displayed in the list, and will scale the
 219:    * {@link #preferredScrollableViewportSize} property accordingly. The actual
 220:    * number of displayed rows, when the list is placed in a real {@link
 221:    * Viewport} or other component, may be greater or less than this number.
 222:    */
 223:   int visibleRowCount;
 224: 
 225:   /**
 226:    * Fire a {@link ListSelectionEvent} to all the registered ListSelectionListeners.
 227:    */
 228:   protected void fireSelectionValueChanged(int firstIndex, int lastIndex, boolean isAdjusting) 
 229:   {
 230:     ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
 231:     ListSelectionListener listeners[] = getListSelectionListeners();
 232:     for (int i = 0; i < listeners.length; ++i)
 233:       {
 234:         listeners[i].valueChanged(evt);
 235:       }
 236:   }
 237: 
 238:   /**
 239:    * This private listener propagates {@link ListSelectionEvent} events
 240:    * from the list's "selectionModel" property to the list's {@link
 241:    * ListSelectionListener} listeners. It also listens to {@link
 242:    * ListDataEvent} events from the list's {@link #model} property. If this
 243:    * class receives either type of event, it triggers repainting of the
 244:    * list.
 245:    */
 246:   private class ListListener 
 247:     implements ListSelectionListener, ListDataListener
 248:   {
 249:     // ListDataListener events
 250:     public void contentsChanged(ListDataEvent event)
 251:     {
 252:       JList.this.revalidate();
 253:       JList.this.repaint();
 254:     }
 255:     public void intervalAdded(ListDataEvent event)
 256:     {
 257:       JList.this.revalidate();
 258:       JList.this.repaint();
 259:     }
 260:     public void intervalRemoved(ListDataEvent event)
 261:     {
 262:       JList.this.revalidate();
 263:       JList.this.repaint();
 264:     }
 265:     // ListSelectionListener events
 266:     public void valueChanged(ListSelectionEvent event)
 267:     {
 268:       JList.this.fireSelectionValueChanged(event.getFirstIndex(),
 269:                                            event.getLastIndex(),
 270:                                            event.getValueIsAdjusting());
 271:       JList.this.repaint();
 272:     }
 273:   };
 274: 
 275:   /** 
 276:    * Shared ListListener instance, subscribed to both the current {@link
 277:    * #model} and {@link #selectionModel} properties of the list.
 278:    */
 279:   ListListener listListener;
 280: 
 281: 
 282:   /**
 283:    * Creates a new JList object.
 284:    */
 285:   public JList()
 286:   {
 287:     init();
 288:   }
 289: 
 290:   /**
 291:    * Creates a new JList object.
 292:    *
 293:    * @param listData Initial data to populate the list with
 294:    */
 295:   public JList(Object[] listData)
 296:   {
 297:     init();
 298:     setListData(listData);
 299:   }
 300: 
 301:   /**
 302:    * Creates a new JList object.
 303:    *
 304:    * @param listData Initial data to populate the list with
 305:    */
 306:   public JList(Vector listData)
 307:   {
 308:     init();
 309:     setListData(listData);
 310:   }
 311: 
 312:   /**
 313:    * Creates a new JList object.
 314:    *
 315:    * @param listData Initial data to populate the list with
 316:    */
 317:   public JList(ListModel listData)
 318:   {
 319:     init();
 320:     setModel(listData);
 321:   }
 322: 
 323:   void init()
 324:   {
 325:     dragEnabled = false;
 326:     fixedCellHeight = -1;
 327:     fixedCellWidth = -1;
 328:     layoutOrientation = VERTICAL;
 329:     opaque = true;
 330:     valueIsAdjusting = false;
 331:     visibleRowCount = 8;
 332: 
 333:     cellRenderer = new DefaultListCellRenderer();
 334:     listListener = new ListListener();
 335: 
 336:     setModel(new DefaultListModel());
 337:     setSelectionModel(createSelectionModel());
 338: 
 339:     updateUI();
 340:   }
 341: 
 342:   /**
 343:    * Creates the default <code>ListSelectionModel</code>.
 344:    *
 345:    * @return the <code>ListSelectionModel</code>
 346:    */
 347:   protected ListSelectionModel createSelectionModel()
 348:   {
 349:     return new DefaultListSelectionModel();
 350:   }
 351:   
 352:   /**
 353:    * Gets the value of the {@link #fixedCellHeight} property. This property
 354:    * may be <code>-1</code> to indicate that no cell height has been
 355:    * set. This property is also set implicitly when the
 356:    * {@link #prototypeCellValue} property is set.
 357:    *
 358:    * @return The current value of the property 
 359:    * 
 360:    * @see #fixedCellHeight
 361:    * @see #setFixedCellHeight
 362:    * @see #setPrototypeCellValue
 363:    */
 364:   public int getFixedCellHeight()
 365:   {
 366:     return fixedCellHeight;
 367:   }
 368: 
 369:   /**
 370:    * Sets the value of the {@link #fixedCellHeight} property. This property
 371:    * may be <code>-1</code> to indicate that no cell height has been
 372:    * set. This property is also set implicitly when the {@link
 373:    * #prototypeCellValue} property is set, but setting it explicitly
 374:    * overrides the height computed from {@link #prototypeCellValue}.
 375:    *
 376:    * @see #getFixedCellHeight
 377:    * @see #getPrototypeCellValue
 378:    */
 379:   public void setFixedCellHeight(int h)
 380:   {
 381:     if (fixedCellHeight == h)
 382:       return;
 383: 
 384:     int old = fixedCellHeight;
 385:     fixedCellHeight = h;
 386:     firePropertyChange("fixedCellWidth", old, h);
 387:   }
 388: 
 389: 
 390:   /**
 391:    * Gets the value of the {@link #fixedCellWidth} property. This property
 392:    * may be <code>-1</code> to indicate that no cell width has been
 393:    * set. This property is also set implicitly when the {@link
 394:    * #prototypeCellValue} property is set.
 395:    *
 396:    * @return The current value of the property 
 397:    * 
 398:    * @see #setFixedCellWidth
 399:    * @see #setPrototypeCellValue
 400:    */
 401:   public int getFixedCellWidth()
 402:   {
 403:     return fixedCellWidth;
 404:   }
 405: 
 406:   /**
 407:    * Sets the value of the {@link #fixedCellWidth} property. This property
 408:    * may be <code>-1</code> to indicate that no cell width has been
 409:    * set. This property is also set implicitly when the {@link
 410:    * #prototypeCellValue} property is set, but setting it explicitly
 411:    * overrides the width computed from {@link #prototypeCellValue}.
 412:    *
 413:    * @see #getFixedCellHeight
 414:    * @see #getPrototypeCellValue
 415:    */
 416:   public void setFixedCellWidth(int w)
 417:   {
 418:     if (fixedCellWidth == w)
 419:       return;
 420:     
 421:     int old = fixedCellWidth;
 422:     fixedCellWidth = w;
 423:     firePropertyChange("fixedCellWidth", old, w);
 424:   }
 425: 
 426:   /** 
 427:    * Gets the value of the {@link #visibleRowCount} property. 
 428:    *
 429:    * @return the current value of the property.
 430:    */
 431: 
 432:   public int getVisibleRowCount()
 433:   {
 434:     return visibleRowCount;
 435:   }
 436: 
 437:   /**
 438:    * Sets the value of the {@link #visibleRowCount} property. 
 439:    *
 440:    * @param visibleRowCount The new property value
 441:    */
 442:   public void setVisibleRowCount(int vc)
 443:   {
 444:     visibleRowCount = vc;
 445:     revalidate();
 446:     repaint();
 447:   }
 448: 
 449:   /**
 450:    * Adds a {@link ListSelectionListener} to the listener list for this
 451:    * list. The listener will be called back with a {@link
 452:    * ListSelectionEvent} any time the list's {@link #selectionModel}
 453:    * property changes. The source of such events will be the JList,
 454:    * not the selection model.
 455:    *
 456:    * @param listener The new listener to add
 457:    */
 458:   public void addListSelectionListener(ListSelectionListener listener)
 459:   {
 460:     listenerList.add (ListSelectionListener.class, listener);
 461:   }
 462: 
 463:   /**
 464:    * Removes a {@link ListSelectionListener} from the listener list for
 465:    * this list. The listener will no longer be called when the list's
 466:    * {@link #selectionModel} changes.
 467:    *
 468:    * @param listener The listener to remove
 469:    */
 470:   public void removeListSelectionListener(ListSelectionListener listener)
 471:   {
 472:     listenerList.remove(ListSelectionListener.class, listener);
 473:   }
 474: 
 475:   /**
 476:    * Returns an array of all ListSelectionListeners subscribed to this
 477:    * list. 
 478:    *
 479:    * @return The current subscribed listeners
 480:    *
 481:    * @since 1.4
 482:    */
 483:   public ListSelectionListener[] getListSelectionListeners()
 484:   {
 485:     return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
 486:   }
 487: 
 488:   public int getSelectionMode()
 489:   {
 490:     return selectionModel.getSelectionMode();
 491:   }
 492:   
 493:   /**
 494:    * Sets the list's "selectionMode" property, which simply mirrors the
 495:    * same property on the list's {@link #selectionModel} property. This
 496:    * property should be one of the integer constants
 497:    * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>,
 498:    * or <code>MULTIPLE_INTERVAL_SELECTION</code> from the {@link
 499:    * ListSelectionModel} interface.
 500:    *
 501:    * @param a The new selection mode
 502:    */
 503:   public void setSelectionMode(int a)
 504:   {
 505:     selectionModel.setSelectionMode(a);
 506:   }
 507: 
 508:   /**
 509:    * Adds the interval <code>[a,a]</code> to the set of selections managed
 510:    * by this list's {@link #selectionModel} property. Depending on the
 511:    * selection mode, this may cause existing selections to become invalid,
 512:    * or may simply expand the set of selections. 
 513:    *
 514:    * @param a A number in the half-open range <code>[0, x)</code> where
 515:    * <code>x = getModel.getSize()</code>, indicating the index of an
 516:    * element in the list to select.
 517:    *
 518:    * @see #setSelectionMode
 519:    * @see #selectionModel
 520:    */
 521:   public void setSelectedIndex(int a)
 522:   {
 523:     selectionModel.setSelectionInterval(a, a);
 524:   }
 525: 
 526:   /**
 527:    * For each element <code>a[i]</code> of the provided array
 528:    * <code>a</code>, calls {@link #setSelectedIndex} on <code>a[i]</code>.
 529:    *
 530:    * @see #setSelectionMode
 531:    * @see #selectionModel
 532:    */
 533:   public void setSelectedIndices(int [] a)
 534:   {
 535:     for (int i = 0; i < a.length; ++i)
 536:       setSelectedIndex(a[i]);
 537:   }
 538: 
 539:   /**
 540:    * Returns the minimum index of an element in the list which is currently
 541:    * selected.
 542:    *
 543:    * @return A number in the half-open range <code>[0, x)</code> where
 544:    * <code>x = getModel.getSize()</code>, indicating the minimum index of
 545:    * an element in the list for which the element is selected, or
 546:    * <code>-1</code> if no elements are selected
 547:    */
 548:   public int getSelectedIndex()
 549:   {
 550:     return selectionModel.getMinSelectionIndex();
 551:   }
 552: 
 553:   /**
 554:    * Returns <code>true</code> if the model's selection is empty, otherwise
 555:    * <code>false</code>. 
 556:    *
 557:    * @return The return value of {@link ListSelectionModel#isSelectionEmpty}
 558:    */
 559:   public boolean isSelectionEmpty()
 560:   {
 561:     return selectionModel.isSelectionEmpty();
 562:   }
 563: 
 564:   /**
 565:    * Returns the list index of the upper left or upper right corner of the
 566:    * {@link #visibleRect} property, depending on the {@link
 567:    * #componentOrientation} property.
 568:    *
 569:    * @return The index of the first visible list cell, or <code>-1</code>
 570:    * if none is visible.
 571:    */
 572:   public int getFirstVisibleIndex()
 573:   {
 574:     ComponentOrientation or = getComponentOrientation();
 575:     Rectangle r = getVisibleRect();
 576:     if (or == ComponentOrientation.RIGHT_TO_LEFT)
 577:       r.translate((int) r.getWidth() - 1, 0);
 578:     return getUI().locationToIndex(this, r.getLocation());      
 579:   }
 580: 
 581: 
 582:   /**
 583:    * Returns index of the cell to which specified location is closest to
 584:    * @param location for which to look for in the list
 585:    * 
 586:    * @return index of the cell to which specified location is closest to.
 587:    */
 588:    public int locationToIndex(Point location) {
 589:      return getUI().locationToIndex(this, location);      
 590:    }
 591: 
 592:   /**
 593:    * Returns location of the cell located at the specified index in the list.
 594:    * @param index of the cell for which location will be determined
 595:    * 
 596:    * @return location of the cell located at the specified index in the list.
 597:    */
 598:    public Point indexToLocation(int index){
 599:      return getCellBounds(index, index).getLocation();
 600:    }
 601: 
 602:   /**
 603:    * Returns the list index of the lower right or lower left corner of the
 604:    * {@link #visibleRect} property, depending on the {@link
 605:    * #componentOrientation} property.
 606:    *
 607:    * @return The index of the last visible list cell, or <code>-1</code>
 608:    * if none is visible.
 609:    */
 610:   public int getLastVisibleIndex()
 611:   {
 612:     ComponentOrientation or = getComponentOrientation();
 613:     Rectangle r = getVisibleRect();
 614:     r.translate(0, (int) r.getHeight() - 1);
 615:     if (or == ComponentOrientation.LEFT_TO_RIGHT)
 616:       r.translate((int) r.getWidth() - 1, 0);
 617:     if (getUI().locationToIndex(this, r.getLocation()) == -1
 618:         && indexToLocation(getModel().getSize() - 1).y < r.y)
 619:       return getModel().getSize() - 1;
 620:     return getUI().locationToIndex(this, r.getLocation());
 621:   }
 622: 
 623:   /**
 624:    * Returns the indices of values in the {@link #model} property which are
 625:    * selected.
 626:    *
 627:    * @return An array of model indices, each of which is selected according
 628:    * to the {@link #selection} property
 629:    */
 630:   public int[] getSelectedIndices()
 631:   {
 632:     int lo, hi, n, i, j;
 633:     if (selectionModel.isSelectionEmpty())
 634:       return new int[0];
 635:     lo = selectionModel.getMinSelectionIndex();
 636:     hi = selectionModel.getMaxSelectionIndex();
 637:     n = 0;
 638:     for (i = lo; i <= hi; ++i)
 639:       if (selectionModel.isSelectedIndex(i))
 640:         n++;
 641:     int [] v = new int[n];
 642:     j = 0;
 643:     for (i = lo; i < hi; ++i)
 644:       if (selectionModel.isSelectedIndex(i))
 645:         v[j++] = i;
 646:     return v;
 647:   }
 648: 
 649:   /**
 650:    * Indicates whether the list element at a given index value is
 651:    * currently selected.
 652:    *
 653:    * @param a The index to check 
 654:    * @return <code>true</code> if <code>a</code> is the index of a selected
 655:    * list element
 656:    */
 657:   public boolean isSelectedIndex(int a)
 658:   {
 659:     return selectionModel.isSelectedIndex(a);
 660:   }
 661: 
 662:   /**
 663:    * Returns the first value in the list's {@link #model} property which is
 664:    * selected, according to the list's {@link #selectionModel} property.
 665:    * This is equivalent to calling
 666:    * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check
 667:    * for the special index value of <code>-1</code> which returns null
 668:    * <code>null</code>.
 669:    *
 670:    * @return The first selected element, or <code>null</code> if no element
 671:    * is selected.
 672:    *
 673:    * @see getSelectedValues
 674:    */
 675:   public Object getSelectedValue()
 676:   {
 677:     int index = getSelectedIndex();
 678:     if (index == -1)
 679:       return null;
 680:     return getModel().getElementAt(index);
 681:   }
 682: 
 683:   /**
 684:    * Returns all the values in the list's {@link #model} property which
 685:    * are selected, according to the list's {@link #selectionModel} property.
 686:    *
 687:    * @return An array containing all the selected values
 688:    *
 689:    * @see getSelectedValue
 690:    */
 691:   public Object[] getSelectedValues()
 692:   {
 693:     int [] idx = getSelectedIndices();
 694:     Object [] v = new Object[idx.length];
 695:     for (int i = 0; i < idx.length; ++i)
 696:       v[i] = getModel().getElementAt(i);
 697:     return v;
 698:   }
 699: 
 700:   /**
 701:    * Gets the value of the {@link #selectionBackground} property.
 702:    *
 703:    * @return The current value of the property
 704:    */
 705:   public Color getSelectionBackground()
 706:   {
 707:     return selectionBackground;
 708:   }
 709: 
 710:   /**
 711:    * Sets the value of the {@link #selectionBackground} property.
 712:    *
 713:    * @param c The new value of the property
 714:    */
 715:   public void setSelectionBackground(Color c)
 716:   {
 717:     if (selectionBackground == c)
 718:       return;
 719: 
 720:     Color old = selectionBackground;
 721:     selectionBackground = c;
 722:     firePropertyChange("selectionBackground", old, c);
 723:     repaint();
 724:   }
 725: 
 726:   /**
 727:    * Gets the value of the {@link #selectionForeground} property.
 728:    *
 729:    * @return The current value of the property
 730:    */
 731:   public Color getSelectionForeground()
 732:   {
 733:     return selectionForeground;
 734:   }
 735:   
 736:   /**
 737:    * Sets the value of the {@link #selectionForeground} property.
 738:    *
 739:    * @param c The new value of the property
 740:    */
 741:   public void setSelectionForeground(Color c)
 742:   {
 743:     if (selectionForeground == c)
 744:       return;
 745: 
 746:     Color old = selectionForeground;
 747:     selectionForeground = c;
 748:     firePropertyChange("selectionForeground", old, c);
 749:   }
 750: 
 751:   /**
 752:    * Sets the selection to cover only the specified value, if it
 753:    * exists in the model. 
 754:    *
 755:    * @param obj The object to select
 756:    * @param scroll Whether to scroll the list to make the newly selected
 757:    * value visible
 758:    *
 759:    * @see #ensureIndexIsVisible
 760:    */
 761: 
 762:   public void setSelectedValue(Object obj, boolean scroll)
 763:   {
 764:     for (int i = 0; i < model.getSize(); ++i)
 765:       {
 766:         if (model.getElementAt(i).equals(obj))
 767:           {
 768:             setSelectedIndex(i);
 769:             if (scroll)
 770:               ensureIndexIsVisible(i);
 771:             break;
 772:           }
 773:       }
 774:   }
 775: 
 776:   /**
 777:    * Scrolls this list to make the specified cell visible. This
 778:    * only works if the list is contained within a viewport.
 779:    *
 780:    * @param i The list index to make visible
 781:    *
 782:    * @see JComponent#scrollRectToVisible
 783:    */
 784:   public void ensureIndexIsVisible(int i)
 785:   {
 786:     scrollRectToVisible(getUI().getCellBounds(this, i, i));
 787:   }
 788: 
 789:   /**
 790:    * Sets the {@link #model} property of the list to a new anonymous
 791:    * {@link AbstractListModel} subclass which accesses the provided Object
 792:    * array directly.
 793:    *
 794:    * @param listData The object array to build a new list model on
 795:    * @see #setModel
 796:    */
 797:   public void setListData(final Object[] listData)
 798:   {
 799:     setModel(new AbstractListModel()
 800:         {
 801:           public int getSize()
 802:           {
 803:             return listData.length;
 804:           }
 805: 
 806:           public Object getElementAt(int i)
 807:           {
 808:             return listData[i];
 809:           }
 810:         });
 811:   }
 812: 
 813:   /**
 814:    * Sets the {@link #model} property of the list to a new anonymous {@link
 815:    * AbstractListModel} subclass which accesses the provided vector
 816:    * directly.
 817:    *
 818:    * @param listData The object array to build a new list model on
 819:    * @see #setModel
 820:    */
 821:   public void setListData(final Vector listData)
 822:   {
 823:     setModel(new AbstractListModel()
 824:         {
 825:           public int getSize()
 826:           {
 827:             return listData.size();
 828:           }
 829: 
 830:           public Object getElementAt(int i)
 831:           {
 832:             return listData.elementAt(i);
 833:           }
 834:         });
 835:   }
 836: 
 837:   /**
 838:    * Gets the value of the {@link #cellRenderer} property. 
 839:    *
 840:    * @return The current value of the property
 841:    */
 842:   public ListCellRenderer getCellRenderer()
 843:   {
 844:     return cellRenderer;
 845:   }
 846: 
 847:   /**
 848:    * Sets the value of the {@link #celLRenderer} property.
 849:    *
 850:    * @param renderer The new property value
 851:    */
 852:   public void setCellRenderer(ListCellRenderer renderer)
 853:   {
 854:     if (cellRenderer == renderer)
 855:       return;
 856:     
 857:     ListCellRenderer old = cellRenderer;
 858:     cellRenderer = renderer;
 859:     firePropertyChange("cellRenderer", old, renderer);
 860:     revalidate();
 861:     repaint();
 862:   }
 863: 
 864:   /**
 865:    * Gets the value of the {@link #model} property. 
 866:    *
 867:    * @return The current value of the property
 868:    */
 869:   public ListModel getModel()
 870:   {
 871:     return model;
 872:   }
 873: 
 874:   /**
 875:    * Sets the value of the {@link #model} property. The list's {@link
 876:    * #listListener} is unsubscribed from the existing model, if it exists,
 877:    * and re-subscribed to the new model.
 878:    *
 879:    * @param model The new property value
 880:    */
 881:   public void setModel(ListModel model)
 882:   {
 883:     if (this.model == model)
 884:       return;
 885:     
 886:     if (this.model != null)
 887:       this.model.removeListDataListener(listListener);
 888:     
 889:     ListModel old = this.model;
 890:     this.model = model;
 891:     
 892:     if (this.model != null)
 893:       this.model.addListDataListener(listListener);
 894:     
 895:     firePropertyChange("model", old, model);
 896:     revalidate();
 897:     repaint();
 898:   }
 899: 
 900: 
 901:   public ListSelectionModel getSelectionModel()
 902:   {
 903:     return selectionModel;
 904:   }
 905: 
 906:   /**
 907:    * Sets the value of the {@link #selectionModel} property. The list's
 908:    * {@link #listListener} is unsubscribed from the existing selection
 909:    * model, if it exists, and re-subscribed to the new selection model.
 910:    *
 911:    * @param model The new property value
 912:    */
 913:   public void setSelectionModel(ListSelectionModel model)
 914:   {
 915:     if (selectionModel == model)
 916:       return;
 917:     
 918:     if (selectionModel != null)
 919:       selectionModel.removeListSelectionListener(listListener);
 920:     
 921:     ListSelectionModel old = selectionModel;
 922:     selectionModel = model;
 923:     
 924:     if (selectionModel != null)
 925:       selectionModel.addListSelectionListener(listListener);
 926:     
 927:     firePropertyChange("selectionModel", old, model);
 928:     revalidate();
 929:     repaint();
 930:   }
 931: 
 932:   /**
 933:    * Gets the value of the UI property.
 934:    *
 935:    * @return The current property value
 936:    */
 937:   public ListUI getUI()
 938:   {
 939:     return (ListUI) ui;
 940:   }
 941: 
 942:   /**
 943:    * Sets the value of the UI property.
 944:    *
 945:    * @param ui The new property value
 946:    */
 947:   public void setUI(ListUI ui)
 948:   {
 949:     super.setUI(ui);
 950:   }
 951: 
 952:   /**
 953:    * Calls {@link #setUI} with the {@link ListUI} subclass
 954:    * returned from calling {@link UIManager#getUI}.
 955:    */
 956:   public void updateUI()
 957:   {
 958:     setUI((ListUI) UIManager.getUI(this));
 959:   }
 960: 
 961:   /**
 962:    * Return the class identifier for the list's UI property.  This should
 963:    * be the constant string <code>"ListUI"</code>, and map to an
 964:    * appropriate UI class in the {@link UIManager}.
 965:    *
 966:    * @return The class identifier
 967:    */
 968:   public String getUIClassID()
 969:   {
 970:     return "ListUI";
 971:   }
 972: 
 973: 
 974:   /**
 975:    * Returns the current value of the {@link #prototypeCellValue}
 976:    * property. This property holds a reference to a "prototype" data value
 977:    * -- typically a String -- which is used to calculate the {@link
 978:    * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
 979:    * {@link #cellRenderer} property to acquire a component to render the
 980:    * prototype.
 981:    *
 982:    * @return The current prototype cell value
 983:    * @see #setPrototypeCellValue
 984:    */
 985:   public Object getPrototypeCellValue()
 986:   {
 987:     return prototypeCellValue;
 988:   }
 989: 
 990:   /**
 991:    * <p>Set the {@link #prototypeCellValue} property. This property holds a
 992:    * reference to a "prototype" data value -- typically a String -- which
 993:    * is used to calculate the {@link #fixedCellWidth} and {@link
 994:    * #fixedCellHeight} properties, using the {@link #cellRenderer} property
 995:    * to acquire a component to render the prototype.</p>
 996:    *
 997:    * <p>It is important that you <em>not</em> set this value to a
 998:    * component. It has to be a <em>data value</em> such as the objects you
 999:    * would find in the list's model. Setting it to a component will have
1000:    * undefined (and undesirable) affects. </p>
1001:    *
1002:    * @param obj The new prototype cell value
1003:    * @see #getPrototypeCellValue
1004:    */
1005:   public void setPrototypeCellValue(Object obj)
1006:   {
1007:     if (prototypeCellValue == obj)
1008:       return;
1009: 
1010:     Object old = prototypeCellValue;
1011:     Component comp = getCellRenderer()
1012:       .getListCellRendererComponent(this, obj, 0, false, false); 
1013:     Dimension d = comp.getPreferredSize();
1014:     fixedCellWidth = d.width;
1015:     fixedCellHeight = d.height;
1016:     prototypeCellValue = obj;
1017:     firePropertyChange("prototypeCellValue", old, obj);
1018:   }
1019: 
1020:   public AccessibleContext getAccessibleContext()
1021:   {
1022:     return null;
1023:   }
1024: 
1025:   /**
1026:    * Returns a size indicating how much space this list would like to
1027:    * consume, when contained in a scrollable viewport. This is part of the
1028:    * {@link Scrollable} interface, which interacts with {@link
1029:    * ScrollPaneLayout} and {@link Viewport} to define scrollable objects.
1030:    *
1031:    * @return The preferred size
1032:    */
1033:   public Dimension getPreferredScrollableViewportSize()
1034:   {
1035:     //If the layout orientation is not VERTICAL, then this will 
1036:     //return the value from getPreferredSize. The current ListUI is 
1037:     //expected to override getPreferredSize to return an appropriate value.
1038:     if (getLayoutOrientation() != VERTICAL)
1039:       return getPreferredSize();
1040:     
1041:     if (fixedCellHeight != -1 && fixedCellWidth != -1)
1042:       return new Dimension(fixedCellWidth, getModel().getSize() * 
1043:                            fixedCellHeight);
1044: 
1045:     int prefWidth, prefHeight;
1046:     if (fixedCellWidth != -1)
1047:       prefWidth = fixedCellWidth;
1048:     else
1049:       {
1050:         prefWidth = 0;
1051:         int size = getModel().getSize();
1052:         for (int i = 0; i < size; i++)
1053:           if (getCellBounds(i, i).width > prefWidth)
1054:             prefWidth = getCellBounds(i, i).width;
1055:       }
1056:     
1057:     if (getModel().getSize() == 0 && fixedCellWidth == -1)
1058:       return new Dimension(256, 16 * getVisibleRowCount());
1059:     else if (getModel().getSize() == 0)
1060:       return new Dimension (fixedCellWidth, 16 * getVisibleRowCount());
1061:     
1062:     if (fixedCellHeight != -1)
1063:       prefHeight = fixedCellHeight;
1064:     else
1065:       {
1066:         prefHeight = getVisibleRowCount() * getCellBounds
1067:           (getFirstVisibleIndex(), getFirstVisibleIndex()).height;
1068:       }
1069:     return new Dimension (prefWidth, prefHeight);
1070:   }
1071: 
1072:   /**
1073:    * <p>Return the number of pixels the list must scroll in order to move a
1074:    * "unit" of the list into the provided visible rectangle. When the
1075:    * provided direction is positive, the call describes a "downwards"
1076:    * scroll, which will be exposing a cell at a <em>greater</em> index in
1077:    * the list than those elements currently showing. Then the provided
1078:    * direction is negative, the call describes an "upwards" scroll, which
1079:    * will be exposing a cell at a <em>lesser</em> index in the list than
1080:    * those elements currently showing.</p>
1081:    *
1082:    * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
1083:    * comments refer to "rightwards" for positive direction, and "leftwards"
1084:    * for negative.</p>
1085:    * 
1086:    *
1087:    * @param visibleRect The rectangle to scroll an element into
1088:    * @param orientation One of the numeric consants <code>VERTICAL</code>
1089:    * or <code>HORIZONTAL</code>
1090:    * @param direction An integer indicating the scroll direction: positive means
1091:    * forwards (down, right), negative means backwards (up, left)
1092:    *
1093:    * @return The scrollable unit increment, in pixels
1094:    */
1095:   public int getScrollableUnitIncrement(Rectangle visibleRect,
1096:                                         int orientation, int direction)
1097:   {
1098:     ListUI lui = this.getUI();
1099:     if (orientation == SwingConstants.VERTICAL)
1100:       {
1101:         if (direction > 0)
1102:           {
1103:             // Scrolling down
1104:             Point bottomLeft = new Point(visibleRect.x,
1105:                                          visibleRect.y + visibleRect.height);
1106:             int curIdx = lui.locationToIndex(this, bottomLeft);
1107:             Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
1108:             if (curBounds.y + curBounds.height == bottomLeft.y)
1109:               {
1110:                 // we are at the exact bottom of the current cell, so we 
1111:                 // are being asked to scroll to the end of the next one
1112:                 if (curIdx + 1 < model.getSize())
1113:                   {
1114:                     // there *is* a next item in the list
1115:                     Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1);
1116:                     return nxtBounds.height;
1117:                   }
1118:                 else
1119:                   {
1120:                     // no next item, no advance possible
1121:                     return 0;
1122:                   }
1123:               }
1124:             else
1125:               {
1126:                 // we are part way through an existing cell, so we are being
1127:                 // asked to scroll to the bottom of it
1128:                 return (curBounds.y + curBounds.height) - bottomLeft.y;
1129:               }              
1130:           }
1131:         else
1132:           {
1133:             // scrolling up
1134:             Point topLeft = new Point(visibleRect.x, visibleRect.y);
1135:             int curIdx = lui.locationToIndex(this, topLeft);
1136:             Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
1137:             if (curBounds.y == topLeft.y)
1138:               {
1139:                 // we are at the exact top of the current cell, so we 
1140:                 // are being asked to scroll to the top of the previous one
1141:                 if (curIdx > 0)
1142:                   {
1143:                     // there *is* a previous item in the list
1144:                     Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1);
1145:                     return -nxtBounds.height;
1146:                   }
1147:                 else
1148:                   {
1149:                     // no previous item, no advance possible
1150:                     return 0;
1151:                   }
1152:               }
1153:             else
1154:               {
1155:                 // we are part way through an existing cell, so we are being
1156:                 // asked to scroll to the top of it
1157:                 return curBounds.y - topLeft.y;
1158:               }              
1159:           }
1160:       }
1161: 
1162:     // FIXME: handle horizontal scrolling (also wrapping?)
1163:     return 1;
1164:   }
1165: 
1166:   /**
1167:    * <p>Return the number of pixels the list must scroll in order to move a
1168:    * "block" of the list into the provided visible rectangle. When the
1169:    * provided direction is positive, the call describes a "downwards"
1170:    * scroll, which will be exposing a cell at a <em>greater</em> index in
1171:    * the list than those elements currently showing. Then the provided
1172:    * direction is negative, the call describes an "upwards" scroll, which
1173:    * will be exposing a cell at a <em>lesser</em> index in the list than
1174:    * those elements currently showing.</p>
1175:    *
1176:    * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
1177:    * comments refer to "rightwards" for positive direction, and "leftwards"
1178:    * for negative.</p>
1179:    * 
1180:    *
1181:    * @param visibleRect The rectangle to scroll an element into
1182:    * @param orientation One of the numeric consants <code>VERTICAL</code>
1183:    * or <code>HORIZONTAL</code>
1184:    * @param direction An integer indicating the scroll direction: positive means
1185:    * forwards (down, right), negative means backwards (up, left)
1186:    *
1187:    * @return The scrollable unit increment, in pixels
1188:    */
1189:   public int getScrollableBlockIncrement(Rectangle visibleRect,
1190:                                          int orientation, int direction)
1191:   {
1192:       if (orientation == VERTICAL)
1193:       return visibleRect.height * direction;
1194:       else
1195:       return visibleRect.width * direction;
1196:   }
1197: 
1198:   /**
1199:    * Gets the value of the {@link #scrollableTracksViewportWidth} property.
1200:    *
1201:    * @return <code>true</code> if the viewport is larger (horizontally)
1202:    * than the list and the list should be expanded to fit the viewport;
1203:    * <code>false</code> if the viewport is smaller than the list and the
1204:    * list should scroll (horizontally) within the viewport
1205:    */
1206:   public boolean getScrollableTracksViewportWidth()
1207:   {
1208:     Component parent = getParent();
1209:     boolean retVal = false;
1210:     if (parent instanceof JViewport)
1211:       {
1212:         JViewport viewport = (JViewport) parent;
1213:         Dimension pref = getPreferredSize();
1214:         if (viewport.getSize().width > pref.width)
1215:           retVal = true;
1216:         if ((getLayoutOrientation() == HORIZONTAL_WRAP)
1217:             && (getVisibleRowCount() <= 0))
1218:           retVal = true;
1219:       }
1220:     return retVal;
1221:   }
1222: 
1223:   /**
1224:    * Gets the value of the {@link #scrollableTracksViewportWidth} property.
1225:    *
1226:    * @return <code>true</code> if the viewport is larger (vertically)
1227:    * than the list and the list should be expanded to fit the viewport;
1228:    * <code>false</code> if the viewport is smaller than the list and the
1229:    * list should scroll (vertically) within the viewport
1230:    */
1231:   public boolean getScrollableTracksViewportHeight()
1232:   {
1233:     Component parent = getParent();
1234:     boolean retVal = false;
1235:     if (parent instanceof JViewport)
1236:       {
1237:         JViewport viewport = (JViewport) parent;
1238:         Dimension pref = getPreferredSize();
1239:         if (viewport.getSize().height > pref.height)
1240:           retVal = true;
1241:         if ((getLayoutOrientation() == VERTICAL_WRAP)
1242:             && (getVisibleRowCount() <= 0))
1243:           retVal = true;
1244:       }
1245:     return retVal;
1246:   }
1247: 
1248:   public int getAnchorSelectionIndex()
1249:   {
1250:     return selectionModel.getAnchorSelectionIndex();
1251:   }
1252: 
1253:   public int getLeadSelectionIndex()
1254:   {
1255:     return selectionModel.getLeadSelectionIndex();
1256:   }
1257: 
1258:   public int getMinSelectionIndex()
1259:   {
1260:     return selectionModel.getMaxSelectionIndex();
1261:   }
1262: 
1263:   public int getMaxSelectionIndex()
1264:   {
1265:     return selectionModel.getMaxSelectionIndex();
1266:   }
1267: 
1268:   public void clearSelection()
1269:   {
1270:     selectionModel.clearSelection();
1271:   }
1272: 
1273:   public void setSelectionInterval(int anchor, int lead)
1274:   {
1275:     selectionModel.setSelectionInterval(anchor, lead);
1276:   }
1277: 
1278:   public void addSelectionInterval(int anchor, int lead)
1279:   {
1280:     selectionModel.addSelectionInterval(anchor, lead);
1281:   }
1282: 
1283:   public void removeSelectionInterval(int index0, int index1)
1284:   {
1285:     selectionModel.removeSelectionInterval(index0, index1);
1286:   }
1287: 
1288:   /**
1289:    * Returns the value of the <code>valueIsAdjusting</code> property.
1290:    *
1291:    * @return the value
1292:    */
1293:   public boolean getValueIsAdjusting()
1294:   {
1295:     return valueIsAdjusting;
1296:   }
1297: 
1298:   /**
1299:    * Sets the <code>valueIsAdjusting</code> property.
1300:    *
1301:    * @param isAdjusting the new value
1302:    */
1303:   public void setValueIsAdjusting(boolean isAdjusting)
1304:   {
1305:     valueIsAdjusting = isAdjusting;
1306:   }
1307: 
1308:   /**
1309:    * Return the value of the <code>dragEnabled</code> property.
1310:    *
1311:    * @return the value
1312:    * 
1313:    * @since 1.4
1314:    */
1315:   public boolean getDragEnabled()
1316:   {
1317:     return dragEnabled;
1318:   }
1319: 
1320:   /**
1321:    * Set the <code>dragEnabled</code> property.
1322:    *
1323:    * @param enabled new value
1324:    * 
1325:    * @since 1.4
1326:    */
1327:   public void setDragEnabled(boolean enabled)
1328:   {
1329:     dragEnabled = enabled;
1330:   }
1331: 
1332:   /**
1333:    * Returns the layout orientation.
1334:    *
1335:    * @return the orientation, one of <code>JList.VERTICAL</code>,
1336:    * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code>
1337:    *
1338:    * @since 1.4
1339:    */
1340:   public int getLayoutOrientation()
1341:   {
1342:     return layoutOrientation;
1343:   }
1344: 
1345:   /**
1346:    * Sets the layout orientation.
1347:    *
1348:    * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>,
1349:    * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code>
1350:    *
1351:    * @since 1.4
1352:    */
1353:   public void setLayoutOrientation(int orientation)
1354:   {
1355:     if (layoutOrientation == orientation)
1356:       return;
1357: 
1358:     int old = layoutOrientation;
1359:     layoutOrientation = orientation;
1360:     firePropertyChange("layoutOrientation", old, orientation);
1361:   }
1362: 
1363:   /**
1364:    * Returns the bounds of the rectangle that encloses both list cells
1365:    * with index0 and index1.
1366:    *
1367:    * @param index0 the index of the first cell
1368:    * @param index1 the index of the second cell
1369:    *
1370:    * @return  the bounds of the rectangle that encloses both list cells
1371:    *     with index0 and index1, <code>null</code> if one of the indices is
1372:    *     not valid
1373:    */
1374:   public Rectangle getCellBounds(int index0, int index1)
1375:   {
1376:     return ((ListUI) ui).getCellBounds(this, index0, index1);
1377:   }
1378: 
1379:   /**
1380:    * Returns the next list element (beginning from <code>startIndex</code>
1381:    * that starts with <code>prefix</code>. Searching is done in the direction
1382:    * specified by <code>bias</code>.
1383:    *
1384:    * @param prefix the prefix to search for in the cell values
1385:    * @param startIndex the index where to start searching from
1386:    * @param bias the search direction, either {@link Position.Bias.Forward}
1387:    *     or {@link Position.Bias.Backward}
1388:    *
1389:    * @return the index of the found element or -1 if no such element has
1390:    *     been found
1391:    *
1392:    * @throws IllegalArgumentException if prefix is <code>null</code> or
1393:    *     startIndex is not valid
1394:    *
1395:    * @since 1.4
1396:    */
1397:   public int getNextMatch(String prefix, int startIndex, Position.Bias bias)
1398:   {
1399:     if (prefix == null)
1400:       throw new IllegalArgumentException("The argument 'prefix' must not be"
1401:                                          + " null.");
1402:     if (startIndex < 0)
1403:       throw new IllegalArgumentException("The argument 'startIndex' must not"
1404:                                          + " be less than zero.");
1405: 
1406:     int size = model.getSize();
1407:     if (startIndex > model.getSize())
1408:       throw new IllegalArgumentException("The argument 'startIndex' must not"
1409:                                          + " be greater than the number of"
1410:                                          + " elements in the ListModel.");
1411: 
1412:     int index = -1;
1413:     if (bias == Position.Bias.Forward)
1414:       {
1415:         for (int i = startIndex; i < size; i++)
1416:           {
1417:             String item = model.getElementAt(i).toString();
1418:             if (item.startsWith(prefix))
1419:               {
1420:                 index = i;
1421:                 break;
1422:               }
1423:           }
1424:       }
1425:     else
1426:       {
1427:         for (int i = startIndex; i >= 0; i--)
1428:           {
1429:             String item = model.getElementAt(i).toString();
1430:             if (item.startsWith(prefix))
1431:               {
1432:                 index = i;
1433:                 break;
1434:               }
1435:           }
1436:       }
1437:     return index;
1438:   }
1439: }