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 */ JMenuItem()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 */ JMenuItem(Icon icon)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 */ JMenuItem(String text)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 */ JMenuItem(Action a)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 */ JMenuItem(String text, Icon icon)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 */ JMenuItem(String text, int mnemonic)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 */ setModel(ButtonModel newModel)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 */ initFocusability()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 */ init(String text, Icon icon)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 { focusGained(FocusEvent event)213 public void focusGained(FocusEvent event) {} focusLost(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.") setUI(MenuItemUI ui)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 */ updateUI()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) getUIClassID()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") setArmed(boolean b)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 */ isArmed()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.") setEnabled(boolean b)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 alwaysOnTop()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") setAccelerator(KeyStroke keyStroke)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 */ getAccelerator()354 public KeyStroke getAccelerator() { 355 return this.accelerator; 356 } 357 358 /** 359 * {@inheritDoc} 360 * 361 * @since 1.3 362 */ configurePropertiesFromAction(Action a)363 protected void configurePropertiesFromAction(Action a) { 364 super.configurePropertiesFromAction(a); 365 configureAcceleratorFromAction(a); 366 } 367 setIconFromAction(Action a)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 largeIconChanged(Action a)376 void largeIconChanged(Action a) { 377 } 378 smallIconChanged(Action a)379 void smallIconChanged(Action a) { 380 setIconFromAction(a); 381 } 382 configureAcceleratorFromAction(Action a)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 */ actionPropertyChanged(Action action, String propertyName)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 @SuppressWarnings("deprecation") processMouseEvent(MouseEvent e,MenuElement path[],MenuSelectionManager manager)416 public void processMouseEvent(MouseEvent e,MenuElement path[],MenuSelectionManager manager) { 417 processMenuDragMouseEvent( 418 new MenuDragMouseEvent(e.getComponent(), e.getID(), 419 e.getWhen(), 420 e.getModifiers(), e.getX(), e.getY(), 421 e.getXOnScreen(), e.getYOnScreen(), 422 e.getClickCount(), e.isPopupTrigger(), 423 path, manager)); 424 } 425 426 427 /** 428 * Processes a key event forwarded from the 429 * <code>MenuSelectionManager</code> and changes the menu selection, 430 * if necessary, by using <code>MenuSelectionManager</code>'s API. 431 * <p> 432 * Note: you do not have to forward the event to sub-components. 433 * This is done automatically by the <code>MenuSelectionManager</code>. 434 * 435 * @param e a <code>KeyEvent</code> 436 * @param path the <code>MenuElement</code> path array 437 * @param manager the <code>MenuSelectionManager</code> 438 */ 439 @SuppressWarnings("deprecation") processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager)440 public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) { 441 if (DEBUG) { 442 System.out.println("in JMenuItem.processKeyEvent/3 for " + getText() + 443 " " + KeyStroke.getKeyStrokeForEvent(e)); 444 } 445 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(), 446 e.getWhen(), e.getModifiers(), 447 e.getKeyCode(), e.getKeyChar(), 448 path, manager); 449 processMenuKeyEvent(mke); 450 451 if (mke.isConsumed()) { 452 e.consume(); 453 } 454 } 455 456 457 458 /** 459 * Handles mouse drag in a menu. 460 * 461 * @param e a <code>MenuDragMouseEvent</code> object 462 */ processMenuDragMouseEvent(MenuDragMouseEvent e)463 public void processMenuDragMouseEvent(MenuDragMouseEvent e) { 464 switch (e.getID()) { 465 case MouseEvent.MOUSE_ENTERED: 466 isMouseDragged = false; fireMenuDragMouseEntered(e); break; 467 case MouseEvent.MOUSE_EXITED: 468 isMouseDragged = false; fireMenuDragMouseExited(e); break; 469 case MouseEvent.MOUSE_DRAGGED: 470 isMouseDragged = true; fireMenuDragMouseDragged(e); break; 471 case MouseEvent.MOUSE_RELEASED: 472 if(isMouseDragged) fireMenuDragMouseReleased(e); break; 473 default: 474 break; 475 } 476 } 477 478 /** 479 * Handles a keystroke in a menu. 480 * 481 * @param e a <code>MenuKeyEvent</code> object 482 */ processMenuKeyEvent(MenuKeyEvent e)483 public void processMenuKeyEvent(MenuKeyEvent e) { 484 if (DEBUG) { 485 System.out.println("in JMenuItem.processMenuKeyEvent for " + getText()+ 486 " " + KeyStroke.getKeyStrokeForEvent(e)); 487 } 488 switch (e.getID()) { 489 case KeyEvent.KEY_PRESSED: 490 fireMenuKeyPressed(e); break; 491 case KeyEvent.KEY_RELEASED: 492 fireMenuKeyReleased(e); break; 493 case KeyEvent.KEY_TYPED: 494 fireMenuKeyTyped(e); break; 495 default: 496 break; 497 } 498 } 499 500 /** 501 * Notifies all listeners that have registered interest for 502 * notification on this event type. 503 * 504 * @param event a <code>MenuMouseDragEvent</code> 505 * @see EventListenerList 506 */ fireMenuDragMouseEntered(MenuDragMouseEvent event)507 protected void fireMenuDragMouseEntered(MenuDragMouseEvent event) { 508 // Guaranteed to return a non-null array 509 Object[] listeners = listenerList.getListenerList(); 510 // Process the listeners last to first, notifying 511 // those that are interested in this event 512 for (int i = listeners.length-2; i>=0; i-=2) { 513 if (listeners[i]==MenuDragMouseListener.class) { 514 // Lazily create the event: 515 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseEntered(event); 516 } 517 } 518 } 519 520 /** 521 * Notifies all listeners that have registered interest for 522 * notification on this event type. 523 * 524 * @param event a <code>MenuDragMouseEvent</code> 525 * @see EventListenerList 526 */ fireMenuDragMouseExited(MenuDragMouseEvent event)527 protected void fireMenuDragMouseExited(MenuDragMouseEvent event) { 528 // Guaranteed to return a non-null array 529 Object[] listeners = listenerList.getListenerList(); 530 // Process the listeners last to first, notifying 531 // those that are interested in this event 532 for (int i = listeners.length-2; i>=0; i-=2) { 533 if (listeners[i]==MenuDragMouseListener.class) { 534 // Lazily create the event: 535 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseExited(event); 536 } 537 } 538 } 539 540 /** 541 * Notifies all listeners that have registered interest for 542 * notification on this event type. 543 * 544 * @param event a <code>MenuDragMouseEvent</code> 545 * @see EventListenerList 546 */ fireMenuDragMouseDragged(MenuDragMouseEvent event)547 protected void fireMenuDragMouseDragged(MenuDragMouseEvent event) { 548 // Guaranteed to return a non-null array 549 Object[] listeners = listenerList.getListenerList(); 550 // Process the listeners last to first, notifying 551 // those that are interested in this event 552 for (int i = listeners.length-2; i>=0; i-=2) { 553 if (listeners[i]==MenuDragMouseListener.class) { 554 // Lazily create the event: 555 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseDragged(event); 556 } 557 } 558 } 559 560 /** 561 * Notifies all listeners that have registered interest for 562 * notification on this event type. 563 * 564 * @param event a <code>MenuDragMouseEvent</code> 565 * @see EventListenerList 566 */ fireMenuDragMouseReleased(MenuDragMouseEvent event)567 protected void fireMenuDragMouseReleased(MenuDragMouseEvent event) { 568 // Guaranteed to return a non-null array 569 Object[] listeners = listenerList.getListenerList(); 570 // Process the listeners last to first, notifying 571 // those that are interested in this event 572 for (int i = listeners.length-2; i>=0; i-=2) { 573 if (listeners[i]==MenuDragMouseListener.class) { 574 // Lazily create the event: 575 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseReleased(event); 576 } 577 } 578 } 579 580 /** 581 * Notifies all listeners that have registered interest for 582 * notification on this event type. 583 * 584 * @param event a <code>MenuKeyEvent</code> 585 * @see EventListenerList 586 */ fireMenuKeyPressed(MenuKeyEvent event)587 protected void fireMenuKeyPressed(MenuKeyEvent event) { 588 if (DEBUG) { 589 System.out.println("in JMenuItem.fireMenuKeyPressed for " + getText()+ 590 " " + KeyStroke.getKeyStrokeForEvent(event)); 591 } 592 // Guaranteed to return a non-null array 593 Object[] listeners = listenerList.getListenerList(); 594 // Process the listeners last to first, notifying 595 // those that are interested in this event 596 for (int i = listeners.length-2; i>=0; i-=2) { 597 if (listeners[i]==MenuKeyListener.class) { 598 // Lazily create the event: 599 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event); 600 } 601 } 602 } 603 604 /** 605 * Notifies all listeners that have registered interest for 606 * notification on this event type. 607 * 608 * @param event a <code>MenuKeyEvent</code> 609 * @see EventListenerList 610 */ fireMenuKeyReleased(MenuKeyEvent event)611 protected void fireMenuKeyReleased(MenuKeyEvent event) { 612 if (DEBUG) { 613 System.out.println("in JMenuItem.fireMenuKeyReleased for " + getText()+ 614 " " + KeyStroke.getKeyStrokeForEvent(event)); 615 } 616 // Guaranteed to return a non-null array 617 Object[] listeners = listenerList.getListenerList(); 618 // Process the listeners last to first, notifying 619 // those that are interested in this event 620 for (int i = listeners.length-2; i>=0; i-=2) { 621 if (listeners[i]==MenuKeyListener.class) { 622 // Lazily create the event: 623 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event); 624 } 625 } 626 } 627 628 /** 629 * Notifies all listeners that have registered interest for 630 * notification on this event type. 631 * 632 * @param event a <code>MenuKeyEvent</code> 633 * @see EventListenerList 634 */ fireMenuKeyTyped(MenuKeyEvent event)635 protected void fireMenuKeyTyped(MenuKeyEvent event) { 636 if (DEBUG) { 637 System.out.println("in JMenuItem.fireMenuKeyTyped for " + getText()+ 638 " " + KeyStroke.getKeyStrokeForEvent(event)); 639 } 640 // Guaranteed to return a non-null array 641 Object[] listeners = listenerList.getListenerList(); 642 // Process the listeners last to first, notifying 643 // those that are interested in this event 644 for (int i = listeners.length-2; i>=0; i-=2) { 645 if (listeners[i]==MenuKeyListener.class) { 646 // Lazily create the event: 647 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event); 648 } 649 } 650 } 651 652 /** 653 * Called by the <code>MenuSelectionManager</code> when the 654 * <code>MenuElement</code> is selected or unselected. 655 * 656 * @param isIncluded true if this menu item is on the part of the menu 657 * path that changed, false if this menu is part of the 658 * a menu path that changed, but this particular part of 659 * that path is still the same 660 * @see MenuSelectionManager#setSelectedPath(MenuElement[]) 661 */ menuSelectionChanged(boolean isIncluded)662 public void menuSelectionChanged(boolean isIncluded) { 663 setArmed(isIncluded); 664 } 665 666 /** 667 * This method returns an array containing the sub-menu 668 * components for this menu component. 669 * 670 * @return an array of <code>MenuElement</code>s 671 */ 672 @BeanProperty(bound = false) getSubElements()673 public MenuElement[] getSubElements() { 674 return new MenuElement[0]; 675 } 676 677 /** 678 * Returns the <code>java.awt.Component</code> used to paint 679 * this object. The returned component will be used to convert 680 * events and detect if an event is inside a menu component. 681 * 682 * @return the <code>Component</code> that paints this menu item 683 */ getComponent()684 public Component getComponent() { 685 return this; 686 } 687 688 /** 689 * Adds a <code>MenuDragMouseListener</code> to the menu item. 690 * 691 * @param l the <code>MenuDragMouseListener</code> to be added 692 */ addMenuDragMouseListener(MenuDragMouseListener l)693 public void addMenuDragMouseListener(MenuDragMouseListener l) { 694 listenerList.add(MenuDragMouseListener.class, l); 695 } 696 697 /** 698 * Removes a <code>MenuDragMouseListener</code> from the menu item. 699 * 700 * @param l the <code>MenuDragMouseListener</code> to be removed 701 */ removeMenuDragMouseListener(MenuDragMouseListener l)702 public void removeMenuDragMouseListener(MenuDragMouseListener l) { 703 listenerList.remove(MenuDragMouseListener.class, l); 704 } 705 706 /** 707 * Returns an array of all the <code>MenuDragMouseListener</code>s added 708 * to this JMenuItem with addMenuDragMouseListener(). 709 * 710 * @return all of the <code>MenuDragMouseListener</code>s added or an empty 711 * array if no listeners have been added 712 * @since 1.4 713 */ 714 @BeanProperty(bound = false) getMenuDragMouseListeners()715 public MenuDragMouseListener[] getMenuDragMouseListeners() { 716 return listenerList.getListeners(MenuDragMouseListener.class); 717 } 718 719 /** 720 * Adds a <code>MenuKeyListener</code> to the menu item. 721 * 722 * @param l the <code>MenuKeyListener</code> to be added 723 */ addMenuKeyListener(MenuKeyListener l)724 public void addMenuKeyListener(MenuKeyListener l) { 725 listenerList.add(MenuKeyListener.class, l); 726 } 727 728 /** 729 * Removes a <code>MenuKeyListener</code> from the menu item. 730 * 731 * @param l the <code>MenuKeyListener</code> to be removed 732 */ removeMenuKeyListener(MenuKeyListener l)733 public void removeMenuKeyListener(MenuKeyListener l) { 734 listenerList.remove(MenuKeyListener.class, l); 735 } 736 737 /** 738 * Returns an array of all the <code>MenuKeyListener</code>s added 739 * to this JMenuItem with addMenuKeyListener(). 740 * 741 * @return all of the <code>MenuKeyListener</code>s added or an empty 742 * array if no listeners have been added 743 * @since 1.4 744 */ 745 @BeanProperty(bound = false) getMenuKeyListeners()746 public MenuKeyListener[] getMenuKeyListeners() { 747 return listenerList.getListeners(MenuKeyListener.class); 748 } 749 750 /** 751 * See JComponent.readObject() for information about serialization 752 * in Swing. 753 */ readObject(ObjectInputStream s)754 private void readObject(ObjectInputStream s) 755 throws IOException, ClassNotFoundException 756 { 757 s.defaultReadObject(); 758 if (getUIClassID().equals(uiClassID)) { 759 updateUI(); 760 } 761 } 762 writeObject(ObjectOutputStream s)763 private void writeObject(ObjectOutputStream s) throws IOException { 764 s.defaultWriteObject(); 765 if (getUIClassID().equals(uiClassID)) { 766 byte count = JComponent.getWriteObjCounter(this); 767 JComponent.setWriteObjCounter(this, --count); 768 if (count == 0 && ui != null) { 769 ui.installUI(this); 770 } 771 } 772 } 773 774 775 /** 776 * Returns a string representation of this <code>JMenuItem</code>. 777 * This method is intended to be used only for debugging purposes, 778 * and the content and format of the returned string may vary between 779 * implementations. The returned string may be empty but may not 780 * be <code>null</code>. 781 * 782 * @return a string representation of this <code>JMenuItem</code> 783 */ paramString()784 protected String paramString() { 785 return super.paramString(); 786 } 787 788 ///////////////// 789 // Accessibility support 790 //////////////// 791 792 /** 793 * Returns the <code>AccessibleContext</code> associated with this 794 * <code>JMenuItem</code>. For <code>JMenuItem</code>s, 795 * the <code>AccessibleContext</code> takes the form of an 796 * <code>AccessibleJMenuItem</code>. 797 * A new AccessibleJMenuItme instance is created if necessary. 798 * 799 * @return an <code>AccessibleJMenuItem</code> that serves as the 800 * <code>AccessibleContext</code> of this <code>JMenuItem</code> 801 */ 802 @BeanProperty(bound = false) getAccessibleContext()803 public AccessibleContext getAccessibleContext() { 804 if (accessibleContext == null) { 805 accessibleContext = new AccessibleJMenuItem(); 806 } 807 return accessibleContext; 808 } 809 810 811 /** 812 * This class implements accessibility support for the 813 * <code>JMenuItem</code> class. It provides an implementation of the 814 * Java Accessibility API appropriate to menu item user-interface 815 * elements. 816 * <p> 817 * <strong>Warning:</strong> 818 * Serialized objects of this class will not be compatible with 819 * future Swing releases. The current serialization support is 820 * appropriate for short term storage or RMI between applications running 821 * the same version of Swing. As of 1.4, support for long term storage 822 * of all JavaBeans™ 823 * has been added to the <code>java.beans</code> package. 824 * Please see {@link java.beans.XMLEncoder}. 825 */ 826 @SuppressWarnings("serial") 827 protected class AccessibleJMenuItem extends AccessibleAbstractButton implements ChangeListener { 828 829 private boolean isArmed = false; 830 private boolean hasFocus = false; 831 private boolean isPressed = false; 832 private boolean isSelected = false; 833 AccessibleJMenuItem()834 AccessibleJMenuItem() { 835 super(); 836 JMenuItem.this.addChangeListener(this); 837 } 838 839 /** 840 * Get the role of this object. 841 * 842 * @return an instance of AccessibleRole describing the role of the 843 * object 844 */ getAccessibleRole()845 public AccessibleRole getAccessibleRole() { 846 return AccessibleRole.MENU_ITEM; 847 } 848 fireAccessibilityFocusedEvent(JMenuItem toCheck)849 private void fireAccessibilityFocusedEvent(JMenuItem toCheck) { 850 MenuElement [] path = 851 MenuSelectionManager.defaultManager().getSelectedPath(); 852 if (path.length > 0) { 853 Object menuItem = path[path.length - 1]; 854 if (toCheck == menuItem) { 855 firePropertyChange( 856 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 857 null, AccessibleState.FOCUSED); 858 } 859 } 860 } 861 862 /** 863 * Supports the change listener interface and fires property changes. 864 */ stateChanged(ChangeEvent e)865 public void stateChanged(ChangeEvent e) { 866 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 867 Boolean.valueOf(false), Boolean.valueOf(true)); 868 if (JMenuItem.this.getModel().isArmed()) { 869 if (!isArmed) { 870 isArmed = true; 871 firePropertyChange( 872 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 873 null, AccessibleState.ARMED); 874 // Fix for 4848220 moved here to avoid major memory leak 875 // Here we will fire the event in case of JMenuItem 876 // See bug 4910323 for details [zav] 877 fireAccessibilityFocusedEvent(JMenuItem.this); 878 } 879 } else { 880 if (isArmed) { 881 isArmed = false; 882 firePropertyChange( 883 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 884 AccessibleState.ARMED, null); 885 } 886 } 887 if (JMenuItem.this.isFocusOwner()) { 888 if (!hasFocus) { 889 hasFocus = true; 890 firePropertyChange( 891 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 892 null, AccessibleState.FOCUSED); 893 } 894 } else { 895 if (hasFocus) { 896 hasFocus = false; 897 firePropertyChange( 898 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 899 AccessibleState.FOCUSED, null); 900 } 901 } 902 if (JMenuItem.this.getModel().isPressed()) { 903 if (!isPressed) { 904 isPressed = true; 905 firePropertyChange( 906 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 907 null, AccessibleState.PRESSED); 908 } 909 } else { 910 if (isPressed) { 911 isPressed = false; 912 firePropertyChange( 913 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 914 AccessibleState.PRESSED, null); 915 } 916 } 917 if (JMenuItem.this.getModel().isSelected()) { 918 if (!isSelected) { 919 isSelected = true; 920 firePropertyChange( 921 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 922 null, AccessibleState.CHECKED); 923 924 // Fix for 4848220 moved here to avoid major memory leak 925 // Here we will fire the event in case of JMenu 926 // See bug 4910323 for details [zav] 927 fireAccessibilityFocusedEvent(JMenuItem.this); 928 } 929 } else { 930 if (isSelected) { 931 isSelected = false; 932 firePropertyChange( 933 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 934 AccessibleState.CHECKED, null); 935 } 936 } 937 938 } 939 } // inner class AccessibleJMenuItem 940 941 942 } 943