001    /* Choice.java -- Java choice button widget.
002       Copyright (C) 1999, 2000, 2001, 2002, 2004, 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 java.awt;
040    
041    import java.awt.event.ItemEvent;
042    import java.awt.event.ItemListener;
043    import java.awt.peer.ChoicePeer;
044    import java.io.Serializable;
045    import java.util.EventListener;
046    import java.util.Vector;
047    
048    import javax.accessibility.Accessible;
049    import javax.accessibility.AccessibleAction;
050    import javax.accessibility.AccessibleContext;
051    import javax.accessibility.AccessibleRole;
052    
053    /**
054     * This class implements a drop down choice list.
055     *
056     * @author Aaron M. Renn (arenn@urbanophile.com)
057     */
058    public 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