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