blob: 4580d32507dadb7927ae11e2cac9a0b4f282f8e2 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing;
26
27import java.util.EventListener;
28import java.awt.*;
29import java.awt.event.*;
30import java.awt.image.*;
31
32import java.beans.PropertyChangeEvent;
33import java.beans.PropertyChangeListener;
34
35import java.io.Serializable;
36import java.io.ObjectOutputStream;
37import java.io.ObjectInputStream;
38import java.io.IOException;
39
40import javax.swing.plaf.*;
41import javax.swing.plaf.basic.*;
42import javax.swing.event.*;
43import 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://java.sun.com/docs/books/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://java.sun.com/docs/books/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 */
90public class JMenuItem extends AbstractButton implements Accessible,MenuElement {
91
92 /**
93 * @see #getUIClassID
94 * @see #readObject
95 */
96 private static final String uiClassID = "MenuItemUI";
97
98 /* diagnostic aids -- should be false for production builds. */
99 private static final boolean TRACE = false; // trace creates and disposes
100 private static final boolean VERBOSE = false; // show reuse hits/misses
101 private static final boolean DEBUG = false; // show bad params, misc.
102
103 private boolean isMouseDragged = false;
104
105 /**
106 * Creates a <code>JMenuItem</code> with no set text or icon.
107 */
108 public JMenuItem() {
109 this(null, (Icon)null);
110 }
111
112 /**
113 * Creates a <code>JMenuItem</code> with the specified icon.
114 *
115 * @param icon the icon of the <code>JMenuItem</code>
116 */
117 public JMenuItem(Icon icon) {
118 this(null, icon);
119 }
120
121 /**
122 * Creates a <code>JMenuItem</code> with the specified text.
123 *
124 * @param text the text of the <code>JMenuItem</code>
125 */
126 public JMenuItem(String text) {
127 this(text, (Icon)null);
128 }
129
130 /**
131 * Creates a menu item whose properties are taken from the
132 * specified <code>Action</code>.
133 *
134 * @param a the action of the <code>JMenuItem</code>
135 * @since 1.3
136 */
137 public JMenuItem(Action a) {
138 this();
139 setAction(a);
140 }
141
142 /**
143 * Creates a <code>JMenuItem</code> with the specified text and icon.
144 *
145 * @param text the text of the <code>JMenuItem</code>
146 * @param icon the icon of the <code>JMenuItem</code>
147 */
148 public JMenuItem(String text, Icon icon) {
149 setModel(new DefaultButtonModel());
150 init(text, icon);
151 initFocusability();
152 }
153
154 /**
155 * Creates a <code>JMenuItem</code> with the specified text and
156 * keyboard mnemonic.
157 *
158 * @param text the text of the <code>JMenuItem</code>
159 * @param mnemonic the keyboard mnemonic for the <code>JMenuItem</code>
160 */
161 public JMenuItem(String text, int mnemonic) {
162 setModel(new DefaultButtonModel());
163 init(text, null);
164 setMnemonic(mnemonic);
165 initFocusability();
166 }
167
168 /**
169 * {@inheritDoc}
170 */
171 public void setModel(ButtonModel newModel) {
172 super.setModel(newModel);
173 if(newModel instanceof DefaultButtonModel) {
174 ((DefaultButtonModel)newModel).setMenuItem(true);
175 }
176 }
177
178 /**
179 * Inititalizes the focusability of the the <code>JMenuItem</code>.
180 * <code>JMenuItem</code>'s are focusable, but subclasses may
181 * want to be, this provides them the opportunity to override this
182 * and invoke something else, or nothing at all. Refer to
183 * {@link javax.swing.JMenu#initFocusability} for the motivation of
184 * this.
185 */
186 void initFocusability() {
187 setFocusable(false);
188 }
189
190 /**
191 * Initializes the menu item with the specified text and icon.
192 *
193 * @param text the text of the <code>JMenuItem</code>
194 * @param icon the icon of the <code>JMenuItem</code>
195 */
196 protected void init(String text, Icon icon) {
197 if(text != null) {
198 setText(text);
199 }
200
201 if(icon != null) {
202 setIcon(icon);
203 }
204
205 // Listen for Focus events
206 addFocusListener(new MenuItemFocusListener());
207 setUIProperty("borderPainted", Boolean.FALSE);
208 setFocusPainted(false);
209 setHorizontalTextPosition(JButton.TRAILING);
210 setHorizontalAlignment(JButton.LEADING);
211 updateUI();
212 }
213
214 private static class MenuItemFocusListener implements FocusListener,
215 Serializable {
216 public void focusGained(FocusEvent event) {}
217 public void focusLost(FocusEvent event) {
218 // When focus is lost, repaint if
219 // the focus information is painted
220 JMenuItem mi = (JMenuItem)event.getSource();
221 if(mi.isFocusPainted()) {
222 mi.repaint();
223 }
224 }
225 }
226
227
228 /**
229 * Sets the look and feel object that renders this component.
230 *
231 * @param ui the <code>JMenuItemUI</code> L&F object
232 * @see UIDefaults#getUI
233 * @beaninfo
234 * bound: true
235 * hidden: true
236 * attribute: visualUpdate true
237 * description: The UI object that implements the Component's LookAndFeel.
238 */
239 public void setUI(MenuItemUI ui) {
240 super.setUI(ui);
241 }
242
243 /**
244 * Resets the UI property with a value from the current look and feel.
245 *
246 * @see JComponent#updateUI
247 */
248 public void updateUI() {
249 setUI((MenuItemUI)UIManager.getUI(this));
250 }
251
252
253 /**
254 * Returns the suffix used to construct the name of the L&F class used to
255 * render this component.
256 *
257 * @return the string "MenuItemUI"
258 * @see JComponent#getUIClassID
259 * @see UIDefaults#getUI
260 */
261 public String getUIClassID() {
262 return uiClassID;
263 }
264
265
266 /**
267 * Identifies the menu item as "armed". If the mouse button is
268 * released while it is over this item, the menu's action event
269 * will fire. If the mouse button is released elsewhere, the
270 * event will not fire and the menu item will be disarmed.
271 *
272 * @param b true to arm the menu item so it can be selected
273 * @beaninfo
274 * description: Mouse release will fire an action event
275 * hidden: true
276 */
277 public void setArmed(boolean b) {
278 ButtonModel model = (ButtonModel) getModel();
279
280 boolean oldValue = model.isArmed();
281 if(model.isArmed() != b) {
282 model.setArmed(b);
283 }
284 }
285
286 /**
287 * Returns whether the menu item is "armed".
288 *
289 * @return true if the menu item is armed, and it can be selected
290 * @see #setArmed
291 */
292 public boolean isArmed() {
293 ButtonModel model = (ButtonModel) getModel();
294 return model.isArmed();
295 }
296
297 /**
298 * Enables or disables the menu item.
299 *
300 * @param b true to enable the item
301 * @beaninfo
302 * description: Does the component react to user interaction
303 * bound: true
304 * preferred: true
305 */
306 public void setEnabled(boolean b) {
307 // Make sure we aren't armed!
308 if (!b && !UIManager.getBoolean("MenuItem.disabledAreNavigable")) {
309 setArmed(false);
310 }
311 super.setEnabled(b);
312 }
313
314
315 /**
316 * Returns true since <code>Menu</code>s, by definition,
317 * should always be on top of all other windows. If the menu is
318 * in an internal frame false is returned due to the rollover effect
319 * for windows laf where the menu is not always on top.
320 */
321 // package private
322 boolean alwaysOnTop() {
323 // Fix for bug #4482165
324 if (SwingUtilities.getAncestorOfClass(JInternalFrame.class, this) !=
325 null) {
326 return false;
327 }
328 return true;
329 }
330
331
332 /* The keystroke which acts as the menu item's accelerator
333 */
334 private KeyStroke accelerator;
335
336 /**
337 * Sets the key combination which invokes the menu item's
338 * action listeners without navigating the menu hierarchy. It is the
339 * UI's responsibility to install the correct action. Note that
340 * when the keyboard accelerator is typed, it will work whether or
341 * not the menu is currently displayed.
342 *
343 * @param keyStroke the <code>KeyStroke</code> which will
344 * serve as an accelerator
345 * @beaninfo
346 * description: The keystroke combination which will invoke the
347 * JMenuItem's actionlisteners without navigating the
348 * menu hierarchy
349 * bound: true
350 * preferred: true
351 */
352 public void setAccelerator(KeyStroke keyStroke) {
353 KeyStroke oldAccelerator = accelerator;
354 this.accelerator = keyStroke;
355 repaint();
356 revalidate();
357 firePropertyChange("accelerator", oldAccelerator, accelerator);
358 }
359
360 /**
361 * Returns the <code>KeyStroke</code> which serves as an accelerator
362 * for the menu item.
363 * @return a <code>KeyStroke</code> object identifying the
364 * accelerator key
365 */
366 public KeyStroke getAccelerator() {
367 return this.accelerator;
368 }
369
370 /**
371 * {@inheritDoc}
372 *
373 * @since 1.3
374 */
375 protected void configurePropertiesFromAction(Action a) {
376 super.configurePropertiesFromAction(a);
377 configureAcceleratorFromAction(a);
378 }
379
380 void setIconFromAction(Action a) {
381 Icon icon = null;
382 if (a != null) {
383 icon = (Icon)a.getValue(Action.SMALL_ICON);
384 }
385 setIcon(icon);
386 }
387
388 void largeIconChanged(Action a) {
389 }
390
391 void smallIconChanged(Action a) {
392 setIconFromAction(a);
393 }
394
395 void configureAcceleratorFromAction(Action a) {
396 KeyStroke ks = (a==null) ? null :
397 (KeyStroke)a.getValue(Action.ACCELERATOR_KEY);
398 setAccelerator(ks);
399 }
400
401 /**
402 * {@inheritDoc}
403 * @since 1.6
404 */
405 protected void actionPropertyChanged(Action action, String propertyName) {
406 if (propertyName == Action.ACCELERATOR_KEY) {
407 configureAcceleratorFromAction(action);
408 }
409 else {
410 super.actionPropertyChanged(action, propertyName);
411 }
412 }
413
414 /**
415 * Processes a mouse event forwarded from the
416 * <code>MenuSelectionManager</code> and changes the menu
417 * selection, if necessary, by using the
418 * <code>MenuSelectionManager</code>'s API.
419 * <p>
420 * Note: you do not have to forward the event to sub-components.
421 * This is done automatically by the <code>MenuSelectionManager</code>.
422 *
423 * @param e a <code>MouseEvent</code>
424 * @param path the <code>MenuElement</code> path array
425 * @param manager the <code>MenuSelectionManager</code>
426 */
427 public void processMouseEvent(MouseEvent e,MenuElement path[],MenuSelectionManager manager) {
428 processMenuDragMouseEvent(
429 new MenuDragMouseEvent(e.getComponent(), e.getID(),
430 e.getWhen(),
431 e.getModifiers(), e.getX(), e.getY(),
432 e.getXOnScreen(), e.getYOnScreen(),
433 e.getClickCount(), e.isPopupTrigger(),
434 path, manager));
435 }
436
437
438 /**
439 * Processes a key event forwarded from the
440 * <code>MenuSelectionManager</code> and changes the menu selection,
441 * if necessary, by using <code>MenuSelectionManager</code>'s API.
442 * <p>
443 * Note: you do not have to forward the event to sub-components.
444 * This is done automatically by the <code>MenuSelectionManager</code>.
445 *
446 * @param e a <code>KeyEvent</code>
447 * @param path the <code>MenuElement</code> path array
448 * @param manager the <code>MenuSelectionManager</code>
449 */
450 public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
451 if (DEBUG) {
452 System.out.println("in JMenuItem.processKeyEvent/3 for " + getText() +
453 " " + KeyStroke.getKeyStrokeForEvent(e));
454 }
455 MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
456 e.getWhen(), e.getModifiers(),
457 e.getKeyCode(), e.getKeyChar(),
458 path, manager);
459 processMenuKeyEvent(mke);
460
461 if (mke.isConsumed()) {
462 e.consume();
463 }
464 }
465
466
467
468 /**
469 * Handles mouse drag in a menu.
470 *
471 * @param e a <code>MenuDragMouseEvent</code> object
472 */
473 public void processMenuDragMouseEvent(MenuDragMouseEvent e) {
474 switch (e.getID()) {
475 case MouseEvent.MOUSE_ENTERED:
476 isMouseDragged = false; fireMenuDragMouseEntered(e); break;
477 case MouseEvent.MOUSE_EXITED:
478 isMouseDragged = false; fireMenuDragMouseExited(e); break;
479 case MouseEvent.MOUSE_DRAGGED:
480 isMouseDragged = true; fireMenuDragMouseDragged(e); break;
481 case MouseEvent.MOUSE_RELEASED:
482 if(isMouseDragged) fireMenuDragMouseReleased(e); break;
483 default:
484 break;
485 }
486 }
487
488 /**
489 * Handles a keystroke in a menu.
490 *
491 * @param e a <code>MenuKeyEvent</code> object
492 */
493 public void processMenuKeyEvent(MenuKeyEvent e) {
494 if (DEBUG) {
495 System.out.println("in JMenuItem.processMenuKeyEvent for " + getText()+
496 " " + KeyStroke.getKeyStrokeForEvent(e));
497 }
498 switch (e.getID()) {
499 case KeyEvent.KEY_PRESSED:
500 fireMenuKeyPressed(e); break;
501 case KeyEvent.KEY_RELEASED:
502 fireMenuKeyReleased(e); break;
503 case KeyEvent.KEY_TYPED:
504 fireMenuKeyTyped(e); break;
505 default:
506 break;
507 }
508 }
509
510 /**
511 * Notifies all listeners that have registered interest for
512 * notification on this event type.
513 *
514 * @param event a <code>MenuMouseDragEvent</code>
515 * @see EventListenerList
516 */
517 protected void fireMenuDragMouseEntered(MenuDragMouseEvent event) {
518 // Guaranteed to return a non-null array
519 Object[] listeners = listenerList.getListenerList();
520 // Process the listeners last to first, notifying
521 // those that are interested in this event
522 for (int i = listeners.length-2; i>=0; i-=2) {
523 if (listeners[i]==MenuDragMouseListener.class) {
524 // Lazily create the event:
525 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseEntered(event);
526 }
527 }
528 }
529
530 /**
531 * Notifies all listeners that have registered interest for
532 * notification on this event type.
533 *
534 * @param event a <code>MenuDragMouseEvent</code>
535 * @see EventListenerList
536 */
537 protected void fireMenuDragMouseExited(MenuDragMouseEvent event) {
538 // Guaranteed to return a non-null array
539 Object[] listeners = listenerList.getListenerList();
540 // Process the listeners last to first, notifying
541 // those that are interested in this event
542 for (int i = listeners.length-2; i>=0; i-=2) {
543 if (listeners[i]==MenuDragMouseListener.class) {
544 // Lazily create the event:
545 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseExited(event);
546 }
547 }
548 }
549
550 /**
551 * Notifies all listeners that have registered interest for
552 * notification on this event type.
553 *
554 * @param event a <code>MenuDragMouseEvent</code>
555 * @see EventListenerList
556 */
557 protected void fireMenuDragMouseDragged(MenuDragMouseEvent event) {
558 // Guaranteed to return a non-null array
559 Object[] listeners = listenerList.getListenerList();
560 // Process the listeners last to first, notifying
561 // those that are interested in this event
562 for (int i = listeners.length-2; i>=0; i-=2) {
563 if (listeners[i]==MenuDragMouseListener.class) {
564 // Lazily create the event:
565 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseDragged(event);
566 }
567 }
568 }
569
570 /**
571 * Notifies all listeners that have registered interest for
572 * notification on this event type.
573 *
574 * @param event a <code>MenuDragMouseEvent</code>
575 * @see EventListenerList
576 */
577 protected void fireMenuDragMouseReleased(MenuDragMouseEvent event) {
578 // Guaranteed to return a non-null array
579 Object[] listeners = listenerList.getListenerList();
580 // Process the listeners last to first, notifying
581 // those that are interested in this event
582 for (int i = listeners.length-2; i>=0; i-=2) {
583 if (listeners[i]==MenuDragMouseListener.class) {
584 // Lazily create the event:
585 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseReleased(event);
586 }
587 }
588 }
589
590 /**
591 * Notifies all listeners that have registered interest for
592 * notification on this event type.
593 *
594 * @param event a <code>MenuKeyEvent</code>
595 * @see EventListenerList
596 */
597 protected void fireMenuKeyPressed(MenuKeyEvent event) {
598 if (DEBUG) {
599 System.out.println("in JMenuItem.fireMenuKeyPressed for " + getText()+
600 " " + KeyStroke.getKeyStrokeForEvent(event));
601 }
602 // Guaranteed to return a non-null array
603 Object[] listeners = listenerList.getListenerList();
604 // Process the listeners last to first, notifying
605 // those that are interested in this event
606 for (int i = listeners.length-2; i>=0; i-=2) {
607 if (listeners[i]==MenuKeyListener.class) {
608 // Lazily create the event:
609 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
610 }
611 }
612 }
613
614 /**
615 * Notifies all listeners that have registered interest for
616 * notification on this event type.
617 *
618 * @param event a <code>MenuKeyEvent</code>
619 * @see EventListenerList
620 */
621 protected void fireMenuKeyReleased(MenuKeyEvent event) {
622 if (DEBUG) {
623 System.out.println("in JMenuItem.fireMenuKeyReleased for " + getText()+
624 " " + KeyStroke.getKeyStrokeForEvent(event));
625 }
626 // Guaranteed to return a non-null array
627 Object[] listeners = listenerList.getListenerList();
628 // Process the listeners last to first, notifying
629 // those that are interested in this event
630 for (int i = listeners.length-2; i>=0; i-=2) {
631 if (listeners[i]==MenuKeyListener.class) {
632 // Lazily create the event:
633 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
634 }
635 }
636 }
637
638 /**
639 * Notifies all listeners that have registered interest for
640 * notification on this event type.
641 *
642 * @param event a <code>MenuKeyEvent</code>
643 * @see EventListenerList
644 */
645 protected void fireMenuKeyTyped(MenuKeyEvent event) {
646 if (DEBUG) {
647 System.out.println("in JMenuItem.fireMenuKeyTyped for " + getText()+
648 " " + KeyStroke.getKeyStrokeForEvent(event));
649 }
650 // Guaranteed to return a non-null array
651 Object[] listeners = listenerList.getListenerList();
652 // Process the listeners last to first, notifying
653 // those that are interested in this event
654 for (int i = listeners.length-2; i>=0; i-=2) {
655 if (listeners[i]==MenuKeyListener.class) {
656 // Lazily create the event:
657 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
658 }
659 }
660 }
661
662 /**
663 * Called by the <code>MenuSelectionManager</code> when the
664 * <code>MenuElement</code> is selected or unselected.
665 *
666 * @param isIncluded true if this menu item is on the part of the menu
667 * path that changed, false if this menu is part of the
668 * a menu path that changed, but this particular part of
669 * that path is still the same
670 * @see MenuSelectionManager#setSelectedPath(MenuElement[])
671 */
672 public void menuSelectionChanged(boolean isIncluded) {
673 setArmed(isIncluded);
674 }
675
676 /**
677 * This method returns an array containing the sub-menu
678 * components for this menu component.
679 *
680 * @return an array of <code>MenuElement</code>s
681 */
682 public MenuElement[] getSubElements() {
683 return new MenuElement[0];
684 }
685
686 /**
687 * Returns the <code>java.awt.Component</code> used to paint
688 * this object. The returned component will be used to convert
689 * events and detect if an event is inside a menu component.
690 *
691 * @return the <code>Component</code> that paints this menu item
692 */
693 public Component getComponent() {
694 return this;
695 }
696
697 /**
698 * Adds a <code>MenuDragMouseListener</code> to the menu item.
699 *
700 * @param l the <code>MenuDragMouseListener</code> to be added
701 */
702 public void addMenuDragMouseListener(MenuDragMouseListener l) {
703 listenerList.add(MenuDragMouseListener.class, l);
704 }
705
706 /**
707 * Removes a <code>MenuDragMouseListener</code> from the menu item.
708 *
709 * @param l the <code>MenuDragMouseListener</code> to be removed
710 */
711 public void removeMenuDragMouseListener(MenuDragMouseListener l) {
712 listenerList.remove(MenuDragMouseListener.class, l);
713 }
714
715 /**
716 * Returns an array of all the <code>MenuDragMouseListener</code>s added
717 * to this JMenuItem with addMenuDragMouseListener().
718 *
719 * @return all of the <code>MenuDragMouseListener</code>s added or an empty
720 * array if no listeners have been added
721 * @since 1.4
722 */
723 public MenuDragMouseListener[] getMenuDragMouseListeners() {
724 return (MenuDragMouseListener[])listenerList.getListeners(
725 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 (MenuKeyListener[])listenerList.getListeners(
756 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<sup><font size="-2">TM</font></sup>
831 * has been added to the <code>java.beans</code> package.
832 * Please see {@link java.beans.XMLEncoder}.
833 */
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}