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