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