1/*
2 * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt;
27
28import java.awt.event.ItemEvent;
29import java.awt.event.ItemListener;
30import java.awt.peer.CheckboxMenuItemPeer;
31import java.io.IOException;
32import java.io.ObjectInputStream;
33import java.io.ObjectOutputStream;
34import java.util.EventListener;
35
36import javax.accessibility.Accessible;
37import javax.accessibility.AccessibleAction;
38import javax.accessibility.AccessibleContext;
39import javax.accessibility.AccessibleRole;
40import javax.accessibility.AccessibleValue;
41
42import sun.awt.AWTAccessor;
43
44/**
45 * This class represents a check box that can be included in a menu.
46 * Selecting the check box in the menu changes its state from
47 * "on" to "off" or from "off" to "on."
48 * <p>
49 * The following picture depicts a menu which contains an instance
50 * of {@code CheckBoxMenuItem}:
51 * <p>
52 * <img src="doc-files/MenuBar-1.gif"
53 * alt="Menu labeled Examples, containing items Basic, Simple, Check, and More
54 * Examples. The Check item is a CheckBoxMenuItem instance, in the off state."
55 * style="float:center; margin: 7px 10px;">
56 * <p>
57 * The item labeled {@code Check} shows a check box menu item
58 * in its "off" state.
59 * <p>
60 * When a check box menu item is selected, AWT sends an item event to
61 * the item. Since the event is an instance of {@code ItemEvent},
62 * the {@code processEvent} method examines the event and passes
63 * it along to {@code processItemEvent}. The latter method redirects
64 * the event to any {@code ItemListener} objects that have
65 * registered an interest in item events generated by this menu item.
66 *
67 * @author      Sami Shaio
68 * @see         java.awt.event.ItemEvent
69 * @see         java.awt.event.ItemListener
70 * @since       1.0
71 */
72public class CheckboxMenuItem extends MenuItem implements ItemSelectable, Accessible {
73
74    static {
75        /* ensure that the necessary native libraries are loaded */
76        Toolkit.loadLibraries();
77        if (!GraphicsEnvironment.isHeadless()) {
78            initIDs();
79        }
80
81        AWTAccessor.setCheckboxMenuItemAccessor(
82            new AWTAccessor.CheckboxMenuItemAccessor() {
83                public boolean getState(CheckboxMenuItem cmi) {
84                    return cmi.state;
85                }
86            });
87    }
88
89   /**
90    * The state of a checkbox menu item
91    * @serial
92    * @see #getState()
93    * @see #setState(boolean)
94    */
95    private volatile boolean state;
96
97    private transient volatile ItemListener itemListener;
98
99    private static final String base = "chkmenuitem";
100    private static int nameCounter = 0;
101
102    /*
103     * JDK 1.1 serialVersionUID
104     */
105     private static final long serialVersionUID = 6190621106981774043L;
106
107    /**
108     * Create a check box menu item with an empty label.
109     * The item's state is initially set to "off."
110     * @exception HeadlessException if GraphicsEnvironment.isHeadless()
111     * returns true
112     * @see java.awt.GraphicsEnvironment#isHeadless
113     * @since   1.1
114     */
115    public CheckboxMenuItem() throws HeadlessException {
116        this("", false);
117    }
118
119    /**
120     * Create a check box menu item with the specified label.
121     * The item's state is initially set to "off."
122
123     * @param     label   a string label for the check box menu item,
124     *                or {@code null} for an unlabeled menu item.
125     * @exception HeadlessException if GraphicsEnvironment.isHeadless()
126     * returns true
127     * @see java.awt.GraphicsEnvironment#isHeadless
128     */
129    public CheckboxMenuItem(String label) throws HeadlessException {
130        this(label, false);
131    }
132
133    /**
134     * Create a check box menu item with the specified label and state.
135     * @param      label   a string label for the check box menu item,
136     *                     or {@code null} for an unlabeled menu item.
137     * @param      state   the initial state of the menu item, where
138     *                     {@code true} indicates "on" and
139     *                     {@code false} indicates "off."
140     * @exception HeadlessException if GraphicsEnvironment.isHeadless()
141     * returns true
142     * @see java.awt.GraphicsEnvironment#isHeadless
143     * @since      1.1
144     */
145    public CheckboxMenuItem(String label, boolean state)
146        throws HeadlessException {
147        super(label);
148        this.state = state;
149    }
150
151    /**
152     * Construct a name for this MenuComponent.  Called by getName() when
153     * the name is null.
154     */
155    String constructComponentName() {
156        synchronized (CheckboxMenuItem.class) {
157            return base + nameCounter++;
158        }
159    }
160
161    /**
162     * Creates the peer of the checkbox item.  This peer allows us to
163     * change the look of the checkbox item without changing its
164     * functionality.
165     * Most applications do not call this method directly.
166     * @see     java.awt.Component#getToolkit()
167     */
168    public void addNotify() {
169        synchronized (getTreeLock()) {
170            if (peer == null)
171                peer = getComponentFactory().createCheckboxMenuItem(this);
172            super.addNotify();
173        }
174    }
175
176    /**
177     * Determines whether the state of this check box menu item
178     * is "on" or "off."
179     *
180     * @return      the state of this check box menu item, where
181     *                     {@code true} indicates "on" and
182     *                     {@code false} indicates "off"
183     * @see        #setState
184     */
185    public boolean getState() {
186        return state;
187    }
188
189    /**
190     * Sets this check box menu item to the specified state.
191     * The boolean value {@code true} indicates "on" while
192     * {@code false} indicates "off."
193     *
194     * <p>Note that this method should be primarily used to
195     * initialize the state of the check box menu item.
196     * Programmatically setting the state of the check box
197     * menu item will <i>not</i> trigger
198     * an {@code ItemEvent}.  The only way to trigger an
199     * {@code ItemEvent} is by user interaction.
200     *
201     * @param      b   {@code true} if the check box
202     *             menu item is on, otherwise {@code false}
203     * @see        #getState
204     */
205    public synchronized void setState(boolean b) {
206        state = b;
207        CheckboxMenuItemPeer peer = (CheckboxMenuItemPeer)this.peer;
208        if (peer != null) {
209            peer.setState(b);
210        }
211    }
212
213    /**
214     * Returns the an array (length 1) containing the checkbox menu item
215     * label or null if the checkbox is not selected.
216     * @see ItemSelectable
217     */
218    public synchronized Object[] getSelectedObjects() {
219        if (state) {
220            Object[] items = new Object[1];
221            items[0] = label;
222            return items;
223        }
224        return null;
225    }
226
227    /**
228     * Adds the specified item listener to receive item events from
229     * this check box menu item.  Item events are sent in response to user
230     * actions, but not in response to calls to setState().
231     * If l is null, no exception is thrown and no action is performed.
232     * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
233     * >AWT Threading Issues</a> for details on AWT's threading model.
234     *
235     * @param         l the item listener
236     * @see           #removeItemListener
237     * @see           #getItemListeners
238     * @see           #setState
239     * @see           java.awt.event.ItemEvent
240     * @see           java.awt.event.ItemListener
241     * @since         1.1
242     */
243    public synchronized void addItemListener(ItemListener l) {
244        if (l == null) {
245            return;
246        }
247        itemListener = AWTEventMulticaster.add(itemListener, l);
248        newEventsOnly = true;
249    }
250
251    /**
252     * Removes the specified item listener so that it no longer receives
253     * item events from this check box menu item.
254     * If l is null, no exception is thrown and no action is performed.
255     * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
256     * >AWT Threading Issues</a> for details on AWT's threading model.
257     *
258     * @param         l the item listener
259     * @see           #addItemListener
260     * @see           #getItemListeners
261     * @see           java.awt.event.ItemEvent
262     * @see           java.awt.event.ItemListener
263     * @since         1.1
264     */
265    public synchronized void removeItemListener(ItemListener l) {
266        if (l == null) {
267            return;
268        }
269        itemListener = AWTEventMulticaster.remove(itemListener, l);
270    }
271
272    /**
273     * Returns an array of all the item listeners
274     * registered on this checkbox menuitem.
275     *
276     * @return all of this checkbox menuitem's {@code ItemListener}s
277     *         or an empty array if no item
278     *         listeners are currently registered
279     *
280     * @see           #addItemListener
281     * @see           #removeItemListener
282     * @see           java.awt.event.ItemEvent
283     * @see           java.awt.event.ItemListener
284     * @since 1.4
285     */
286    public synchronized ItemListener[] getItemListeners() {
287        return getListeners(ItemListener.class);
288    }
289
290    /**
291     * Returns an array of all the objects currently registered
292     * as <code><em>Foo</em>Listener</code>s
293     * upon this {@code CheckboxMenuItem}.
294     * <code><em>Foo</em>Listener</code>s are registered using the
295     * <code>add<em>Foo</em>Listener</code> method.
296     *
297     * <p>
298     * You can specify the {@code listenerType} argument
299     * with a class literal, such as
300     * <code><em>Foo</em>Listener.class</code>.
301     * For example, you can query a
302     * {@code CheckboxMenuItem c}
303     * for its item listeners with the following code:
304     *
305     * <pre>ItemListener[] ils = (ItemListener[])(c.getListeners(ItemListener.class));</pre>
306     *
307     * If no such listeners exist, this method returns an empty array.
308     *
309     * @param listenerType the type of listeners requested; this parameter
310     *          should specify an interface that descends from
311     *          {@code java.util.EventListener}
312     * @return an array of all objects registered as
313     *          <code><em>Foo</em>Listener</code>s on this checkbox menuitem,
314     *          or an empty array if no such
315     *          listeners have been added
316     * @exception ClassCastException if {@code listenerType}
317     *          doesn't specify a class or interface that implements
318     *          {@code java.util.EventListener}
319     *
320     * @see #getItemListeners
321     * @since 1.3
322     */
323    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
324        EventListener l = null;
325        if  (listenerType == ItemListener.class) {
326            l = itemListener;
327        } else {
328            return super.getListeners(listenerType);
329        }
330        return AWTEventMulticaster.getListeners(l, listenerType);
331    }
332
333    // REMIND: remove when filtering is done at lower level
334    boolean eventEnabled(AWTEvent e) {
335        if (e.id == ItemEvent.ITEM_STATE_CHANGED) {
336            if ((eventMask & AWTEvent.ITEM_EVENT_MASK) != 0 ||
337                itemListener != null) {
338                return true;
339            }
340            return false;
341        }
342        return super.eventEnabled(e);
343    }
344
345    /**
346     * Processes events on this check box menu item.
347     * If the event is an instance of {@code ItemEvent},
348     * this method invokes the {@code processItemEvent} method.
349     * If the event is not an item event,
350     * it invokes {@code processEvent} on the superclass.
351     * <p>
352     * Check box menu items currently support only item events.
353     * <p>Note that if the event parameter is {@code null}
354     * the behavior is unspecified and may result in an
355     * exception.
356     *
357     * @param        e the event
358     * @see          java.awt.event.ItemEvent
359     * @see          #processItemEvent
360     * @since        1.1
361     */
362    protected void processEvent(AWTEvent e) {
363        if (e instanceof ItemEvent) {
364            processItemEvent((ItemEvent)e);
365            return;
366        }
367        super.processEvent(e);
368    }
369
370    /**
371     * Processes item events occurring on this check box menu item by
372     * dispatching them to any registered {@code ItemListener} objects.
373     * <p>
374     * This method is not called unless item events are
375     * enabled for this menu item. Item events are enabled
376     * when one of the following occurs:
377     * <ul>
378     * <li>An {@code ItemListener} object is registered
379     * via {@code addItemListener}.
380     * <li>Item events are enabled via {@code enableEvents}.
381     * </ul>
382     * <p>Note that if the event parameter is {@code null}
383     * the behavior is unspecified and may result in an
384     * exception.
385     *
386     * @param       e the item event
387     * @see         java.awt.event.ItemEvent
388     * @see         java.awt.event.ItemListener
389     * @see         #addItemListener
390     * @see         java.awt.MenuItem#enableEvents
391     * @since       1.1
392     */
393    protected void processItemEvent(ItemEvent e) {
394        ItemListener listener = itemListener;
395        if (listener != null) {
396            listener.itemStateChanged(e);
397        }
398    }
399
400    /*
401     * Post an ItemEvent and toggle state.
402     */
403    void doMenuEvent(long when, int modifiers) {
404        setState(!state);
405        Toolkit.getEventQueue().postEvent(
406            new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
407                          getLabel(),
408                          state ? ItemEvent.SELECTED :
409                                  ItemEvent.DESELECTED));
410    }
411
412    /**
413     * Returns a string representing the state of this
414     * {@code CheckBoxMenuItem}. This
415     * method is intended to be used only for debugging purposes, and the
416     * content and format of the returned string may vary between
417     * implementations. The returned string may be empty but may not be
418     * {@code null}.
419     *
420     * @return     the parameter string of this check box menu item
421     */
422    public String paramString() {
423        return super.paramString() + ",state=" + state;
424    }
425
426    /* Serialization support.
427     */
428
429    /*
430     * Serial Data Version
431     * @serial
432     */
433    private int checkboxMenuItemSerializedDataVersion = 1;
434
435    /**
436     * Writes default serializable fields to stream.  Writes
437     * a list of serializable {@code ItemListeners}
438     * as optional data.  The non-serializable
439     * {@code ItemListeners} are detected and
440     * no attempt is made to serialize them.
441     *
442     * @param s the {@code ObjectOutputStream} to write
443     * @serialData {@code null} terminated sequence of
444     *  0 or more pairs; the pair consists of a {@code String}
445     *  and an {@code Object}; the {@code String} indicates
446     *  the type of object and is one of the following:
447     *  {@code itemListenerK} indicating an
448     *    {@code ItemListener} object
449     *
450     * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
451     * @see java.awt.Component#itemListenerK
452     * @see #readObject(ObjectInputStream)
453     */
454    private void writeObject(ObjectOutputStream s)
455      throws java.io.IOException
456    {
457      s.defaultWriteObject();
458
459      AWTEventMulticaster.save(s, itemListenerK, itemListener);
460      s.writeObject(null);
461    }
462
463    /*
464     * Reads the {@code ObjectInputStream} and if it
465     * isn't {@code null} adds a listener to receive
466     * item events fired by the {@code Checkbox} menu item.
467     * Unrecognized keys or values will be ignored.
468     *
469     * @param s the {@code ObjectInputStream} to read
470     * @serial
471     * @see removeActionListener()
472     * @see addActionListener()
473     * @see #writeObject
474     */
475    private void readObject(ObjectInputStream s)
476      throws ClassNotFoundException, IOException
477    {
478      s.defaultReadObject();
479
480      Object keyOrNull;
481      while(null != (keyOrNull = s.readObject())) {
482        String key = ((String)keyOrNull).intern();
483
484        if (itemListenerK == key)
485          addItemListener((ItemListener)(s.readObject()));
486
487        else // skip value for unrecognized key
488          s.readObject();
489      }
490    }
491
492    /**
493     * Initialize JNI field and method IDs
494     */
495    private static native void initIDs();
496
497
498/////////////////
499// Accessibility support
500////////////////
501
502    /**
503     * Gets the AccessibleContext associated with this CheckboxMenuItem.
504     * For checkbox menu items, the AccessibleContext takes the
505     * form of an AccessibleAWTCheckboxMenuItem.
506     * A new AccessibleAWTCheckboxMenuItem is created if necessary.
507     *
508     * @return an AccessibleAWTCheckboxMenuItem that serves as the
509     *         AccessibleContext of this CheckboxMenuItem
510     * @since 1.3
511     */
512    public AccessibleContext getAccessibleContext() {
513        if (accessibleContext == null) {
514            accessibleContext = new AccessibleAWTCheckboxMenuItem();
515        }
516        return accessibleContext;
517    }
518
519    /**
520     * Inner class of CheckboxMenuItem used to provide default support for
521     * accessibility.  This class is not meant to be used directly by
522     * application developers, but is instead meant only to be
523     * subclassed by menu component developers.
524     * <p>
525     * This class implements accessibility support for the
526     * {@code CheckboxMenuItem} class.  It provides an implementation
527     * of the Java Accessibility API appropriate to checkbox menu item
528     * user-interface elements.
529     * @since 1.3
530     */
531    protected class AccessibleAWTCheckboxMenuItem extends AccessibleAWTMenuItem
532        implements AccessibleAction, AccessibleValue
533    {
534        /*
535         * JDK 1.3 serialVersionUID
536         */
537        private static final long serialVersionUID = -1122642964303476L;
538
539        /**
540         * Get the AccessibleAction associated with this object.  In the
541         * implementation of the Java Accessibility API for this class,
542         * return this object, which is responsible for implementing the
543         * AccessibleAction interface on behalf of itself.
544         *
545         * @return this object
546         */
547        public AccessibleAction getAccessibleAction() {
548            return this;
549        }
550
551        /**
552         * Get the AccessibleValue associated with this object.  In the
553         * implementation of the Java Accessibility API for this class,
554         * return this object, which is responsible for implementing the
555         * AccessibleValue interface on behalf of itself.
556         *
557         * @return this object
558         */
559        public AccessibleValue getAccessibleValue() {
560            return this;
561        }
562
563        /**
564         * Returns the number of Actions available in this object.
565         * If there is more than one, the first one is the "default"
566         * action.
567         *
568         * @return the number of Actions in this object
569         */
570        public int getAccessibleActionCount() {
571            return 0;  //  To be fully implemented in a future release
572        }
573
574        /**
575         * Return a description of the specified action of the object.
576         *
577         * @param i zero-based index of the actions
578         */
579        public String getAccessibleActionDescription(int i) {
580            return null;  //  To be fully implemented in a future release
581        }
582
583        /**
584         * Perform the specified Action on the object
585         *
586         * @param i zero-based index of actions
587         * @return true if the action was performed; otherwise false.
588         */
589        public boolean doAccessibleAction(int i) {
590            return false;    //  To be fully implemented in a future release
591        }
592
593        /**
594         * Get the value of this object as a Number.  If the value has not been
595         * set, the return value will be null.
596         *
597         * @return value of the object
598         * @see #setCurrentAccessibleValue
599         */
600        public Number getCurrentAccessibleValue() {
601            return null;  //  To be fully implemented in a future release
602        }
603
604        /**
605         * Set the value of this object as a Number.
606         *
607         * @return true if the value was set; otherwise false
608         * @see #getCurrentAccessibleValue
609         */
610        public boolean setCurrentAccessibleValue(Number n) {
611            return false;  //  To be fully implemented in a future release
612        }
613
614        /**
615         * Get the minimum value of this object as a Number.
616         *
617         * @return Minimum value of the object; null if this object does not
618         * have a minimum value
619         * @see #getMaximumAccessibleValue
620         */
621        public Number getMinimumAccessibleValue() {
622            return null;  //  To be fully implemented in a future release
623        }
624
625        /**
626         * Get the maximum value of this object as a Number.
627         *
628         * @return Maximum value of the object; null if this object does not
629         * have a maximum value
630         * @see #getMinimumAccessibleValue
631         */
632        public Number getMaximumAccessibleValue() {
633            return null;  //  To be fully implemented in a future release
634        }
635
636        /**
637         * Get the role of this object.
638         *
639         * @return an instance of AccessibleRole describing the role of the
640         * object
641         */
642        public AccessibleRole getAccessibleRole() {
643            return AccessibleRole.CHECK_BOX;
644        }
645
646    } // class AccessibleAWTMenuItem
647
648}
649