001/* Choice.java -- Java choice button widget.
002   Copyright (C) 1999, 2000, 2001, 2002, 2004, 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt;
040
041import java.awt.event.ItemEvent;
042import java.awt.event.ItemListener;
043import java.awt.peer.ChoicePeer;
044import java.io.Serializable;
045import java.util.EventListener;
046import java.util.Vector;
047
048import javax.accessibility.Accessible;
049import javax.accessibility.AccessibleAction;
050import javax.accessibility.AccessibleContext;
051import javax.accessibility.AccessibleRole;
052
053/**
054 * This class implements a drop down choice list.
055 *
056 * @author Aaron M. Renn (arenn@urbanophile.com)
057 */
058public class Choice extends Component
059  implements ItemSelectable, Serializable, Accessible
060{
061  /**
062   * The number used to generate the name returned by getName.
063   */
064  private static transient long next_choice_number;
065
066  // Serialization constant
067  private static final long serialVersionUID = -4075310674757313071L;
068
069  /**
070   * @serial A list of items for the choice box, which can be <code>null</code>.
071   * This is package-private to avoid an accessor method.
072   */
073  Vector pItems = new Vector();
074
075  /**
076   * @serial The index of the selected item in the choice box.
077   */
078  private int selectedIndex = -1;
079
080  /**
081   * ItemListener chain
082   */
083  private ItemListener item_listeners;
084
085  /**
086   * This class provides accessibility support for the
087   * combo box.
088   *
089   * @author Jerry Quinn  (jlquinn@optonline.net)
090   * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
091   */
092  protected class AccessibleAWTChoice
093    extends AccessibleAWTComponent
094    implements AccessibleAction
095  {
096
097    /**
098     * Serialization constant to match JDK 1.5
099     */
100    private static final long serialVersionUID = 7175603582428509322L;
101
102    /**
103     * Default constructor which simply calls the
104     * super class for generic component accessibility
105     * handling.
106     */
107    public AccessibleAWTChoice()
108    {
109      super();
110    }
111
112    /**
113     * Returns an implementation of the <code>AccessibleAction</code>
114     * interface for this accessible object.  In this case, the
115     * current instance is simply returned (with a more appropriate
116     * type), as it also implements the accessible action as well as
117     * the context.
118     *
119     * @return the accessible action associated with this context.
120     * @see javax.accessibility.AccessibleAction
121     */
122    public AccessibleAction getAccessibleAction()
123    {
124      return this;
125    }
126
127    /**
128     * Returns the role of this accessible object.
129     *
130     * @return the instance of <code>AccessibleRole</code>,
131     *         which describes this object.
132     * @see javax.accessibility.AccessibleRole
133     */
134    public AccessibleRole getAccessibleRole()
135    {
136      return AccessibleRole.COMBO_BOX;
137    }
138          
139    /**
140     * Returns the number of actions associated with this accessible
141     * object.  In this case, it is the number of choices available.
142     *
143     * @return the number of choices available.
144     * @see javax.accessibility.AccessibleAction#getAccessibleActionCount()
145     */
146    public int getAccessibleActionCount()
147    {
148      return pItems.size();
149    }
150
151    /**
152     * Returns a description of the action with the supplied id.
153     * In this case, it is the text used in displaying the particular
154     * choice on-screen.
155     *
156     * @param i the id of the choice whose description should be
157     *          retrieved.
158     * @return the <code>String</code> used to describe the choice.
159     * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int)
160     */
161    public String getAccessibleActionDescription(int i)
162    {
163      return (String) pItems.get(i);
164    }
165          
166    /**
167     * Executes the action with the specified id.  In this case,
168     * calling this method provides the same behaviour as would
169     * choosing a choice from the list in a visual manner.
170     *
171     * @param i the id of the choice to select.
172     * @return true if a valid choice was specified.
173     * @see javax.accessibility.AccessibleAction#doAccessibleAction(int)
174     */
175    public boolean doAccessibleAction(int i)
176    {
177      if (i < 0 || i >= pItems.size())
178        return false;
179            
180      Choice.this.select( i );
181
182      return true;
183    }
184  }
185
186  /**
187   * Initializes a new instance of <code>Choice</code>.
188   *
189   * @exception HeadlessException If GraphicsEnvironment.isHeadless()
190   * returns true
191   */
192  public Choice()
193  {
194    if (GraphicsEnvironment.isHeadless())
195      throw new HeadlessException ();
196  }
197
198  /**
199   * Returns the number of items in the list.
200   *
201   * @return The number of items in the list.
202   */
203  public int getItemCount()
204  {
205    return countItems ();
206  }
207
208  /**
209   * Returns the number of items in the list.
210   *
211   * @return The number of items in the list.
212   *
213   * @deprecated This method is deprecated in favor of <code>getItemCount</code>.
214   */
215  public int countItems()
216  {
217    return pItems.size();
218  }
219
220  /**
221   * Returns the item at the specified index in the list.
222   *
223   * @param index The index into the list to return the item from.
224   *
225   * @exception ArrayIndexOutOfBoundsException If the index is invalid.
226   */
227  public String getItem(int index)
228  {
229    return (String)pItems.elementAt(index);
230  }
231
232  /**
233   * Adds the specified item to this choice box.
234   *
235   * @param item The item to add.
236   *
237   * @exception NullPointerException If the item's value is null
238   *
239   * @since 1.1
240   */
241  public synchronized void add(String item)
242  {
243    if (item == null)
244      throw new NullPointerException ("item must be non-null");
245
246    pItems.addElement(item);
247
248    if (peer != null)
249      ((ChoicePeer) peer).add(item, getItemCount() - 1);
250
251    if (selectedIndex == -1) 
252      select( 0 );
253  }
254
255  /**
256   * Adds the specified item to this choice box.
257   *
258   * This method is oboslete since Java 2 platform 1.1. Please use 
259   * {@link #add(String)} instead.
260   *
261   * @param item The item to add.
262   *
263   * @exception NullPointerException If the item's value is equal to null
264   */
265  public synchronized void addItem(String item)
266  {
267    add(item);
268  }
269
270  /** Inserts an item into this Choice.  Existing items are shifted
271   * upwards.  If the new item is the only item, then it is selected.
272   * If the currently selected item is shifted, then the first item is
273   * selected.  If the currently selected item is not shifted, then it
274   * remains selected.
275   *
276   * @param item The item to add.
277   * @param index The index at which the item should be inserted.
278   *
279   * @exception IllegalArgumentException If index is less than 0
280   */
281  public synchronized void insert(String item, int index)
282  {
283    if (index < 0)
284      throw new IllegalArgumentException ("index may not be less then 0");
285
286    if (index > getItemCount ())
287      index = getItemCount ();
288
289    pItems.insertElementAt(item, index);
290
291    if (peer != null)
292      ((ChoicePeer) peer).add (item, index);
293
294    if (selectedIndex == -1 || selectedIndex >= index)
295      select(0);
296  }
297
298  /**
299   * Removes the specified item from the choice box.
300   *
301   * @param item The item to remove.
302   *
303   * @exception IllegalArgumentException If the specified item doesn't exist.
304   */
305  public synchronized void remove(String item)
306  {
307    int index = pItems.indexOf(item);
308    if (index == -1)
309      throw new IllegalArgumentException ("item \""
310                                          + item + "\" not found in Choice");
311    remove(index);
312  }
313
314  /**
315   * Removes the item at the specified index from the choice box.
316   *
317   * @param index The index of the item to remove.
318   *
319   * @exception IndexOutOfBoundsException If the index is not valid.
320   */
321  public synchronized void remove(int index)
322  {
323    pItems.removeElementAt(index);
324
325    if (peer != null)
326      ((ChoicePeer) peer).remove( index );
327
328    if( getItemCount() == 0 )
329      selectedIndex = -1;
330    else 
331      {
332        if( selectedIndex > index ) 
333          selectedIndex--;
334        else if( selectedIndex == index )
335          selectedIndex = 0;
336
337        if( peer != null )
338          ((ChoicePeer)peer).select( selectedIndex );
339      }
340  }
341
342  /**
343   * Removes all of the objects from this choice box.
344   */
345  public synchronized void removeAll()
346  {
347    if (getItemCount() <= 0)
348      return;
349  
350    pItems.removeAllElements ();
351
352    if (peer != null)
353      {
354        ChoicePeer cp = (ChoicePeer) peer;
355        cp.removeAll ();
356      }
357
358    selectedIndex = -1;
359  }
360
361  /**
362   * Returns the currently selected item, or null if no item is
363   * selected.
364   *
365   * @return The currently selected item.
366   */
367  public synchronized String getSelectedItem()
368  {
369    return (selectedIndex == -1
370            ? null
371            : ((String)pItems.elementAt(selectedIndex)));
372  }
373
374  /**
375   * Returns an array with one row containing the selected item.
376   *
377   * @return An array containing the selected item.
378   */
379  public synchronized Object[] getSelectedObjects()
380  {
381    if (selectedIndex == -1)
382      return null;
383
384    Object[] objs = new Object[1];
385    objs[0] = pItems.elementAt(selectedIndex);
386
387    return objs;
388  }
389
390  /**
391   * Returns the index of the selected item.
392   *
393   * @return The index of the selected item.
394   */
395  public int getSelectedIndex()
396  {
397    return selectedIndex;
398  }
399
400  /**
401   * Forces the item at the specified index to be selected.
402   *
403   * @param index The index of the row to make selected.
404   *
405   * @exception IllegalArgumentException If the specified index is invalid.
406   */
407  public synchronized void select(int index)
408  {
409    if ((index < 0) || (index >= getItemCount()))
410      throw new IllegalArgumentException("Bad index: " + index);
411
412    if( selectedIndex == index ) 
413      return;
414
415    selectedIndex = index;
416    if( peer != null ) 
417      ((ChoicePeer)peer).select( index );
418  }
419
420  /**
421   * Forces the named item to be selected.
422   *
423   * @param item The item to be selected.
424   *
425   * @exception IllegalArgumentException If the specified item does not exist.
426   */
427  public synchronized void select(String item)
428  {
429    int index = pItems.indexOf(item);
430    if( index >= 0 )
431      select( index );
432  }
433
434  /**
435   * Creates the native peer for this object.
436   */
437  public void addNotify()
438  {
439    if (peer == null)
440      peer = getToolkit ().createChoice (this);
441    super.addNotify ();
442  }
443
444  /**
445   * Adds the specified listener to the list of registered listeners for
446   * this object.
447   *
448   * @param listener The listener to add.
449   */
450  public synchronized void addItemListener(ItemListener listener)
451  {
452    item_listeners = AWTEventMulticaster.add(item_listeners, listener);
453  }
454
455  /**
456   * Removes the specified listener from the list of registered listeners for
457   * this object.
458   *
459   * @param listener The listener to remove.
460   */
461  public synchronized void removeItemListener(ItemListener listener)
462  {
463    item_listeners = AWTEventMulticaster.remove(item_listeners, listener);
464  }
465
466  /**
467   * Processes this event by invoking <code>processItemEvent()</code> if the
468   * event is an instance of <code>ItemEvent</code>, otherwise the event
469   * is passed to the superclass.
470   *
471   * @param event The event to process.
472   */
473  protected void processEvent(AWTEvent event)
474  {
475    if (event instanceof ItemEvent)
476      processItemEvent((ItemEvent)event);
477    else
478      super.processEvent(event);
479  }
480
481  void dispatchEventImpl(AWTEvent e)
482  {
483    super.dispatchEventImpl(e);
484
485    if( e.id <= ItemEvent.ITEM_LAST && e.id >= ItemEvent.ITEM_FIRST && 
486        ( item_listeners != null || 
487          ( eventMask & AWTEvent.ITEM_EVENT_MASK ) != 0 ) )
488      processEvent(e);
489  }
490
491  /**
492   * Processes item event by dispatching to any registered listeners.
493   *
494   * @param event The event to process.
495   */
496  protected void processItemEvent(ItemEvent event)
497  {
498    int index = pItems.indexOf((String) event.getItem());
499    if (item_listeners != null)
500      item_listeners.itemStateChanged(event);
501  }
502
503  /**
504   * Returns a debugging string for this object.
505   *
506   * @return A debugging string for this object.
507   */
508  protected String paramString()
509  {
510    return "selectedIndex=" + selectedIndex + "," + super.paramString();
511  }
512
513  /**
514   * Returns an array of all the objects currently registered as FooListeners
515   * upon this Choice. FooListeners are registered using the addFooListener
516   * method.
517   *
518   * @exception ClassCastException If listenerType doesn't specify a class or
519   * interface that implements java.util.EventListener.
520   *
521   * @since 1.3
522   */
523  public <T extends EventListener> T[] getListeners (Class<T> listenerType)
524  {
525    if (listenerType == ItemListener.class)
526      return AWTEventMulticaster.getListeners (item_listeners, listenerType);
527
528    return super.getListeners (listenerType);
529  }
530
531  /**
532   * Returns all registered item listeners.
533   *
534   * @since 1.4
535   */
536  public ItemListener[] getItemListeners ()
537  {
538    return (ItemListener[]) getListeners (ItemListener.class);
539  }
540
541  /**
542   * Gets the AccessibleContext associated with this <code>Choice</code>.
543   * The context is created, if necessary.
544   *
545   * @return the associated context
546   */
547  public AccessibleContext getAccessibleContext()
548  {
549    /* Create the context if this is the first request */
550    if (accessibleContext == null)
551      accessibleContext = new AccessibleAWTChoice();
552    return accessibleContext;
553  }
554  
555  /**
556   * Generate a unique name for this <code>Choice</code>.
557   *
558   * @return A unique name for this <code>Choice</code>.
559   */
560  String generateName()
561  {
562    return "choice" + getUniqueLong();
563  }
564
565  private static synchronized long getUniqueLong()
566  {
567    return next_choice_number++;
568  }
569} // class Choice