001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import java.util.EventListener;
028 import java.awt.*;
029 import java.awt.event.*;
030 import java.awt.image.*;
031
032 import java.beans.PropertyChangeEvent;
033 import java.beans.PropertyChangeListener;
034
035 import java.io.Serializable;
036 import java.io.ObjectOutputStream;
037 import java.io.ObjectInputStream;
038 import java.io.IOException;
039
040 import javax.swing.plaf.*;
041 import javax.swing.plaf.basic.*;
042 import javax.swing.event.*;
043 import javax.accessibility.*;
044
045 /**
046 * An implementation of an item in a menu. A menu item is essentially a button
047 * sitting in a list. When the user selects the "button", the action
048 * associated with the menu item is performed. A <code>JMenuItem</code>
049 * contained in a <code>JPopupMenu</code> performs exactly that function.
050 * <p>
051 * Menu items can be configured, and to some degree controlled, by
052 * <code><a href="Action.html">Action</a></code>s. Using an
053 * <code>Action</code> with a menu item has many benefits beyond directly
054 * configuring a menu item. Refer to <a href="Action.html#buttonActions">
055 * Swing Components Supporting <code>Action</code></a> for more
056 * details, and you can find more information in <a
057 * href="http://java.sun.com/docs/books/tutorial/uiswing/misc/action.html">How
058 * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
059 * <p>
060 * For further documentation and for examples, see
061 * <a
062 href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
063 * in <em>The Java Tutorial.</em>
064 * <p>
065 * <strong>Warning:</strong> Swing is not thread safe. For more
066 * information see <a
067 * href="package-summary.html#threading">Swing's Threading
068 * Policy</a>.
069 * <p>
070 * <strong>Warning:</strong>
071 * Serialized objects of this class will not be compatible with
072 * future Swing releases. The current serialization support is
073 * appropriate for short term storage or RMI between applications running
074 * the same version of Swing. As of 1.4, support for long term storage
075 * of all JavaBeans<sup><font size="-2">TM</font></sup>
076 * has been added to the <code>java.beans</code> package.
077 * Please see {@link java.beans.XMLEncoder}.
078 *
079 * @beaninfo
080 * attribute: isContainer false
081 * description: An item which can be selected in a menu.
082 *
083 * @version 1.134 05/05/07
084 * @author Georges Saab
085 * @author David Karlton
086 * @see JPopupMenu
087 * @see JMenu
088 * @see JCheckBoxMenuItem
089 * @see JRadioButtonMenuItem
090 */
091 public class JMenuItem extends AbstractButton implements Accessible,
092 MenuElement {
093
094 /**
095 * @see #getUIClassID
096 * @see #readObject
097 */
098 private static final String uiClassID = "MenuItemUI";
099
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
217 FocusListener, Serializable {
218 public void focusGained(FocusEvent event) {
219 }
220
221 public void focusLost(FocusEvent event) {
222 // When focus is lost, repaint if
223 // the focus information is painted
224 JMenuItem mi = (JMenuItem) event.getSource();
225 if (mi.isFocusPainted()) {
226 mi.repaint();
227 }
228 }
229 }
230
231 /**
232 * Sets the look and feel object that renders this component.
233 *
234 * @param ui the <code>JMenuItemUI</code> L&F object
235 * @see UIDefaults#getUI
236 * @beaninfo
237 * bound: true
238 * hidden: true
239 * attribute: visualUpdate true
240 * description: The UI object that implements the Component's LookAndFeel.
241 */
242 public void setUI(MenuItemUI ui) {
243 super .setUI(ui);
244 }
245
246 /**
247 * Resets the UI property with a value from the current look and feel.
248 *
249 * @see JComponent#updateUI
250 */
251 public void updateUI() {
252 setUI((MenuItemUI) UIManager.getUI(this ));
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 * 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 = (ButtonModel) 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 = (ButtonModel) 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
310 && !UIManager
311 .getBoolean("MenuItem.disabledAreNavigable")) {
312 setArmed(false);
313 }
314 super .setEnabled(b);
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,
327 this ) != null) {
328 return false;
329 }
330 return true;
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 : (KeyStroke) a
398 .getValue(Action.ACCELERATOR_KEY);
399 setAccelerator(ks);
400 }
401
402 /**
403 * {@inheritDoc}
404 * @since 1.6
405 */
406 protected void actionPropertyChanged(Action action,
407 String propertyName) {
408 if (propertyName == Action.ACCELERATOR_KEY) {
409 configureAcceleratorFromAction(action);
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[],
429 MenuSelectionManager manager) {
430 processMenuDragMouseEvent(new MenuDragMouseEvent(e
431 .getComponent(), e.getID(), e.getWhen(), e
432 .getModifiers(), e.getX(), e.getY(), e.getXOnScreen(),
433 e.getYOnScreen(), e.getClickCount(),
434 e.isPopupTrigger(), path, manager));
435 }
436
437 /**
438 * Processes a key event forwarded from the
439 * <code>MenuSelectionManager</code> and changes the menu selection,
440 * if necessary, by using <code>MenuSelectionManager</code>'s API.
441 * <p>
442 * Note: you do not have to forward the event to sub-components.
443 * This is done automatically by the <code>MenuSelectionManager</code>.
444 *
445 * @param e a <code>KeyEvent</code>
446 * @param path the <code>MenuElement</code> path array
447 * @param manager the <code>MenuSelectionManager</code>
448 */
449 public void processKeyEvent(KeyEvent e, MenuElement path[],
450 MenuSelectionManager manager) {
451 if (DEBUG) {
452 System.out.println("in JMenuItem.processKeyEvent/3 for "
453 + getText() + " "
454 + KeyStroke.getKeyStrokeForEvent(e));
455 }
456 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(),
457 e.getID(), e.getWhen(), e.getModifiers(), e
458 .getKeyCode(), e.getKeyChar(), path, manager);
459 processMenuKeyEvent(mke);
460
461 if (mke.isConsumed()) {
462 e.consume();
463 }
464 }
465
466 /**
467 * Handles mouse drag in a menu.
468 *
469 * @param e a <code>MenuDragMouseEvent</code> object
470 */
471 public void processMenuDragMouseEvent(MenuDragMouseEvent e) {
472 switch (e.getID()) {
473 case MouseEvent.MOUSE_ENTERED:
474 isMouseDragged = false;
475 fireMenuDragMouseEntered(e);
476 break;
477 case MouseEvent.MOUSE_EXITED:
478 isMouseDragged = false;
479 fireMenuDragMouseExited(e);
480 break;
481 case MouseEvent.MOUSE_DRAGGED:
482 isMouseDragged = true;
483 fireMenuDragMouseDragged(e);
484 break;
485 case MouseEvent.MOUSE_RELEASED:
486 if (isMouseDragged)
487 fireMenuDragMouseReleased(e);
488 break;
489 default:
490 break;
491 }
492 }
493
494 /**
495 * Handles a keystroke in a menu.
496 *
497 * @param e a <code>MenuKeyEvent</code> object
498 */
499 public void processMenuKeyEvent(MenuKeyEvent e) {
500 if (DEBUG) {
501 System.out.println("in JMenuItem.processMenuKeyEvent for "
502 + getText() + " "
503 + KeyStroke.getKeyStrokeForEvent(e));
504 }
505 switch (e.getID()) {
506 case KeyEvent.KEY_PRESSED:
507 fireMenuKeyPressed(e);
508 break;
509 case KeyEvent.KEY_RELEASED:
510 fireMenuKeyReleased(e);
511 break;
512 case KeyEvent.KEY_TYPED:
513 fireMenuKeyTyped(e);
514 break;
515 default:
516 break;
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>MenuMouseDragEvent</code>
525 * @see EventListenerList
526 */
527 protected void fireMenuDragMouseEntered(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])
536 .menuDragMouseEntered(event);
537 }
538 }
539 }
540
541 /**
542 * Notifies all listeners that have registered interest for
543 * notification on this event type.
544 *
545 * @param event a <code>MenuDragMouseEvent</code>
546 * @see EventListenerList
547 */
548 protected void fireMenuDragMouseExited(MenuDragMouseEvent event) {
549 // Guaranteed to return a non-null array
550 Object[] listeners = listenerList.getListenerList();
551 // Process the listeners last to first, notifying
552 // those that are interested in this event
553 for (int i = listeners.length - 2; i >= 0; i -= 2) {
554 if (listeners[i] == MenuDragMouseListener.class) {
555 // Lazily create the event:
556 ((MenuDragMouseListener) listeners[i + 1])
557 .menuDragMouseExited(event);
558 }
559 }
560 }
561
562 /**
563 * Notifies all listeners that have registered interest for
564 * notification on this event type.
565 *
566 * @param event a <code>MenuDragMouseEvent</code>
567 * @see EventListenerList
568 */
569 protected void fireMenuDragMouseDragged(MenuDragMouseEvent event) {
570 // Guaranteed to return a non-null array
571 Object[] listeners = listenerList.getListenerList();
572 // Process the listeners last to first, notifying
573 // those that are interested in this event
574 for (int i = listeners.length - 2; i >= 0; i -= 2) {
575 if (listeners[i] == MenuDragMouseListener.class) {
576 // Lazily create the event:
577 ((MenuDragMouseListener) listeners[i + 1])
578 .menuDragMouseDragged(event);
579 }
580 }
581 }
582
583 /**
584 * Notifies all listeners that have registered interest for
585 * notification on this event type.
586 *
587 * @param event a <code>MenuDragMouseEvent</code>
588 * @see EventListenerList
589 */
590 protected void fireMenuDragMouseReleased(MenuDragMouseEvent event) {
591 // Guaranteed to return a non-null array
592 Object[] listeners = listenerList.getListenerList();
593 // Process the listeners last to first, notifying
594 // those that are interested in this event
595 for (int i = listeners.length - 2; i >= 0; i -= 2) {
596 if (listeners[i] == MenuDragMouseListener.class) {
597 // Lazily create the event:
598 ((MenuDragMouseListener) listeners[i + 1])
599 .menuDragMouseReleased(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 */
611 protected void fireMenuKeyPressed(MenuKeyEvent event) {
612 if (DEBUG) {
613 System.out.println("in JMenuItem.fireMenuKeyPressed for "
614 + getText() + " "
615 + KeyStroke.getKeyStrokeForEvent(event));
616 }
617 // Guaranteed to return a non-null array
618 Object[] listeners = listenerList.getListenerList();
619 // Process the listeners last to first, notifying
620 // those that are interested in this event
621 for (int i = listeners.length - 2; i >= 0; i -= 2) {
622 if (listeners[i] == MenuKeyListener.class) {
623 // Lazily create the event:
624 ((MenuKeyListener) listeners[i + 1])
625 .menuKeyPressed(event);
626 }
627 }
628 }
629
630 /**
631 * Notifies all listeners that have registered interest for
632 * notification on this event type.
633 *
634 * @param event a <code>MenuKeyEvent</code>
635 * @see EventListenerList
636 */
637 protected void fireMenuKeyReleased(MenuKeyEvent event) {
638 if (DEBUG) {
639 System.out.println("in JMenuItem.fireMenuKeyReleased for "
640 + getText() + " "
641 + KeyStroke.getKeyStrokeForEvent(event));
642 }
643 // Guaranteed to return a non-null array
644 Object[] listeners = listenerList.getListenerList();
645 // Process the listeners last to first, notifying
646 // those that are interested in this event
647 for (int i = listeners.length - 2; i >= 0; i -= 2) {
648 if (listeners[i] == MenuKeyListener.class) {
649 // Lazily create the event:
650 ((MenuKeyListener) listeners[i + 1])
651 .menuKeyReleased(event);
652 }
653 }
654 }
655
656 /**
657 * Notifies all listeners that have registered interest for
658 * notification on this event type.
659 *
660 * @param event a <code>MenuKeyEvent</code>
661 * @see EventListenerList
662 */
663 protected void fireMenuKeyTyped(MenuKeyEvent event) {
664 if (DEBUG) {
665 System.out.println("in JMenuItem.fireMenuKeyTyped for "
666 + getText() + " "
667 + KeyStroke.getKeyStrokeForEvent(event));
668 }
669 // Guaranteed to return a non-null array
670 Object[] listeners = listenerList.getListenerList();
671 // Process the listeners last to first, notifying
672 // those that are interested in this event
673 for (int i = listeners.length - 2; i >= 0; i -= 2) {
674 if (listeners[i] == MenuKeyListener.class) {
675 // Lazily create the event:
676 ((MenuKeyListener) listeners[i + 1])
677 .menuKeyTyped(event);
678 }
679 }
680 }
681
682 /**
683 * Called by the <code>MenuSelectionManager</code> when the
684 * <code>MenuElement</code> is selected or unselected.
685 *
686 * @param isIncluded true if this menu item is on the part of the menu
687 * path that changed, false if this menu is part of the
688 * a menu path that changed, but this particular part of
689 * that path is still the same
690 * @see MenuSelectionManager#setSelectedPath(MenuElement[])
691 */
692 public void menuSelectionChanged(boolean isIncluded) {
693 setArmed(isIncluded);
694 }
695
696 /**
697 * This method returns an array containing the sub-menu
698 * components for this menu component.
699 *
700 * @return an array of <code>MenuElement</code>s
701 */
702 public MenuElement[] getSubElements() {
703 return new MenuElement[0];
704 }
705
706 /**
707 * Returns the <code>java.awt.Component</code> used to paint
708 * this object. The returned component will be used to convert
709 * events and detect if an event is inside a menu component.
710 *
711 * @return the <code>Component</code> that paints this menu item
712 */
713 public Component getComponent() {
714 return this ;
715 }
716
717 /**
718 * Adds a <code>MenuDragMouseListener</code> to the menu item.
719 *
720 * @param l the <code>MenuDragMouseListener</code> to be added
721 */
722 public void addMenuDragMouseListener(MenuDragMouseListener l) {
723 listenerList.add(MenuDragMouseListener.class, l);
724 }
725
726 /**
727 * Removes a <code>MenuDragMouseListener</code> from the menu item.
728 *
729 * @param l the <code>MenuDragMouseListener</code> to be removed
730 */
731 public void removeMenuDragMouseListener(MenuDragMouseListener l) {
732 listenerList.remove(MenuDragMouseListener.class, l);
733 }
734
735 /**
736 * Returns an array of all the <code>MenuDragMouseListener</code>s added
737 * to this JMenuItem with addMenuDragMouseListener().
738 *
739 * @return all of the <code>MenuDragMouseListener</code>s added or an empty
740 * array if no listeners have been added
741 * @since 1.4
742 */
743 public MenuDragMouseListener[] getMenuDragMouseListeners() {
744 return (MenuDragMouseListener[]) listenerList
745 .getListeners(MenuDragMouseListener.class);
746 }
747
748 /**
749 * Adds a <code>MenuKeyListener</code> to the menu item.
750 *
751 * @param l the <code>MenuKeyListener</code> to be added
752 */
753 public void addMenuKeyListener(MenuKeyListener l) {
754 listenerList.add(MenuKeyListener.class, l);
755 }
756
757 /**
758 * Removes a <code>MenuKeyListener</code> from the menu item.
759 *
760 * @param l the <code>MenuKeyListener</code> to be removed
761 */
762 public void removeMenuKeyListener(MenuKeyListener l) {
763 listenerList.remove(MenuKeyListener.class, l);
764 }
765
766 /**
767 * Returns an array of all the <code>MenuKeyListener</code>s added
768 * to this JMenuItem with addMenuKeyListener().
769 *
770 * @return all of the <code>MenuKeyListener</code>s added or an empty
771 * array if no listeners have been added
772 * @since 1.4
773 */
774 public MenuKeyListener[] getMenuKeyListeners() {
775 return (MenuKeyListener[]) listenerList
776 .getListeners(MenuKeyListener.class);
777 }
778
779 /**
780 * See JComponent.readObject() for information about serialization
781 * in Swing.
782 */
783 private void readObject(ObjectInputStream s) throws IOException,
784 ClassNotFoundException {
785 s.defaultReadObject();
786 if (getUIClassID().equals(uiClassID)) {
787 updateUI();
788 }
789 }
790
791 private void writeObject(ObjectOutputStream s) throws IOException {
792 s.defaultWriteObject();
793 if (getUIClassID().equals(uiClassID)) {
794 byte count = JComponent.getWriteObjCounter(this );
795 JComponent.setWriteObjCounter(this , --count);
796 if (count == 0 && ui != null) {
797 ui.installUI(this );
798 }
799 }
800 }
801
802 /**
803 * Returns a string representation of this <code>JMenuItem</code>.
804 * This method is intended to be used only for debugging purposes,
805 * and the content and format of the returned string may vary between
806 * implementations. The returned string may be empty but may not
807 * be <code>null</code>.
808 *
809 * @return a string representation of this <code>JMenuItem</code>
810 */
811 protected String paramString() {
812 return super .paramString();
813 }
814
815 /////////////////
816 // Accessibility support
817 ////////////////
818
819 /**
820 * Returns the <code>AccessibleContext</code> associated with this
821 * <code>JMenuItem</code>. For <code>JMenuItem</code>s,
822 * the <code>AccessibleContext</code> takes the form of an
823 * <code>AccessibleJMenuItem</code>.
824 * A new AccessibleJMenuItme instance is created if necessary.
825 *
826 * @return an <code>AccessibleJMenuItem</code> that serves as the
827 * <code>AccessibleContext</code> of this <code>JMenuItem</code>
828 */
829 public AccessibleContext getAccessibleContext() {
830 if (accessibleContext == null) {
831 accessibleContext = new AccessibleJMenuItem();
832 }
833 return accessibleContext;
834 }
835
836 /**
837 * This class implements accessibility support for the
838 * <code>JMenuItem</code> class. It provides an implementation of the
839 * Java Accessibility API appropriate to menu item user-interface
840 * elements.
841 * <p>
842 * <strong>Warning:</strong>
843 * Serialized objects of this class will not be compatible with
844 * future Swing releases. The current serialization support is
845 * appropriate for short term storage or RMI between applications running
846 * the same version of Swing. As of 1.4, support for long term storage
847 * of all JavaBeans<sup><font size="-2">TM</font></sup>
848 * has been added to the <code>java.beans</code> package.
849 * Please see {@link java.beans.XMLEncoder}.
850 */
851 protected class AccessibleJMenuItem extends
852 AccessibleAbstractButton implements ChangeListener {
853
854 private boolean isArmed = false;
855 private boolean hasFocus = false;
856 private boolean isPressed = false;
857 private boolean isSelected = false;
858
859 AccessibleJMenuItem() {
860 super ();
861 JMenuItem.this .addChangeListener(this );
862 }
863
864 /**
865 * Get the role of this object.
866 *
867 * @return an instance of AccessibleRole describing the role of the
868 * object
869 */
870 public AccessibleRole getAccessibleRole() {
871 return AccessibleRole.MENU_ITEM;
872 }
873
874 private void fireAccessibilityFocusedEvent(JMenuItem toCheck) {
875 MenuElement[] path = MenuSelectionManager.defaultManager()
876 .getSelectedPath();
877 if (path.length > 0) {
878 Object menuItem = path[path.length - 1];
879 if (toCheck == menuItem) {
880 firePropertyChange(
881 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
882 null, AccessibleState.FOCUSED);
883 }
884 }
885 }
886
887 /**
888 * Supports the change listener interface and fires property changes.
889 */
890 public void stateChanged(ChangeEvent e) {
891 firePropertyChange(
892 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
893 Boolean.valueOf(false), Boolean.valueOf(true));
894 if (JMenuItem.this .getModel().isArmed()) {
895 if (!isArmed) {
896 isArmed = true;
897 firePropertyChange(
898 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
899 null, AccessibleState.ARMED);
900 // Fix for 4848220 moved here to avoid major memory leak
901 // Here we will fire the event in case of JMenuItem
902 // See bug 4910323 for details [zav]
903 fireAccessibilityFocusedEvent(JMenuItem.this );
904 }
905 } else {
906 if (isArmed) {
907 isArmed = false;
908 firePropertyChange(
909 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
910 AccessibleState.ARMED, null);
911 }
912 }
913 if (JMenuItem.this .isFocusOwner()) {
914 if (!hasFocus) {
915 hasFocus = true;
916 firePropertyChange(
917 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
918 null, AccessibleState.FOCUSED);
919 }
920 } else {
921 if (hasFocus) {
922 hasFocus = false;
923 firePropertyChange(
924 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
925 AccessibleState.FOCUSED, null);
926 }
927 }
928 if (JMenuItem.this .getModel().isPressed()) {
929 if (!isPressed) {
930 isPressed = true;
931 firePropertyChange(
932 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
933 null, AccessibleState.PRESSED);
934 }
935 } else {
936 if (isPressed) {
937 isPressed = false;
938 firePropertyChange(
939 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
940 AccessibleState.PRESSED, null);
941 }
942 }
943 if (JMenuItem.this .getModel().isSelected()) {
944 if (!isSelected) {
945 isSelected = true;
946 firePropertyChange(
947 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
948 null, AccessibleState.CHECKED);
949
950 // Fix for 4848220 moved here to avoid major memory leak
951 // Here we will fire the event in case of JMenu
952 // See bug 4910323 for details [zav]
953 fireAccessibilityFocusedEvent(JMenuItem.this );
954 }
955 } else {
956 if (isSelected) {
957 isSelected = false;
958 firePropertyChange(
959 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
960 AccessibleState.CHECKED, null);
961 }
962 }
963
964 }
965 } // inner class AccessibleJMenuItem
966
967 }
|