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(), 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:        //FIXME: Need to implement.
 600:     return null;
 601:    }
 602: 
 603:   /**
 604:    * Returns the list index of the lower right or lower left corner of the
 605:    * {@link #visibleRect} property, depending on the {@link
 606:    * #componentOrientation} property.
 607:    *
 608:    * @return The index of the first visible list cell, or <code>-1</code>
 609:    * if none is visible.
 610:    */
 611:   public int getLastVisibleIndex()
 612:   {
 613:     ComponentOrientation or = getComponentOrientation();
 614:     Rectangle r = getVisibleRect();
 615:     r.translate(0, (int) r.getHeight());
 616:     if (or == ComponentOrientation.LEFT_TO_RIGHT)
 617:       r.translate((int) r.getWidth(), 0);
 618:     return getUI().locationToIndex(this, r.getLocation());      
 619:   }
 620: 
 621:   /**
 622:    * Returns the indices of values in the {@link #model} property which are
 623:    * selected.
 624:    *
 625:    * @return An array of model indices, each of which is selected according
 626:    * to the {@link #selection} property
 627:    */
 628:   public int[] getSelectedIndices()
 629:   {
 630:     int lo, hi, n, i, j;
 631:     if (selectionModel.isSelectionEmpty())
 632:       return new int[0];
 633:     lo = selectionModel.getMinSelectionIndex();
 634:     hi = selectionModel.getMaxSelectionIndex();
 635:     n = 0;
 636:     for (i = lo; i <= hi; ++i)
 637:       if (selectionModel.isSelectedIndex(i))
 638:         n++;
 639:     int [] v = new int[n];
 640:     j = 0;
 641:     for (i = lo; i < hi; ++i)
 642:       if (selectionModel.isSelectedIndex(i))
 643:         v[j++] = i;
 644:     return v;
 645:   }
 646: 
 647:   /**
 648:    * Indicates whether the list element at a given index value is
 649:    * currently selected.
 650:    *
 651:    * @param a The index to check 
 652:    * @return <code>true</code> if <code>a</code> is the index of a selected
 653:    * list element
 654:    */
 655:   public boolean isSelectedIndex(int a)
 656:   {
 657:     return selectionModel.isSelectedIndex(a);
 658:   }
 659: 
 660:   /**
 661:    * Returns the first value in the list's {@link #model} property which is
 662:    * selected, according to the list's {@link #selectionModel} property.
 663:    * This is equivalent to calling
 664:    * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check
 665:    * for the special index value of <code>-1</code> which returns null
 666:    * <code>null</code>.
 667:    *
 668:    * @return The first selected element, or <code>null</code> if no element
 669:    * is selected.
 670:    *
 671:    * @see getSelectedValues
 672:    */
 673:   public Object getSelectedValue()
 674:   {
 675:     int index = getSelectedIndex();
 676:     if (index == -1)
 677:       return null;
 678:     return getModel().getElementAt(index);
 679:   }
 680: 
 681:   /**
 682:    * Returns all the values in the list's {@link #model} property which
 683:    * are selected, according to the list's {@link #selectionModel} property.
 684:    *
 685:    * @return An array containing all the selected values
 686:    *
 687:    * @see getSelectedValue
 688:    */
 689:   public Object[] getSelectedValues()
 690:   {
 691:     int [] idx = getSelectedIndices();
 692:     Object [] v = new Object[idx.length];
 693:     for (int i = 0; i < idx.length; ++i)
 694:       v[i] = getModel().getElementAt(i);
 695:     return v;
 696:   }
 697: 
 698:   /**
 699:    * Gets the value of the {@link #selectionBackground} property.
 700:    *
 701:    * @return The current value of the property
 702:    */
 703:   public Color getSelectionBackground()
 704:   {
 705:     return selectionBackground;
 706:   }
 707: 
 708:   /**
 709:    * Sets the value of the {@link #selectionBackground} property.
 710:    *
 711:    * @param c The new value of the property
 712:    */
 713:   public void setSelectionBackground(Color c)
 714:   {
 715:     if (selectionBackground == c)
 716:       return;
 717: 
 718:     Color old = selectionBackground;
 719:     selectionBackground = c;
 720:     firePropertyChange("selectionBackground", old, c);
 721:     repaint();
 722:   }
 723: 
 724:   /**
 725:    * Gets the value of the {@link #selectionForeground} property.
 726:    *
 727:    * @return The current value of the property
 728:    */
 729:   public Color getSelectionForeground()
 730:   {
 731:     return selectionForeground;
 732:   }
 733:   
 734:   /**
 735:    * Sets the value of the {@link #selectionForeground} property.
 736:    *
 737:    * @param c The new value of the property
 738:    */
 739:   public void setSelectionForeground(Color c)
 740:   {
 741:     if (selectionForeground == c)
 742:       return;
 743: 
 744:     Color old = selectionForeground;
 745:     selectionForeground = c;
 746:     firePropertyChange("selectionForeground", old, c);
 747:   }
 748: 
 749:   /**
 750:    * Sets the selection to cover only the specified value, if it
 751:    * exists in the model. 
 752:    *
 753:    * @param obj The object to select
 754:    * @param scroll Whether to scroll the list to make the newly selected
 755:    * value visible
 756:    *
 757:    * @see #ensureIndexIsVisible
 758:    */
 759: 
 760:   public void setSelectedValue(Object obj, boolean scroll)
 761:   {
 762:     for (int i = 0; i < model.getSize(); ++i)
 763:       {
 764:         if (model.getElementAt(i).equals(obj))
 765:           {
 766:             setSelectedIndex(i);
 767:             if (scroll)
 768:               ensureIndexIsVisible(i);
 769:             break;
 770:           }
 771:       }
 772:   }
 773: 
 774:   /**
 775:    * Scrolls this list to make the specified cell visible. This
 776:    * only works if the list is contained within a viewport.
 777:    *
 778:    * @param i The list index to make visible
 779:    *
 780:    * @see JComponent#scrollRectToVisible
 781:    */
 782:   public void ensureIndexIsVisible(int i)
 783:   {
 784:     scrollRectToVisible(getUI().getCellBounds(this, i, i));
 785:   }
 786: 
 787:   /**
 788:    * Sets the {@link #model} property of the list to a new anonymous
 789:    * {@link AbstractListModel} subclass which accesses the provided Object
 790:    * array directly.
 791:    *
 792:    * @param listData The object array to build a new list model on
 793:    * @see #setModel
 794:    */
 795:   public void setListData(final Object[] listData)
 796:   {
 797:     setModel(new AbstractListModel()
 798:         {
 799:           public int getSize()
 800:           {
 801:             return listData.length;
 802:           }
 803: 
 804:           public Object getElementAt(int i)
 805:           {
 806:             return listData[i];
 807:           }
 808:         });
 809:   }
 810: 
 811:   /**
 812:    * Sets the {@link #model} property of the list to a new anonymous {@link
 813:    * AbstractListModel} subclass which accesses the provided vector
 814:    * directly.
 815:    *
 816:    * @param listData The object array to build a new list model on
 817:    * @see #setModel
 818:    */
 819:   public void setListData(final Vector listData)
 820:   {
 821:     setModel(new AbstractListModel()
 822:         {
 823:           public int getSize()
 824:           {
 825:             return listData.size();
 826:           }
 827: 
 828:           public Object getElementAt(int i)
 829:           {
 830:             return listData.elementAt(i);
 831:           }
 832:         });
 833:   }
 834: 
 835:   /**
 836:    * Gets the value of the {@link #cellRenderer} property. 
 837:    *
 838:    * @return The current value of the property
 839:    */
 840:   public ListCellRenderer getCellRenderer()
 841:   {
 842:     return cellRenderer;
 843:   }
 844: 
 845:   /**
 846:    * Sets the value of the {@link #celLRenderer} property.
 847:    *
 848:    * @param renderer The new property value
 849:    */
 850:   public void setCellRenderer(ListCellRenderer renderer)
 851:   {
 852:     if (cellRenderer == renderer)
 853:       return;
 854:     
 855:     ListCellRenderer old = cellRenderer;
 856:     cellRenderer = renderer;
 857:     firePropertyChange("cellRenderer", old, renderer);
 858:     revalidate();
 859:     repaint();
 860:   }
 861: 
 862:   /**
 863:    * Gets the value of the {@link #model} property. 
 864:    *
 865:    * @return The current value of the property
 866:    */
 867:   public ListModel getModel()
 868:   {
 869:     return model;
 870:   }
 871: 
 872:   /**
 873:    * Sets the value of the {@link #model} property. The list's {@link
 874:    * #listListener} is unsubscribed from the existing model, if it exists,
 875:    * and re-subscribed to the new model.
 876:    *
 877:    * @param model The new property value
 878:    */
 879:   public void setModel(ListModel model)
 880:   {
 881:     if (this.model == model)
 882:       return;
 883:     
 884:     if (this.model != null)
 885:       this.model.removeListDataListener(listListener);
 886:     
 887:     ListModel old = this.model;
 888:     this.model = model;
 889:     
 890:     if (this.model != null)
 891:       this.model.addListDataListener(listListener);
 892:     
 893:     firePropertyChange("model", old, model);
 894:     revalidate();
 895:     repaint();
 896:   }
 897: 
 898: 
 899:   public ListSelectionModel getSelectionModel()
 900:   {
 901:     return selectionModel;
 902:   }
 903: 
 904:   /**
 905:    * Sets the value of the {@link #selectionModel} property. The list's
 906:    * {@link #listListener} is unsubscribed from the existing selection
 907:    * model, if it exists, and re-subscribed to the new selection model.
 908:    *
 909:    * @param model The new property value
 910:    */
 911:   public void setSelectionModel(ListSelectionModel model)
 912:   {
 913:     if (selectionModel == model)
 914:       return;
 915:     
 916:     if (selectionModel != null)
 917:       selectionModel.removeListSelectionListener(listListener);
 918:     
 919:     ListSelectionModel old = selectionModel;
 920:     selectionModel = model;
 921:     
 922:     if (selectionModel != null)
 923:       selectionModel.addListSelectionListener(listListener);
 924:     
 925:     firePropertyChange("selectionModel", old, model);
 926:     revalidate();
 927:     repaint();
 928:   }
 929: 
 930:   /**
 931:    * Gets the value of the UI property.
 932:    *
 933:    * @return The current property value
 934:    */
 935:   public ListUI getUI()
 936:   {
 937:     return (ListUI) ui;
 938:   }
 939: 
 940:   /**
 941:    * Sets the value of the UI property.
 942:    *
 943:    * @param ui The new property value
 944:    */
 945:   public void setUI(ListUI ui)
 946:   {
 947:     super.setUI(ui);
 948:   }
 949: 
 950:   /**
 951:    * Calls {@link #setUI} with the {@link ListUI} subclass
 952:    * returned from calling {@link UIManager#getUI}.
 953:    */
 954:   public void updateUI()
 955:   {
 956:     setUI((ListUI) UIManager.getUI(this));
 957:   }
 958: 
 959:   /**
 960:    * Return the class identifier for the list's UI property.  This should
 961:    * be the constant string <code>"ListUI"</code>, and map to an
 962:    * appropriate UI class in the {@link UIManager}.
 963:    *
 964:    * @return The class identifier
 965:    */
 966:   public String getUIClassID()
 967:   {
 968:     return "ListUI";
 969:   }
 970: 
 971: 
 972:   /**
 973:    * Returns the current value of the {@link #prototypeCellValue}
 974:    * property. This property holds a reference to a "prototype" data value
 975:    * -- typically a String -- which is used to calculate the {@link
 976:    * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
 977:    * {@link #cellRenderer} property to acquire a component to render the
 978:    * prototype.
 979:    *
 980:    * @return The current prototype cell value
 981:    * @see #setPrototypeCellValue
 982:    */
 983:   public Object getPrototypeCellValue()
 984:   {
 985:     return prototypeCellValue;
 986:   }
 987: 
 988:   /**
 989:    * <p>Set the {@link #prototypeCellValue} property. This property holds a
 990:    * reference to a "prototype" data value -- typically a String -- which
 991:    * is used to calculate the {@link #fixedCellWidth} and {@link
 992:    * #fixedCellHeight} properties, using the {@link #cellRenderer} property
 993:    * to acquire a component to render the prototype.</p>
 994:    *
 995:    * <p>It is important that you <em>not</em> set this value to a
 996:    * component. It has to be a <em>data value</em> such as the objects you
 997:    * would find in the list's model. Setting it to a component will have
 998:    * undefined (and undesirable) affects. </p>
 999:    *
1000:    * @param obj The new prototype cell value
1001:    * @see #getPrototypeCellValue
1002:    */
1003:   public void setPrototypeCellValue(Object obj)
1004:   {
1005:     if (prototypeCellValue == obj)
1006:       return;
1007: 
1008:     Object old = prototypeCellValue;
1009:     Component comp = getCellRenderer()
1010:       .getListCellRendererComponent(this, obj, 0, false, false); 
1011:     Dimension d = comp.getPreferredSize();
1012:     fixedCellWidth = d.width;
1013:     fixedCellHeight = d.height;
1014:     prototypeCellValue = obj;
1015:     firePropertyChange("prototypeCellValue", old, obj);
1016:   }
1017: 
1018:   public AccessibleContext getAccessibleContext()
1019:   {
1020:     return null;
1021:   }
1022: 
1023:   /**
1024:    * Returns a size indicating how much space this list would like to
1025:    * consume, when contained in a scrollable viewport. This is part of the
1026:    * {@link Scrollable} interface, which interacts with {@link
1027:    * ScrollPaneLayout} and {@link Viewport} to define scrollable objects.
1028:    *
1029:    * @return The preferred size
1030:    */
1031:   public Dimension getPreferredScrollableViewportSize()
1032:   {
1033: 
1034:     Dimension retVal = getPreferredSize();
1035:     if (getLayoutOrientation() == VERTICAL)
1036:       {
1037:         if (fixedCellHeight != -1)
1038:           {
1039:             if (fixedCellWidth != -1)
1040:               {
1041:                 int size = getModel().getSize();
1042:                 retVal = new Dimension(fixedCellWidth, size * fixedCellHeight);
1043:               } // TODO: add else clause (preferredSize is ok for now)
1044:           } // TODO: add else clause (preferredSize is ok for now)
1045:       }
1046:     return retVal;
1047:   }
1048: 
1049:   /**
1050:    * <p>Return the number of pixels the list must scroll in order to move a
1051:    * "unit" of the list into the provided visible rectangle. When the
1052:    * provided direction is positive, the call describes a "downwards"
1053:    * scroll, which will be exposing a cell at a <em>greater</em> index in
1054:    * the list than those elements currently showing. Then the provided
1055:    * direction is negative, the call describes an "upwards" scroll, which
1056:    * will be exposing a cell at a <em>lesser</em> index in the list than
1057:    * those elements currently showing.</p>
1058:    *
1059:    * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
1060:    * comments refer to "rightwards" for positive direction, and "leftwards"
1061:    * for negative.</p>
1062:    * 
1063:    *
1064:    * @param visibleRect The rectangle to scroll an element into
1065:    * @param orientation One of the numeric consants <code>VERTICAL</code>
1066:    * or <code>HORIZONTAL</code>
1067:    * @param direction An integer indicating the scroll direction: positive means
1068:    * forwards (down, right), negative means backwards (up, left)
1069:    *
1070:    * @return The scrollable unit increment, in pixels
1071:    */
1072:   public int getScrollableUnitIncrement(Rectangle visibleRect,
1073:                                         int orientation, int direction)
1074:   {
1075:     ListUI lui = this.getUI();
1076:     if (orientation == SwingConstants.VERTICAL)
1077:       {
1078:         if (direction > 0)
1079:           {
1080:             // Scrolling down
1081:             Point bottomLeft = new Point(visibleRect.x,
1082:                                          visibleRect.y + visibleRect.height);
1083:             int curIdx = lui.locationToIndex(this, bottomLeft);
1084:             Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
1085:             if (curBounds.y + curBounds.height == bottomLeft.y)
1086:               {
1087:                 // we are at the exact bottom of the current cell, so we 
1088:                 // are being asked to scroll to the end of the next one
1089:                 if (curIdx + 1 < model.getSize())
1090:                   {
1091:                     // there *is* a next item in the list
1092:                     Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1);
1093:                     return nxtBounds.height;
1094:                   }
1095:                 else
1096:                   {
1097:                     // no next item, no advance possible
1098:                     return 0;
1099:                   }
1100:               }
1101:             else
1102:               {
1103:                 // we are part way through an existing cell, so we are being
1104:                 // asked to scroll to the bottom of it
1105:                 return (curBounds.y + curBounds.height) - bottomLeft.y;
1106:               }              
1107:           }
1108:         else
1109:           {
1110:             // scrolling up
1111:             Point topLeft = new Point(visibleRect.x, visibleRect.y);
1112:             int curIdx = lui.locationToIndex(this, topLeft);
1113:             Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
1114:             if (curBounds.y == topLeft.y)
1115:               {
1116:                 // we are at the exact top of the current cell, so we 
1117:                 // are being asked to scroll to the top of the previous one
1118:                 if (curIdx > 0)
1119:                   {
1120:                     // there *is* a previous item in the list
1121:                     Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1);
1122:                     return -nxtBounds.height;
1123:                   }
1124:                 else
1125:                   {
1126:                     // no previous item, no advance possible
1127:                     return 0;
1128:                   }
1129:               }
1130:             else
1131:               {
1132:                 // we are part way through an existing cell, so we are being
1133:                 // asked to scroll to the top of it
1134:                 return curBounds.y - topLeft.y;
1135:               }              
1136:           }
1137:       }
1138: 
1139:     // FIXME: handle horizontal scrolling (also wrapping?)
1140:     return 1;
1141:   }
1142: 
1143:   /**
1144:    * <p>Return the number of pixels the list must scroll in order to move a
1145:    * "block" of the list into the provided visible rectangle. When the
1146:    * provided direction is positive, the call describes a "downwards"
1147:    * scroll, which will be exposing a cell at a <em>greater</em> index in
1148:    * the list than those elements currently showing. Then the provided
1149:    * direction is negative, the call describes an "upwards" scroll, which
1150:    * will be exposing a cell at a <em>lesser</em> index in the list than
1151:    * those elements currently showing.</p>
1152:    *
1153:    * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
1154:    * comments refer to "rightwards" for positive direction, and "leftwards"
1155:    * for negative.</p>
1156:    * 
1157:    *
1158:    * @param visibleRect The rectangle to scroll an element into
1159:    * @param orientation One of the numeric consants <code>VERTICAL</code>
1160:    * or <code>HORIZONTAL</code>
1161:    * @param direction An integer indicating the scroll direction: positive means
1162:    * forwards (down, right), negative means backwards (up, left)
1163:    *
1164:    * @return The scrollable unit increment, in pixels
1165:    */
1166:   public int getScrollableBlockIncrement(Rectangle visibleRect,
1167:                                          int orientation, int direction)
1168:   {
1169:       if (orientation == VERTICAL)
1170:       return visibleRect.height * direction;
1171:       else
1172:       return visibleRect.width * direction;
1173:   }
1174: 
1175:   /**
1176:    * Gets the value of the {@link #scrollableTracksViewportWidth} property.
1177:    *
1178:    * @return <code>true</code> if the viewport is larger (horizontally)
1179:    * than the list and the list should be expanded to fit the viewport;
1180:    * <code>false</code> if the viewport is smaller than the list and the
1181:    * list should scroll (horizontally) within the viewport
1182:    */
1183:   public boolean getScrollableTracksViewportWidth()
1184:   {
1185:     Component parent = getParent();
1186:     boolean retVal = false;
1187:     if (parent instanceof JViewport)
1188:       {
1189:         JViewport viewport = (JViewport) parent;
1190:         Dimension pref = getPreferredSize();
1191:         if (viewport.getSize().width > pref.width)
1192:           retVal = true;
1193:         if ((getLayoutOrientation() == HORIZONTAL_WRAP)
1194:             && (getVisibleRowCount() <= 0))
1195:           retVal = true;
1196:       }
1197:     return retVal;
1198:   }
1199: 
1200:   /**
1201:    * Gets the value of the {@link #scrollableTracksViewportWidth} property.
1202:    *
1203:    * @return <code>true</code> if the viewport is larger (vertically)
1204:    * than the list and the list should be expanded to fit the viewport;
1205:    * <code>false</code> if the viewport is smaller than the list and the
1206:    * list should scroll (vertically) within the viewport
1207:    */
1208:   public boolean getScrollableTracksViewportHeight()
1209:   {
1210:     Component parent = getParent();
1211:     boolean retVal = false;
1212:     if (parent instanceof JViewport)
1213:       {
1214:         JViewport viewport = (JViewport) parent;
1215:         Dimension pref = getPreferredSize();
1216:         if (viewport.getSize().height > pref.height)
1217:           retVal = true;
1218:         if ((getLayoutOrientation() == VERTICAL_WRAP)
1219:             && (getVisibleRowCount() <= 0))
1220:           retVal = true;
1221:       }
1222:     return retVal;
1223:   }
1224: 
1225:   public int getAnchorSelectionIndex()
1226:   {
1227:     return selectionModel.getAnchorSelectionIndex();
1228:   }
1229: 
1230:   public int getLeadSelectionIndex()
1231:   {
1232:     return selectionModel.getLeadSelectionIndex();
1233:   }
1234: 
1235:   public int getMinSelectionIndex()
1236:   {
1237:     return selectionModel.getMaxSelectionIndex();
1238:   }
1239: 
1240:   public int getMaxSelectionIndex()
1241:   {
1242:     return selectionModel.getMaxSelectionIndex();
1243:   }
1244: 
1245:   public void clearSelection()
1246:   {
1247:     selectionModel.clearSelection();
1248:   }
1249: 
1250:   public void setSelectionInterval(int anchor, int lead)
1251:   {
1252:     selectionModel.setSelectionInterval(anchor, lead);
1253:   }
1254: 
1255:   public void addSelectionInterval(int anchor, int lead)
1256:   {
1257:     selectionModel.addSelectionInterval(anchor, lead);
1258:   }
1259: 
1260:   public void removeSelectionInterval(int index0, int index1)
1261:   {
1262:     selectionModel.removeSelectionInterval(index0, index1);
1263:   }
1264: 
1265:   /**
1266:    * Returns the value of the <code>valueIsAdjusting</code> property.
1267:    *
1268:    * @return the value
1269:    */
1270:   public boolean getValueIsAdjusting()
1271:   {
1272:     return valueIsAdjusting;
1273:   }
1274: 
1275:   /**
1276:    * Sets the <code>valueIsAdjusting</code> property.
1277:    *
1278:    * @param isAdjusting the new value
1279:    */
1280:   public void setValueIsAdjusting(boolean isAdjusting)
1281:   {
1282:     valueIsAdjusting = isAdjusting;
1283:   }
1284: 
1285:   /**
1286:    * Return the value of the <code>dragEnabled</code> property.
1287:    *
1288:    * @return the value
1289:    * 
1290:    * @since 1.4
1291:    */
1292:   public boolean getDragEnabled()
1293:   {
1294:     return dragEnabled;
1295:   }
1296: 
1297:   /**
1298:    * Set the <code>dragEnabled</code> property.
1299:    *
1300:    * @param enabled new value
1301:    * 
1302:    * @since 1.4
1303:    */
1304:   public void setDragEnabled(boolean enabled)
1305:   {
1306:     dragEnabled = enabled;
1307:   }
1308: 
1309:   /**
1310:    * Returns the layout orientation.
1311:    *
1312:    * @return the orientation, one of <code>JList.VERTICAL</code>,
1313:    * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code>
1314:    *
1315:    * @since 1.4
1316:    */
1317:   public int getLayoutOrientation()
1318:   {
1319:     return layoutOrientation;
1320:   }
1321: 
1322:   /**
1323:    * Sets the layout orientation.
1324:    *
1325:    * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>,
1326:    * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code>
1327:    *
1328:    * @since 1.4
1329:    */
1330:   public void setLayoutOrientation(int orientation)
1331:   {
1332:     if (layoutOrientation == orientation)
1333:       return;
1334: 
1335:     int old = layoutOrientation;
1336:     layoutOrientation = orientation;
1337:     firePropertyChange("layoutOrientation", old, orientation);
1338:   }
1339: 
1340:   /**
1341:    * Returns the bounds of the rectangle that encloses both list cells
1342:    * with index0 and index1.
1343:    *
1344:    * @param index0 the index of the first cell
1345:    * @param index1 the index of the second cell
1346:    *
1347:    * @return  the bounds of the rectangle that encloses both list cells
1348:    *     with index0 and index1, <code>null</code> if one of the indices is
1349:    *     not valid
1350:    */
1351:   public Rectangle getCellBounds(int index0, int index1)
1352:   {
1353:     return ((ListUI) ui).getCellBounds(this, index0, index1);
1354:   }
1355: 
1356:   /**
1357:    * Returns the next list element (beginning from <code>startIndex</code>
1358:    * that starts with <code>prefix</code>. Searching is done in the direction
1359:    * specified by <code>bias</code>.
1360:    *
1361:    * @param prefix the prefix to search for in the cell values
1362:    * @param startIndex the index where to start searching from
1363:    * @param bias the search direction, either {@link Position.Bias.Forward}
1364:    *     or {@link Position.Bias.Backward}
1365:    *
1366:    * @return the index of the found element or -1 if no such element has
1367:    *     been found
1368:    *
1369:    * @throws IllegalArgumentException if prefix is <code>null</code> or
1370:    *     startIndex is not valid
1371:    *
1372:    * @since 1.4
1373:    */
1374:   public int getNextMatch(String prefix, int startIndex, Position.Bias bias)
1375:   {
1376:     if (prefix == null)
1377:       throw new IllegalArgumentException("The argument 'prefix' must not be"
1378:                                          + " null.");
1379:     if (startIndex < 0)
1380:       throw new IllegalArgumentException("The argument 'startIndex' must not"
1381:                                          + " be less than zero.");
1382: 
1383:     int size = model.getSize();
1384:     if (startIndex > model.getSize())
1385:       throw new IllegalArgumentException("The argument 'startIndex' must not"
1386:                                          + " be greater than the number of"
1387:                                          + " elements in the ListModel.");
1388: 
1389:     int index = -1;
1390:     if (bias == Position.Bias.Forward)
1391:       {
1392:         for (int i = startIndex; i < size; i++)
1393:           {
1394:             String item = model.getElementAt(i).toString();
1395:             if (item.startsWith(prefix))
1396:               {
1397:                 index = i;
1398:                 break;
1399:               }
1400:           }
1401:       }
1402:     else
1403:       {
1404:         for (int i = startIndex; i >= 0; i--)
1405:           {
1406:             String item = model.getElementAt(i).toString();
1407:             if (item.startsWith(prefix))
1408:               {
1409:                 index = i;
1410:                 break;
1411:               }
1412:           }
1413:       }
1414:     return index;
1415:   }
1416: }