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