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