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