1 /* 2 * Copyright (c) 1997, 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 javax.swing; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 30 import java.beans.JavaBean; 31 import java.beans.BeanProperty; 32 33 import java.io.Serializable; 34 import java.io.ObjectOutputStream; 35 import java.io.ObjectInputStream; 36 import java.io.IOException; 37 38 import javax.swing.plaf.*; 39 import javax.swing.event.*; 40 import javax.accessibility.*; 41 42 /** 43 * An implementation of an item in a menu. A menu item is essentially a button 44 * sitting in a list. When the user selects the "button", the action 45 * associated with the menu item is performed. A <code>JMenuItem</code> 46 * contained in a <code>JPopupMenu</code> performs exactly that function. 47 * <p> 48 * Menu items can be configured, and to some degree controlled, by 49 * <code><a href="Action.html">Action</a></code>s. Using an 50 * <code>Action</code> with a menu item has many benefits beyond directly 51 * configuring a menu item. Refer to <a href="Action.html#buttonActions"> 52 * Swing Components Supporting <code>Action</code></a> for more 53 * details, and you can find more information in <a 54 * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How 55 * to Use Actions</a>, a section in <em>The Java Tutorial</em>. 56 * <p> 57 * For further documentation and for examples, see 58 * <a 59 href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a> 60 * in <em>The Java Tutorial.</em> 61 * <p> 62 * <strong>Warning:</strong> Swing is not thread safe. For more 63 * information see <a 64 * href="package-summary.html#threading">Swing's Threading 65 * Policy</a>. 66 * <p> 67 * <strong>Warning:</strong> 68 * Serialized objects of this class will not be compatible with 69 * future Swing releases. The current serialization support is 70 * appropriate for short term storage or RMI between applications running 71 * the same version of Swing. As of 1.4, support for long term storage 72 * of all JavaBeans™ 73 * has been added to the <code>java.beans</code> package. 74 * Please see {@link java.beans.XMLEncoder}. 75 * 76 * @author Georges Saab 77 * @author David Karlton 78 * @see JPopupMenu 79 * @see JMenu 80 * @see JCheckBoxMenuItem 81 * @see JRadioButtonMenuItem 82 * @since 1.2 83 */ 84 @JavaBean(defaultProperty = "UIClassID", description = "An item which can be selected in a menu.") 85 @SwingContainer(false) 86 @SuppressWarnings("serial") 87 public class JMenuItem extends AbstractButton implements Accessible,MenuElement { 88 89 /** 90 * @see #getUIClassID 91 * @see #readObject 92 */ 93 private static final String uiClassID = "MenuItemUI"; 94 95 /* diagnostic aids -- should be false for production builds. */ 96 private static final boolean TRACE = false; // trace creates and disposes 97 private static final boolean VERBOSE = false; // show reuse hits/misses 98 private static final boolean DEBUG = false; // show bad params, misc. 99 100 private boolean isMouseDragged = false; 101 102 /** 103 * Creates a <code>JMenuItem</code> with no set text or icon. 104 */ 105 public JMenuItem() { 106 this(null, (Icon)null); 107 } 108 109 /** 110 * Creates a <code>JMenuItem</code> with the specified icon. 111 * 112 * @param icon the icon of the <code>JMenuItem</code> 113 */ 114 public JMenuItem(Icon icon) { 115 this(null, icon); 116 } 117 118 /** 119 * Creates a <code>JMenuItem</code> with the specified text. 120 * 121 * @param text the text of the <code>JMenuItem</code> 122 */ 123 public JMenuItem(String text) { 124 this(text, (Icon)null); 125 } 126 127 /** 128 * Creates a menu item whose properties are taken from the 129 * specified <code>Action</code>. 130 * 131 * @param a the action of the <code>JMenuItem</code> 132 * @since 1.3 133 */ 134 public JMenuItem(Action a) { 135 this(); 136 setAction(a); 137 } 138 139 /** 140 * Creates a <code>JMenuItem</code> with the specified text and icon. 141 * 142 * @param text the text of the <code>JMenuItem</code> 143 * @param icon the icon of the <code>JMenuItem</code> 144 */ 145 public JMenuItem(String text, Icon icon) { 146 setModel(new DefaultButtonModel()); 147 init(text, icon); 148 initFocusability(); 149 } 150 151 /** 152 * Creates a <code>JMenuItem</code> with the specified text and 153 * keyboard mnemonic. 154 * 155 * @param text the text of the <code>JMenuItem</code> 156 * @param mnemonic the keyboard mnemonic for the <code>JMenuItem</code> 157 */ 158 public JMenuItem(String text, int mnemonic) { 159 setModel(new DefaultButtonModel()); 160 init(text, null); 161 setMnemonic(mnemonic); 162 initFocusability(); 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 public void setModel(ButtonModel newModel) { 169 super.setModel(newModel); 170 if(newModel instanceof DefaultButtonModel) { 171 ((DefaultButtonModel)newModel).setMenuItem(true); 172 } 173 } 174 175 /** 176 * Inititalizes the focusability of the <code>JMenuItem</code>. 177 * <code>JMenuItem</code>'s are focusable, but subclasses may 178 * want to be, this provides them the opportunity to override this 179 * and invoke something else, or nothing at all. Refer to 180 * {@link javax.swing.JMenu#initFocusability} for the motivation of 181 * this. 182 */ 183 void initFocusability() { 184 setFocusable(false); 185 } 186 187 /** 188 * Initializes the menu item with the specified text and icon. 189 * 190 * @param text the text of the <code>JMenuItem</code> 191 * @param icon the icon of the <code>JMenuItem</code> 192 */ 193 protected void init(String text, Icon icon) { 194 if(text != null) { 195 setText(text); 196 } 197 198 if(icon != null) { 199 setIcon(icon); 200 } 201 202 // Listen for Focus events 203 addFocusListener(new MenuItemFocusListener()); 204 setUIProperty("borderPainted", Boolean.FALSE); 205 setFocusPainted(false); 206 setHorizontalTextPosition(JButton.TRAILING); 207 setHorizontalAlignment(JButton.LEADING); 208 updateUI(); 209 } 210 211 private static class MenuItemFocusListener implements FocusListener, 212 Serializable { 213 public void focusGained(FocusEvent event) {} 214 public void focusLost(FocusEvent event) { 215 // When focus is lost, repaint if 216 // the focus information is painted 217 JMenuItem mi = (JMenuItem)event.getSource(); 218 if(mi.isFocusPainted()) { 219 mi.repaint(); 220 } 221 } 222 } 223 224 225 /** 226 * Sets the look and feel object that renders this component. 227 * 228 * @param ui the <code>JMenuItemUI</code> L&F object 229 * @see UIDefaults#getUI 230 */ 231 @BeanProperty(hidden = true, visualUpdate = true, description 232 = "The UI object that implements the LookAndFeel.") 233 public void setUI(MenuItemUI ui) { 234 super.setUI(ui); 235 } 236 237 /** 238 * Resets the UI property with a value from the current look and feel. 239 * 240 * @see JComponent#updateUI 241 */ 242 public void updateUI() { 243 setUI((MenuItemUI)UIManager.getUI(this)); 244 } 245 246 247 /** 248 * Returns the suffix used to construct the name of the L&F class used to 249 * render this component. 250 * 251 * @return the string "MenuItemUI" 252 * @see JComponent#getUIClassID 253 * @see UIDefaults#getUI 254 */ 255 @BeanProperty(bound = false) 256 public String getUIClassID() { 257 return uiClassID; 258 } 259 260 261 /** 262 * Identifies the menu item as "armed". If the mouse button is 263 * released while it is over this item, the menu's action event 264 * will fire. If the mouse button is released elsewhere, the 265 * event will not fire and the menu item will be disarmed. 266 * 267 * @param b true to arm the menu item so it can be selected 268 */ 269 @BeanProperty(bound = false, hidden = true, description 270 = "Mouse release will fire an action event") 271 public void setArmed(boolean b) { 272 ButtonModel model = getModel(); 273 274 boolean oldValue = model.isArmed(); 275 if(model.isArmed() != b) { 276 model.setArmed(b); 277 } 278 } 279 280 /** 281 * Returns whether the menu item is "armed". 282 * 283 * @return true if the menu item is armed, and it can be selected 284 * @see #setArmed 285 */ 286 public boolean isArmed() { 287 ButtonModel model = getModel(); 288 return model.isArmed(); 289 } 290 291 /** 292 * Enables or disables the menu item. 293 * 294 * @param b true to enable the item 295 */ 296 @BeanProperty(preferred = true, description 297 = "The enabled state of the component.") 298 public void setEnabled(boolean b) { 299 // Make sure we aren't armed! 300 if (!b && !UIManager.getBoolean("MenuItem.disabledAreNavigable")) { 301 setArmed(false); 302 } 303 super.setEnabled(b); 304 } 305 306 307 /** 308 * Returns true since <code>Menu</code>s, by definition, 309 * should always be on top of all other windows. If the menu is 310 * in an internal frame false is returned due to the rollover effect 311 * for windows laf where the menu is not always on top. 312 */ 313 // package private 314 boolean alwaysOnTop() { 315 // Fix for bug #4482165 316 if (SwingUtilities.getAncestorOfClass(JInternalFrame.class, this) != 317 null) { 318 return false; 319 } 320 return true; 321 } 322 323 324 /* The keystroke which acts as the menu item's accelerator 325 */ 326 private KeyStroke accelerator; 327 328 /** 329 * Sets the key combination which invokes the menu item's 330 * action listeners without navigating the menu hierarchy. It is the 331 * UI's responsibility to install the correct action. Note that 332 * when the keyboard accelerator is typed, it will work whether or 333 * not the menu is currently displayed. 334 * 335 * @param keyStroke the <code>KeyStroke</code> which will 336 * serve as an accelerator 337 */ 338 @BeanProperty(preferred = true, description 339 = "The keystroke combination which will invoke the JMenuItem's actionlisteners without navigating the menu hierarchy") 340 public void setAccelerator(KeyStroke keyStroke) { 341 KeyStroke oldAccelerator = accelerator; 342 this.accelerator = keyStroke; 343 repaint(); 344 revalidate(); 345 firePropertyChange("accelerator", oldAccelerator, accelerator); 346 } 347 348 /** 349 * Returns the <code>KeyStroke</code> which serves as an accelerator 350 * for the menu item. 351 * @return a <code>KeyStroke</code> object identifying the 352 * accelerator key 353 */ 354 public KeyStroke getAccelerator() { 355 return this.accelerator; 356 } 357 358 /** 359 * {@inheritDoc} 360 * 361 * @since 1.3 362 */ 363 protected void configurePropertiesFromAction(Action a) { 364 super.configurePropertiesFromAction(a); 365 configureAcceleratorFromAction(a); 366 } 367 368 void setIconFromAction(Action a) { 369 Icon icon = null; 370 if (a != null) { 371 icon = (Icon)a.getValue(Action.SMALL_ICON); 372 } 373 setIcon(icon); 374 } 375 376 void largeIconChanged(Action a) { 377 } 378 379 void smallIconChanged(Action a) { 380 setIconFromAction(a); 381 } 382 383 void configureAcceleratorFromAction(Action a) { 384 KeyStroke ks = (a==null) ? null : 385 (KeyStroke)a.getValue(Action.ACCELERATOR_KEY); 386 setAccelerator(ks); 387 } 388 389 /** 390 * {@inheritDoc} 391 * @since 1.6 392 */ 393 protected void actionPropertyChanged(Action action, String propertyName) { 394 if (propertyName == Action.ACCELERATOR_KEY) { 395 configureAcceleratorFromAction(action); 396 } 397 else { 398 super.actionPropertyChanged(action, propertyName); 399 } 400 } 401 402 /** 403 * Processes a mouse event forwarded from the 404 * <code>MenuSelectionManager</code> and changes the menu 405 * selection, if necessary, by using the 406 * <code>MenuSelectionManager</code>'s API. 407 * <p> 408 * Note: you do not have to forward the event to sub-components. 409 * This is done automatically by the <code>MenuSelectionManager</code>. 410 * 411 * @param e a <code>MouseEvent</code> 412 * @param path the <code>MenuElement</code> path array 413 * @param manager the <code>MenuSelectionManager</code> 414 */ 415 public void processMouseEvent(MouseEvent e,MenuElement path[],MenuSelectionManager manager) { 416 processMenuDragMouseEvent( 417 new MenuDragMouseEvent(e.getComponent(), e.getID(), 418 e.getWhen(), 419 e.getModifiers(), e.getX(), e.getY(), 420 e.getXOnScreen(), e.getYOnScreen(), 421 e.getClickCount(), e.isPopupTrigger(), 422 path, manager)); 423 } 424 425 426 /** 427 * Processes a key event forwarded from the 428 * <code>MenuSelectionManager</code> and changes the menu selection, 429 * if necessary, by using <code>MenuSelectionManager</code>'s API. 430 * <p> 431 * Note: you do not have to forward the event to sub-components. 432 * This is done automatically by the <code>MenuSelectionManager</code>. 433 * 434 * @param e a <code>KeyEvent</code> 435 * @param path the <code>MenuElement</code> path array 436 * @param manager the <code>MenuSelectionManager</code> 437 */ 438 public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) { 439 if (DEBUG) { 440 System.out.println("in JMenuItem.processKeyEvent/3 for " + getText() + 441 " " + KeyStroke.getKeyStrokeForEvent(e)); 442 } 443 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(), 444 e.getWhen(), e.getModifiers(), 445 e.getKeyCode(), e.getKeyChar(), 446 path, manager); 447 processMenuKeyEvent(mke); 448 449 if (mke.isConsumed()) { 450 e.consume(); 451 } 452 } 453 454 455 456 /** 457 * Handles mouse drag in a menu. 458 * 459 * @param e a <code>MenuDragMouseEvent</code> object 460 */ 461 public void processMenuDragMouseEvent(MenuDragMouseEvent e) { 462 switch (e.getID()) { 463 case MouseEvent.MOUSE_ENTERED: 464 isMouseDragged = false; fireMenuDragMouseEntered(e); break; 465 case MouseEvent.MOUSE_EXITED: 466 isMouseDragged = false; fireMenuDragMouseExited(e); break; 467 case MouseEvent.MOUSE_DRAGGED: 468 isMouseDragged = true; fireMenuDragMouseDragged(e); break; 469 case MouseEvent.MOUSE_RELEASED: 470 if(isMouseDragged) fireMenuDragMouseReleased(e); break; 471 default: 472 break; 473 } 474 } 475 476 /** 477 * Handles a keystroke in a menu. 478 * 479 * @param e a <code>MenuKeyEvent</code> object 480 */ 481 public void processMenuKeyEvent(MenuKeyEvent e) { 482 if (DEBUG) { 483 System.out.println("in JMenuItem.processMenuKeyEvent for " + getText()+ 484 " " + KeyStroke.getKeyStrokeForEvent(e)); 485 } 486 switch (e.getID()) { 487 case KeyEvent.KEY_PRESSED: 488 fireMenuKeyPressed(e); break; 489 case KeyEvent.KEY_RELEASED: 490 fireMenuKeyReleased(e); break; 491 case KeyEvent.KEY_TYPED: 492 fireMenuKeyTyped(e); break; 493 default: 494 break; 495 } 496 } 497 498 /** 499 * Notifies all listeners that have registered interest for 500 * notification on this event type. 501 * 502 * @param event a <code>MenuMouseDragEvent</code> 503 * @see EventListenerList 504 */ 505 protected void fireMenuDragMouseEntered(MenuDragMouseEvent event) { 506 // Guaranteed to return a non-null array 507 Object[] listeners = listenerList.getListenerList(); 508 // Process the listeners last to first, notifying 509 // those that are interested in this event 510 for (int i = listeners.length-2; i>=0; i-=2) { 511 if (listeners[i]==MenuDragMouseListener.class) { 512 // Lazily create the event: 513 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseEntered(event); 514 } 515 } 516 } 517 518 /** 519 * Notifies all listeners that have registered interest for 520 * notification on this event type. 521 * 522 * @param event a <code>MenuDragMouseEvent</code> 523 * @see EventListenerList 524 */ 525 protected void fireMenuDragMouseExited(MenuDragMouseEvent event) { 526 // Guaranteed to return a non-null array 527 Object[] listeners = listenerList.getListenerList(); 528 // Process the listeners last to first, notifying 529 // those that are interested in this event 530 for (int i = listeners.length-2; i>=0; i-=2) { 531 if (listeners[i]==MenuDragMouseListener.class) { 532 // Lazily create the event: 533 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseExited(event); 534 } 535 } 536 } 537 538 /** 539 * Notifies all listeners that have registered interest for 540 * notification on this event type. 541 * 542 * @param event a <code>MenuDragMouseEvent</code> 543 * @see EventListenerList 544 */ 545 protected void fireMenuDragMouseDragged(MenuDragMouseEvent event) { 546 // Guaranteed to return a non-null array 547 Object[] listeners = listenerList.getListenerList(); 548 // Process the listeners last to first, notifying 549 // those that are interested in this event 550 for (int i = listeners.length-2; i>=0; i-=2) { 551 if (listeners[i]==MenuDragMouseListener.class) { 552 // Lazily create the event: 553 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseDragged(event); 554 } 555 } 556 } 557 558 /** 559 * Notifies all listeners that have registered interest for 560 * notification on this event type. 561 * 562 * @param event a <code>MenuDragMouseEvent</code> 563 * @see EventListenerList 564 */ 565 protected void fireMenuDragMouseReleased(MenuDragMouseEvent event) { 566 // Guaranteed to return a non-null array 567 Object[] listeners = listenerList.getListenerList(); 568 // Process the listeners last to first, notifying 569 // those that are interested in this event 570 for (int i = listeners.length-2; i>=0; i-=2) { 571 if (listeners[i]==MenuDragMouseListener.class) { 572 // Lazily create the event: 573 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseReleased(event); 574 } 575 } 576 } 577 578 /** 579 * Notifies all listeners that have registered interest for 580 * notification on this event type. 581 * 582 * @param event a <code>MenuKeyEvent</code> 583 * @see EventListenerList 584 */ 585 protected void fireMenuKeyPressed(MenuKeyEvent event) { 586 if (DEBUG) { 587 System.out.println("in JMenuItem.fireMenuKeyPressed for " + getText()+ 588 " " + KeyStroke.getKeyStrokeForEvent(event)); 589 } 590 // Guaranteed to return a non-null array 591 Object[] listeners = listenerList.getListenerList(); 592 // Process the listeners last to first, notifying 593 // those that are interested in this event 594 for (int i = listeners.length-2; i>=0; i-=2) { 595 if (listeners[i]==MenuKeyListener.class) { 596 // Lazily create the event: 597 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event); 598 } 599 } 600 } 601 602 /** 603 * Notifies all listeners that have registered interest for 604 * notification on this event type. 605 * 606 * @param event a <code>MenuKeyEvent</code> 607 * @see EventListenerList 608 */ 609 protected void fireMenuKeyReleased(MenuKeyEvent event) { 610 if (DEBUG) { 611 System.out.println("in JMenuItem.fireMenuKeyReleased for " + getText()+ 612 " " + KeyStroke.getKeyStrokeForEvent(event)); 613 } 614 // Guaranteed to return a non-null array 615 Object[] listeners = listenerList.getListenerList(); 616 // Process the listeners last to first, notifying 617 // those that are interested in this event 618 for (int i = listeners.length-2; i>=0; i-=2) { 619 if (listeners[i]==MenuKeyListener.class) { 620 // Lazily create the event: 621 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event); 622 } 623 } 624 } 625 626 /** 627 * Notifies all listeners that have registered interest for 628 * notification on this event type. 629 * 630 * @param event a <code>MenuKeyEvent</code> 631 * @see EventListenerList 632 */ 633 protected void fireMenuKeyTyped(MenuKeyEvent event) { 634 if (DEBUG) { 635 System.out.println("in JMenuItem.fireMenuKeyTyped for " + getText()+ 636 " " + KeyStroke.getKeyStrokeForEvent(event)); 637 } 638 // Guaranteed to return a non-null array 639 Object[] listeners = listenerList.getListenerList(); 640 // Process the listeners last to first, notifying 641 // those that are interested in this event 642 for (int i = listeners.length-2; i>=0; i-=2) { 643 if (listeners[i]==MenuKeyListener.class) { 644 // Lazily create the event: 645 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event); 646 } 647 } 648 } 649 650 /** 651 * Called by the <code>MenuSelectionManager</code> when the 652 * <code>MenuElement</code> is selected or unselected. 653 * 654 * @param isIncluded true if this menu item is on the part of the menu 655 * path that changed, false if this menu is part of the 656 * a menu path that changed, but this particular part of 657 * that path is still the same 658 * @see MenuSelectionManager#setSelectedPath(MenuElement[]) 659 */ 660 public void menuSelectionChanged(boolean isIncluded) { 661 setArmed(isIncluded); 662 } 663 664 /** 665 * This method returns an array containing the sub-menu 666 * components for this menu component. 667 * 668 * @return an array of <code>MenuElement</code>s 669 */ 670 @BeanProperty(bound = false) 671 public MenuElement[] getSubElements() { 672 return new MenuElement[0]; 673 } 674 675 /** 676 * Returns the <code>java.awt.Component</code> used to paint 677 * this object. The returned component will be used to convert 678 * events and detect if an event is inside a menu component. 679 * 680 * @return the <code>Component</code> that paints this menu item 681 */ 682 public Component getComponent() { 683 return this; 684 } 685 686 /** 687 * Adds a <code>MenuDragMouseListener</code> to the menu item. 688 * 689 * @param l the <code>MenuDragMouseListener</code> to be added 690 */ 691 public void addMenuDragMouseListener(MenuDragMouseListener l) { 692 listenerList.add(MenuDragMouseListener.class, l); 693 } 694 695 /** 696 * Removes a <code>MenuDragMouseListener</code> from the menu item. 697 * 698 * @param l the <code>MenuDragMouseListener</code> to be removed 699 */ 700 public void removeMenuDragMouseListener(MenuDragMouseListener l) { 701 listenerList.remove(MenuDragMouseListener.class, l); 702 } 703 704 /** 705 * Returns an array of all the <code>MenuDragMouseListener</code>s added 706 * to this JMenuItem with addMenuDragMouseListener(). 707 * 708 * @return all of the <code>MenuDragMouseListener</code>s added or an empty 709 * array if no listeners have been added 710 * @since 1.4 711 */ 712 @BeanProperty(bound = false) 713 public MenuDragMouseListener[] getMenuDragMouseListeners() { 714 return listenerList.getListeners(MenuDragMouseListener.class); 715 } 716 717 /** 718 * Adds a <code>MenuKeyListener</code> to the menu item. 719 * 720 * @param l the <code>MenuKeyListener</code> to be added 721 */ 722 public void addMenuKeyListener(MenuKeyListener l) { 723 listenerList.add(MenuKeyListener.class, l); 724 } 725 726 /** 727 * Removes a <code>MenuKeyListener</code> from the menu item. 728 * 729 * @param l the <code>MenuKeyListener</code> to be removed 730 */ 731 public void removeMenuKeyListener(MenuKeyListener l) { 732 listenerList.remove(MenuKeyListener.class, l); 733 } 734 735 /** 736 * Returns an array of all the <code>MenuKeyListener</code>s added 737 * to this JMenuItem with addMenuKeyListener(). 738 * 739 * @return all of the <code>MenuKeyListener</code>s added or an empty 740 * array if no listeners have been added 741 * @since 1.4 742 */ 743 @BeanProperty(bound = false) 744 public MenuKeyListener[] getMenuKeyListeners() { 745 return listenerList.getListeners(MenuKeyListener.class); 746 } 747 748 /** 749 * See JComponent.readObject() for information about serialization 750 * in Swing. 751 */ 752 private void readObject(ObjectInputStream s) 753 throws IOException, ClassNotFoundException 754 { 755 s.defaultReadObject(); 756 if (getUIClassID().equals(uiClassID)) { 757 updateUI(); 758 } 759 } 760 761 private void writeObject(ObjectOutputStream s) throws IOException { 762 s.defaultWriteObject(); 763 if (getUIClassID().equals(uiClassID)) { 764 byte count = JComponent.getWriteObjCounter(this); 765 JComponent.setWriteObjCounter(this, --count); 766 if (count == 0 && ui != null) { 767 ui.installUI(this); 768 } 769 } 770 } 771 772 773 /** 774 * Returns a string representation of this <code>JMenuItem</code>. 775 * This method is intended to be used only for debugging purposes, 776 * and the content and format of the returned string may vary between 777 * implementations. The returned string may be empty but may not 778 * be <code>null</code>. 779 * 780 * @return a string representation of this <code>JMenuItem</code> 781 */ 782 protected String paramString() { 783 return super.paramString(); 784 } 785 786 ///////////////// 787 // Accessibility support 788 //////////////// 789 790 /** 791 * Returns the <code>AccessibleContext</code> associated with this 792 * <code>JMenuItem</code>. For <code>JMenuItem</code>s, 793 * the <code>AccessibleContext</code> takes the form of an 794 * <code>AccessibleJMenuItem</code>. 795 * A new AccessibleJMenuItme instance is created if necessary. 796 * 797 * @return an <code>AccessibleJMenuItem</code> that serves as the 798 * <code>AccessibleContext</code> of this <code>JMenuItem</code> 799 */ 800 @BeanProperty(bound = false) 801 public AccessibleContext getAccessibleContext() { 802 if (accessibleContext == null) { 803 accessibleContext = new AccessibleJMenuItem(); 804 } 805 return accessibleContext; 806 } 807 808 809 /** 810 * This class implements accessibility support for the 811 * <code>JMenuItem</code> class. It provides an implementation of the 812 * Java Accessibility API appropriate to menu item user-interface 813 * elements. 814 * <p> 815 * <strong>Warning:</strong> 816 * Serialized objects of this class will not be compatible with 817 * future Swing releases. The current serialization support is 818 * appropriate for short term storage or RMI between applications running 819 * the same version of Swing. As of 1.4, support for long term storage 820 * of all JavaBeans™ 821 * has been added to the <code>java.beans</code> package. 822 * Please see {@link java.beans.XMLEncoder}. 823 */ 824 @SuppressWarnings("serial") 825 protected class AccessibleJMenuItem extends AccessibleAbstractButton implements ChangeListener { 826 827 private boolean isArmed = false; 828 private boolean hasFocus = false; 829 private boolean isPressed = false; 830 private boolean isSelected = false; 831 832 AccessibleJMenuItem() { 833 super(); 834 JMenuItem.this.addChangeListener(this); 835 } 836 837 /** 838 * Get the role of this object. 839 * 840 * @return an instance of AccessibleRole describing the role of the 841 * object 842 */ 843 public AccessibleRole getAccessibleRole() { 844 return AccessibleRole.MENU_ITEM; 845 } 846 847 private void fireAccessibilityFocusedEvent(JMenuItem toCheck) { 848 MenuElement [] path = 849 MenuSelectionManager.defaultManager().getSelectedPath(); 850 if (path.length > 0) { 851 Object menuItem = path[path.length - 1]; 852 if (toCheck == menuItem) { 853 firePropertyChange( 854 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 855 null, AccessibleState.FOCUSED); 856 } 857 } 858 } 859 860 /** 861 * Supports the change listener interface and fires property changes. 862 */ 863 public void stateChanged(ChangeEvent e) { 864 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 865 Boolean.valueOf(false), Boolean.valueOf(true)); 866 if (JMenuItem.this.getModel().isArmed()) { 867 if (!isArmed) { 868 isArmed = true; 869 firePropertyChange( 870 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 871 null, AccessibleState.ARMED); 872 // Fix for 4848220 moved here to avoid major memory leak 873 // Here we will fire the event in case of JMenuItem 874 // See bug 4910323 for details [zav] 875 fireAccessibilityFocusedEvent(JMenuItem.this); 876 } 877 } else { 878 if (isArmed) { 879 isArmed = false; 880 firePropertyChange( 881 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 882 AccessibleState.ARMED, null); 883 } 884 } 885 if (JMenuItem.this.isFocusOwner()) { 886 if (!hasFocus) { 887 hasFocus = true; 888 firePropertyChange( 889 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 890 null, AccessibleState.FOCUSED); 891 } 892 } else { 893 if (hasFocus) { 894 hasFocus = false; 895 firePropertyChange( 896 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 897 AccessibleState.FOCUSED, null); 898 } 899 } 900 if (JMenuItem.this.getModel().isPressed()) { 901 if (!isPressed) { 902 isPressed = true; 903 firePropertyChange( 904 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 905 null, AccessibleState.PRESSED); 906 } 907 } else { 908 if (isPressed) { 909 isPressed = false; 910 firePropertyChange( 911 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 912 AccessibleState.PRESSED, null); 913 } 914 } 915 if (JMenuItem.this.getModel().isSelected()) { 916 if (!isSelected) { 917 isSelected = true; 918 firePropertyChange( 919 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 920 null, AccessibleState.CHECKED); 921 922 // Fix for 4848220 moved here to avoid major memory leak 923 // Here we will fire the event in case of JMenu 924 // See bug 4910323 for details [zav] 925 fireAccessibilityFocusedEvent(JMenuItem.this); 926 } 927 } else { 928 if (isSelected) { 929 isSelected = false; 930 firePropertyChange( 931 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 932 AccessibleState.CHECKED, null); 933 } 934 } 935 936 } 937 } // inner class AccessibleJMenuItem 938 939 940 }