001    /* Checkbox.java -- An AWT checkbox widget
002       Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.awt;
041    
042    import java.awt.event.ItemEvent;
043    import java.awt.event.ItemListener;
044    import java.awt.peer.CheckboxPeer;
045    import java.io.Serializable;
046    
047    import javax.accessibility.Accessible;
048    import javax.accessibility.AccessibleAction;
049    import javax.accessibility.AccessibleContext;
050    import javax.accessibility.AccessibleRole;
051    import javax.accessibility.AccessibleState;
052    import javax.accessibility.AccessibleStateSet;
053    import javax.accessibility.AccessibleValue;
054    
055    /**
056     * This class implements a component which has an on/off state.  Two
057     * or more Checkboxes can be grouped by a CheckboxGroup.
058     *
059     * @author Aaron M. Renn (arenn@urbanophile.com)
060     * @author Tom Tromey (tromey@redhat.com)
061     */
062    public class Checkbox extends Component
063      implements ItemSelectable, Accessible, Serializable
064    {
065    
066    // FIXME: Need readObject/writeObject for this.
067    
068    /*
069     * Static Variables
070     */
071    
072    // Serialization Constant
073    private static final long serialVersionUID = 7270714317450821763L;
074    
075    /*************************************************************************/
076    
077    /*
078     * Instance Variables
079     */
080    
081    /**
082      * @serial The checkbox group for this checkbox.
083      */
084    private CheckboxGroup group;
085    
086    /**
087      * @serial The label on this checkbox.
088      */
089    private String label;
090    
091    /**
092      * @serial The state of this checkbox.
093      * This is package-private to avoid an accessor method.
094      */
095    boolean state;
096    
097    // The list of listeners for this object.
098    private transient ItemListener item_listeners;
099    
100      /*
101       * The number used to generate the name returned by getName.
102       */
103      private static transient long next_checkbox_number;
104    
105    /**
106     * This class provides accessibility support for the
107     * checkbox.
108     *
109     * @author Jerry Quinn  (jlquinn@optonline.net)
110     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
111     */
112    protected class AccessibleAWTCheckbox
113      extends AccessibleAWTComponent
114      implements ItemListener, AccessibleAction, AccessibleValue
115    {
116      /**
117       * Serialization constant to match JDK 1.5
118       */
119      private static final long serialVersionUID = 7881579233144754107L;
120    
121      /**
122       * Default constructor which simply calls the
123       * super class for generic component accessibility
124       * handling.
125       */
126      public AccessibleAWTCheckbox()
127      {
128        super();
129      }
130    
131      /**
132       * Captures changes to the state of the checkbox and
133       * fires appropriate accessible property change events.
134       *
135       * @param event the event fired.
136       * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent)
137       */
138      public void itemStateChanged(ItemEvent event)
139      {
140        firePropertyChange(ACCESSIBLE_STATE_PROPERTY,
141                           state ? null : AccessibleState.CHECKED,
142                           state ? AccessibleState.CHECKED : null);
143      }
144      
145      /**
146       * Returns an implementation of the <code>AccessibleAction</code>
147       * interface for this accessible object.  In this case, the
148       * current instance is simply returned (with a more appropriate
149       * type), as it also implements the accessible action as well as
150       * the context.
151       *
152       * @return the accessible action associated with this context.
153       * @see javax.accessibility.AccessibleAction
154       */
155      public AccessibleAction getAccessibleAction()
156      {
157        return this;
158      }
159      
160      /**
161       * Returns an implementation of the <code>AccessibleValue</code>
162       * interface for this accessible object.  In this case, the
163       * current instance is simply returned (with a more appropriate
164       * type), as it also implements the accessible value as well as
165       * the context.
166       *
167       * @return the accessible value associated with this context.
168       * @see javax.accessibility.AccessibleValue
169       */
170      public AccessibleValue getAccessibleValue()
171      {
172        return this;
173      }
174      
175      /* 
176       * The following methods are implemented in the JDK (up to
177       * 1.5) as stubs.  We do likewise here.
178       */
179    
180      /**
181       * Returns the number of actions associated with this accessible
182       * object.  This default implementation returns 0.
183       *
184       * @return the number of accessible actions available.
185       * @see javax.accessibility.AccessibleAction#getAccessibleActionCount()
186       */
187      public int getAccessibleActionCount()
188      {
189        // 1.4.1 and 1.5 do this
190        return 0;
191      }
192    
193      /**
194       * Returns a description of the action with the supplied id.
195       * This default implementation always returns null.
196       *
197       * @param i the id of the action whose description should be
198       *          retrieved.
199       * @return a <code>String</code> describing the action.
200       * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int)
201       */
202      public String getAccessibleActionDescription(int i)
203      {
204        // 1.5 does this
205        return null;
206      }
207    
208      /**
209       * Executes the action with the specified id.  This
210       * default implementation simply returns false.
211       *
212       * @param i the id of the action to perform.
213       * @return true if the action was performed.
214       * @see javax.accessibility.AccessibleAction#doAccessibleAction(int)
215       */
216      public boolean doAccessibleAction(int i)
217      {
218        // 1.5 does this
219        return false;
220      }
221    
222      /**
223       * Returns the current value of this accessible object.
224       * If no value has been set, null is returned.  This
225       * default implementation always returns null, regardless.
226       *
227       * @return the numeric value of this object, or null if
228       *         no value has been set.
229       * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue()
230       */
231      public Number getCurrentAccessibleValue()
232      {
233        // 1.5 does this
234        return null;
235      }
236    
237      /**
238       * Sets the current value of this accessible object
239       * to that supplied.  In this default implementation,
240       * the value is never set and the method always returns
241       * false.
242       *
243       * @param number the new accessible value.
244       * @return true if the value was set.
245       * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number)
246       */
247      public boolean setCurrentAccessibleValue(Number number)
248      {
249        // 1.5 does this
250        return false;
251      }
252    
253      /**
254       * Returns the minimum acceptable accessible value used
255       * by this object, or null if no minimum value exists.
256       * This default implementation always returns null.
257       *
258       * @return the minimum acceptable accessible value, or null
259       *         if there is no minimum.
260       * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue()
261       */
262      public Number getMinimumAccessibleValue()
263      {
264        return null;
265      }
266    
267      /**
268       * Returns the maximum acceptable accessible value used
269       * by this object, or null if no maximum value exists.
270       * This default implementation always returns null.
271       *
272       * @return the maximum acceptable accessible value, or null
273       *         if there is no maximum.
274       * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue()
275       */
276      public Number getMaximumAccessibleValue()
277      {
278        return null;
279      }
280      
281      /**
282       * Returns the role of this accessible object.
283       *
284       * @return the instance of <code>AccessibleRole</code>,
285       *         which describes this object.
286       * @see javax.accessibility.AccessibleRole
287       */
288      public AccessibleRole getAccessibleRole() 
289      {
290        return AccessibleRole.CHECK_BOX;
291      }
292      
293      /**
294       * Returns the state set of this accessible object.
295       *
296       * @return a set of <code>AccessibleState</code>s
297       *         which represent the current state of the
298       *         accessible object.
299       * @see javax.accessibility.AccessibleState
300       * @see javax.accessibility.AccessibleStateSet
301       */
302      public AccessibleStateSet getAccessibleStateSet()
303      {
304        AccessibleStateSet set = super.getAccessibleStateSet();
305        if (state)
306          set.add(AccessibleState.CHECKED);
307        return set;
308      }
309    
310    }
311    
312    /*************************************************************************/
313    
314    /*
315     * Constructors
316     */
317    
318    /**
319      * Initializes a new instance of <code>Checkbox</code> with no label,
320      * an initial state of off, and that is not part of any checkbox group.
321      */
322    public 
323    Checkbox()
324    {
325      this("", false, null);
326    }
327    
328    /*************************************************************************/
329    
330    /**
331      * Initializes a new instance of <code>Checkbox</code> with the specified
332      * label, an initial state of off, and that is not part of any checkbox
333      * group.
334      *
335      * @param label The label for this checkbox.
336      */
337    public
338    Checkbox(String label)
339    {
340      this(label, false, null);
341    }
342    
343    /*************************************************************************/
344    
345    /**
346      * Initializes a new instance of <code>Checkbox</code> with the specified
347      * label and initial state, and that is not part of any checkbox
348      * group.
349      *
350      * @param label The label for this checkbox.
351      * @param state The initial state of the checkbox, <code>true</code> for
352      * on, <code>false</code> for off.
353      */
354    public
355    Checkbox(String label, boolean state)
356    {
357      this(label, state, null);
358    }
359    
360    /*************************************************************************/
361    
362    /**
363      * Initializes a new instance of <code>Checkbox</code> with the specified
364      * label, initial state, and checkbox group.
365      *
366      * @param label The label for this checkbox.
367      * @param group The checkbox group for this box, or <code>null</code>
368      * if there is no checkbox group.
369      * @param state The initial state of the checkbox, <code>true</code> for
370      * on, <code>false</code> for off.
371      */
372    public
373    Checkbox(String label, CheckboxGroup group, boolean state)
374    {
375      this(label, state, group);
376    }
377    
378    /*************************************************************************/
379    
380    /**
381      * Initializes a new instance of <code>Checkbox</code> with the specified
382      * label, initial state, and checkbox group.
383      *
384      * @param label The label for this checkbox.
385      * @param state The initial state of the checkbox, <code>true</code> for
386      * on, <code>false</code> for off.
387      * @param group The checkbox group for this box, or <code>null</code>
388      * if there is no checkbox group.
389      */
390    public
391    Checkbox(String label, boolean state, CheckboxGroup group)
392    {
393      this.label = label;
394      this.state = state;
395      this.group = group;
396    
397      if ( state && group != null )
398        {
399          group.setSelectedCheckbox(this);
400        }
401    }
402    
403    /*************************************************************************/
404    
405    /*
406     * Instance Variables
407     */
408    
409    /**
410      * Returns the label for this checkbox.
411      *
412      * @return The label for this checkbox.
413      */
414    public String
415    getLabel()
416    {
417      return(label);
418    }
419    
420    /*************************************************************************/
421    
422    /**
423      * Sets the label for this checkbox to the specified value.
424      *
425      * @param label The new checkbox label.
426      */
427    public synchronized void
428    setLabel(String label)
429    {
430      this.label = label;
431      if (peer != null)
432        {
433          CheckboxPeer cp = (CheckboxPeer) peer;
434          cp.setLabel(label);
435        }
436    }
437    
438    /*************************************************************************/
439    
440    /**
441      * Returns the state of this checkbox.
442      *
443      * @return The state of this checkbox, which will be <code>true</code> for
444      * on and <code>false</code> for off.
445      */
446    public boolean
447    getState()
448    {
449      return(state);
450    }
451    
452    /*************************************************************************/
453    
454    /**
455      * Sets the state of this checkbox to the specified value.
456      *
457      * @param state The new state of the checkbox, which will be <code>true</code>
458      * for on or <code>false</code> for off.
459      */
460    public synchronized void
461    setState(boolean state)
462    {
463      if (this.state != state)
464        {
465          this.state = state;
466          if (peer != null)
467            {
468              CheckboxPeer cp = (CheckboxPeer) peer;
469              cp.setState (state);
470            }
471        }
472    }
473    
474    /*************************************************************************/
475    
476    /**
477      * Returns an array of length one containing the checkbox label if this
478      * checkbox is selected.  Otherwise <code>null</code> is returned.
479      *
480      * @return The selection state of this checkbox.
481      */
482    public Object[]
483    getSelectedObjects()
484    {
485      if (state == false)
486        return(null);
487    
488      Object[] objs = new Object[1];
489      objs[0] = label;
490    
491      return(objs);
492    }
493    
494    /*************************************************************************/
495    
496    /**
497      * Returns the checkbox group this object is a member of, if any.
498      *
499      * @return This object's checkbox group, of <code>null</code> if it is
500      * not a member of any group.
501      */
502    public CheckboxGroup
503    getCheckboxGroup()
504    {
505      return(group);
506    }
507    
508    /*************************************************************************/
509    
510    /**
511      * Sets this object's checkbox group to the specified group.
512      *
513      * @param group The new checkbox group, or <code>null</code> to make this
514      * object part of no checkbox group.
515      */
516    public synchronized void
517    setCheckboxGroup(CheckboxGroup group)
518    {
519      this.group = group;
520      if (peer != null)
521        {
522          CheckboxPeer cp = (CheckboxPeer) peer;
523          cp.setCheckboxGroup (group);
524        }
525    }
526    
527    /*************************************************************************/
528    
529    /**
530      * Creates this object's native peer.
531      */
532    public void
533    addNotify()
534    {
535      if (peer == null)
536        peer = getToolkit ().createCheckbox (this);
537      super.addNotify ();
538    }
539    
540      public ItemListener[] getItemListeners ()
541      {
542        return (ItemListener[])
543          AWTEventMulticaster.getListeners (item_listeners, ItemListener.class);
544      }
545    
546    /**
547      * Adds a new listeners to the list of registered listeners for this object.
548      *
549      * @param listener The new listener to add.
550      */
551    public synchronized void
552    addItemListener(ItemListener listener)
553    {
554      item_listeners = AWTEventMulticaster.add(item_listeners, listener);
555    }
556    
557    /*************************************************************************/
558    
559    /**
560      * Removes a listener from the list of registered listeners for this object.
561      *
562      * @param listener The listener to remove.
563      */
564    public synchronized void
565    removeItemListener(ItemListener listener)
566    {
567      item_listeners = AWTEventMulticaster.remove(item_listeners, listener);
568    }
569    
570    /*************************************************************************/
571    
572    /**
573      * Processes this event by calling <code>processItemEvent()</code> if it
574      * is any instance of <code>ItemEvent</code>.  Otherwise it is passed to
575      * the superclass for processing.
576      *
577      * @param event The event to process.
578      */
579    protected void
580    processEvent(AWTEvent event)
581    {
582      if (event instanceof ItemEvent)
583        processItemEvent((ItemEvent)event);
584      else
585        super.processEvent(event);
586    }
587    
588    /*************************************************************************/
589    
590    /**
591      * Processes this event by dispatching it to any registered listeners.
592      *
593      * @param event The <code>ItemEvent</code> to process.
594      */
595    protected void
596    processItemEvent(ItemEvent event)
597    {
598      if (item_listeners != null)
599        item_listeners.itemStateChanged(event);
600    }
601    
602    void
603    dispatchEventImpl(AWTEvent e)
604    {
605      if (e.id <= ItemEvent.ITEM_LAST
606          && e.id >= ItemEvent.ITEM_FIRST)
607        {
608          ItemEvent ie = (ItemEvent) e;
609          int itemState = ie.getStateChange();
610          setState(itemState == ItemEvent.SELECTED ? true : false);
611          if (item_listeners != null 
612              || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)
613            processEvent(e);
614        }
615      else
616        super.dispatchEventImpl(e);
617    }
618    
619    /*************************************************************************/
620    
621    /**
622      * Returns a debugging string for this object.
623      */
624    protected String
625    paramString()
626    {
627      // Note: We cannot add the checkbox group information here because this
628      // would trigger infinite recursion when CheckboxGroup.toString() is
629      // called and the box is in its selected state.
630      return ("label=" + label + ",state=" + state + "," + super.paramString());
631    }
632    
633    /**
634     * Gets the AccessibleContext associated with this <code>Checkbox</code>.
635     * The context is created, if necessary.
636     *
637     * @return the associated context
638     */
639    public AccessibleContext getAccessibleContext()
640    {
641      /* Create the context if this is the first request */
642      if (accessibleContext == null)
643      {
644        AccessibleAWTCheckbox ac = new AccessibleAWTCheckbox();
645        accessibleContext = ac;
646        addItemListener(ac);
647      }
648      return accessibleContext;
649    }
650    
651      /**
652       * Generate a unique name for this checkbox.
653       *
654       * @return A unique name for this checkbox.
655       */
656      String generateName()
657      {
658        return "checkbox" + getUniqueLong();
659      }
660    
661      private static synchronized long getUniqueLong()
662      {
663        return next_checkbox_number++;
664      }
665    }