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