2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.awt.Component;
42 import java.awt.event.InputEvent;
43 import java.awt.event.KeyEvent;
44 import java.awt.event.MouseEvent;
45 import java.beans.PropertyChangeEvent;
46 import java.beans.PropertyChangeListener;
47 import java.util.EventListener;
49 import javax.accessibility.Accessible;
50 import javax.accessibility.AccessibleContext;
51 import javax.accessibility.AccessibleRole;
52 import javax.accessibility.AccessibleState;
53 import javax.swing.event.ChangeEvent;
54 import javax.swing.event.ChangeListener;
55 import javax.swing.event.MenuDragMouseEvent;
56 import javax.swing.event.MenuDragMouseListener;
57 import javax.swing.event.MenuKeyEvent;
58 import javax.swing.event.MenuKeyListener;
59 import javax.swing.plaf.MenuItemUI;
62 * JMenuItem represents element in the menu. It inherits most of
63 * its functionality from AbstractButton, however its behavior somewhat
64 * varies from it. JMenuItem fire different kinds of events.
65 * PropertyChangeEvents are fired when menuItems properties are modified;
66 * ChangeEvents are fired when menuItem's state changes and actionEvents are
67 * fired when menu item is selected. In addition to this events menuItem also
68 * fire MenuDragMouseEvent and MenuKeyEvents when mouse is dragged over
69 * the menu item or associated key with menu item is invoked respectively.
71 public class JMenuItem extends AbstractButton implements Accessible,
74 private static final long serialVersionUID = -1681004643499461044L;
76 /** Combination of keyboard keys that can be used to activate this menu item */
77 private KeyStroke accelerator;
80 * Creates a new JMenuItem object.
89 * Creates a new JMenuItem with the given icon.
91 * @param icon Icon that will be displayed on the menu item
93 public JMenuItem(Icon icon)
95 // FIXME: The requestedFocusEnabled property should
96 // be set to false, when only icon is set for menu item.
102 * Creates a new JMenuItem with the given label.
104 * @param text label for the menu item
106 public JMenuItem(String text)
112 * Creates a new JMenuItem associated with the specified action.
114 * @param action action for this menu item
116 public JMenuItem(Action action)
119 super.setAction(action);
123 String name = (String) action.getValue(Action.NAME);
127 KeyStroke accel = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY);
129 setAccelerator(accel);
131 Integer mnemonic = (Integer) action.getValue(Action.MNEMONIC_KEY);
132 if (mnemonic != null)
133 setMnemonic(mnemonic.intValue());
135 String command = (String) action.getValue(Action.ACTION_COMMAND_KEY);
137 setActionCommand(command);
142 * Creates a new JMenuItem with specified text and icon.
143 * Text is displayed to the left of icon by default.
145 * @param text label for this menu item
146 * @param icon icon that will be displayed on this menu item
148 public JMenuItem(String text, Icon icon)
155 * Creates a new JMenuItem object.
157 * @param text label for this menu item
158 * @param mnemonic - Single key that can be used with a
159 * look-and-feel meta key to activate this menu item. However
160 * menu item should be visible on the screen when mnemonic is used.
162 public JMenuItem(String text, int mnemonic)
165 setMnemonic(mnemonic);
169 * Initializes this menu item
171 * @param text label for this menu item
172 * @param icon icon to be displayed for this menu item
174 protected void init(String text, Icon icon)
176 super.init(text, icon);
177 setModel(new DefaultButtonModel());
179 // Initializes properties for this menu item, that are different
180 // from Abstract button properties.
181 /* NOTE: According to java specifications paint_border should be set to false,
182 since menu item should not have a border. However running few java programs
183 it seems that menu items and menues can have a border. Commenting
184 out statement below for now. */
185 //borderPainted = false;
186 focusPainted = false;
187 horizontalAlignment = JButton.LEFT;
188 horizontalTextPosition = JButton.TRAILING;
192 * Set the "UI" property of the menu item, which is a look and feel class
193 * responsible for handling menuItem's input events and painting it.
195 * @param ui The new "UI" property
197 public void setUI(MenuItemUI ui)
203 * This method sets this menuItem's UI to the UIManager's default for the
204 * current look and feel.
206 public void updateUI()
208 setUI((MenuItemUI) UIManager.getUI(this));
212 * This method returns a name to identify which look and feel class will be
213 * the UI delegate for the menuItem.
215 * @return The Look and Feel classID. "MenuItemUI"
217 public String getUIClassID()
223 * Returns true if button's model is armed and false otherwise. The
224 * button model is armed if menu item has focus or it is selected.
226 * @return $boolean$ true if button's model is armed and false otherwise
228 public boolean isArmed()
230 return getModel().isArmed();
234 * Sets menuItem's "ARMED" property
236 * @param armed DOCUMENT ME!
238 public void setArmed(boolean armed)
240 getModel().setArmed(armed);
244 * Enable or disable menu item. When menu item is disabled,
245 * its text and icon are grayed out if they exist.
247 * @param enabled if true enable menu item, and disable otherwise.
249 public void setEnabled(boolean enabled)
251 super.setEnabled(enabled);
255 * Return accelerator for this menu item.
257 * @return $KeyStroke$ accelerator for this menu item.
259 public KeyStroke getAccelerator()
265 * Sets the key combination which invokes the menu item's action
266 * listeners without navigating the menu hierarchy. Note that when the
267 * keyboard accelerator is typed, it will work whether or not the
268 * menu is currently displayed.
270 * @param keystroke accelerator for this menu item.
272 public void setAccelerator(KeyStroke keystroke)
274 KeyStroke old = this.accelerator;
275 this.accelerator = keystroke;
276 firePropertyChange ("accelerator", old, keystroke);
280 * Configures menu items' properties from properties of the specified action.
281 * This method overrides configurePropertiesFromAction from AbstractButton
282 * to also set accelerator property.
284 * @param action action to configure properties from
286 protected void configurePropertiesFromAction(Action action)
288 super.configurePropertiesFromAction(action);
290 if (! (this instanceof JMenu) && action != null)
292 setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY)));
293 if (accelerator != null)
294 super.registerKeyboardAction(action, accelerator,
295 JComponent.WHEN_IN_FOCUSED_WINDOW);
300 * Creates PropertyChangeListener to listen for the changes in action
303 * @param action action to listen to for property changes
305 * @return $PropertyChangeListener$ Listener that listens to changes in
308 protected PropertyChangeListener createActionPropertyChangeListener(Action action)
310 return new PropertyChangeListener()
312 public void propertyChange(PropertyChangeEvent e)
314 Action act = (Action) (e.getSource());
315 configurePropertiesFromAction(act);
321 * Process mouse events forwarded from MenuSelectionManager.
323 * @param event event forwarded from MenuSelectionManager
324 * @param path path to the menu element from which event was generated
325 * @param manager MenuSelectionManager for the current menu hierarchy
327 public void processMouseEvent(MouseEvent event, MenuElement[] path,
328 MenuSelectionManager manager)
330 // Fire MenuDragMouseEvents if mouse is being dragged.
332 = (event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0;
334 processMenuDragMouseEvent(createMenuDragMouseEvent(event, path, manager));
336 switch (event.getID())
338 case MouseEvent.MOUSE_CLICKED:
340 case MouseEvent.MOUSE_ENTERED:
341 if (isRolloverEnabled())
342 model.setRollover(true);
344 case MouseEvent.MOUSE_EXITED:
345 if (isRolloverEnabled())
346 model.setRollover(false);
348 // for JMenu last element on the path is its popupMenu.
349 // JMenu shouldn't me disarmed.
350 if (! (path[path.length - 1] instanceof JPopupMenu) && ! dragged)
353 case MouseEvent.MOUSE_PRESSED:
354 if ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0)
356 model.setArmed(true);
357 model.setPressed(true);
360 case MouseEvent.MOUSE_RELEASED:
362 case MouseEvent.MOUSE_MOVED:
364 case MouseEvent.MOUSE_DRAGGED:
370 * Creates MenuDragMouseEvent.
372 * @param event MouseEvent that occured while mouse was pressed.
373 * @param path Path the the menu element where the dragging event was
375 * @param manager MenuSelectionManager for the current menu hierarchy.
377 * @return new MenuDragMouseEvent
379 private MenuDragMouseEvent createMenuDragMouseEvent(MouseEvent event,
381 MenuSelectionManager manager)
383 return new MenuDragMouseEvent((Component) event.getSource(),
384 event.getID(), event.getWhen(),
385 event.getModifiers(), event.getX(),
386 event.getY(), event.getClickCount(),
387 event.isPopupTrigger(), path, manager);
391 * Process key events forwarded from MenuSelectionManager.
393 * @param event event forwarded from MenuSelectionManager
394 * @param path path to the menu element from which event was generated
395 * @param manager MenuSelectionManager for the current menu hierarchy
397 public void processKeyEvent(KeyEvent event, MenuElement[] path,
398 MenuSelectionManager manager)
400 MenuKeyEvent e = new MenuKeyEvent(event.getComponent(), event.getID(),
401 event.getWhen(), event.getModifiers(),
402 event.getKeyCode(), event.getKeyChar(),
404 processMenuKeyEvent(e);
406 // Consume original key event, if the menu key event has been consumed.
412 * This method fires MenuDragMouseEvents to registered listeners.
413 * Different types of MenuDragMouseEvents are fired depending
414 * on the observed mouse event.
418 public void processMenuDragMouseEvent(MenuDragMouseEvent event)
420 switch (event.getID())
422 case MouseEvent.MOUSE_ENTERED:
423 fireMenuDragMouseEntered(event);
425 case MouseEvent.MOUSE_EXITED:
426 fireMenuDragMouseExited(event);
428 case MouseEvent.MOUSE_DRAGGED:
429 fireMenuDragMouseDragged(event);
431 case MouseEvent.MOUSE_RELEASED:
432 fireMenuDragMouseReleased(event);
438 * This method fires MenuKeyEvent to registered listeners.
439 * Different types of MenuKeyEvents are fired depending
440 * on the observed key event.
442 * @param event DOCUMENT ME!
444 public void processMenuKeyEvent(MenuKeyEvent event)
446 switch (event.getID())
448 case KeyEvent.KEY_PRESSED:
449 fireMenuKeyPressed(event);
451 case KeyEvent.KEY_RELEASED:
452 fireMenuKeyReleased(event);
454 case KeyEvent.KEY_TYPED:
455 fireMenuKeyTyped(event);
463 * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
465 * @param event The event signifying that mouse entered menuItem while it was dragged
467 protected void fireMenuDragMouseEntered(MenuDragMouseEvent event)
469 EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
471 for (int i = 0; i < ll.length; i++)
472 ((MenuDragMouseListener) ll[i]).menuDragMouseEntered(event);
476 * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
478 * @param event The event signifying that mouse has exited menu item, while it was dragged
480 protected void fireMenuDragMouseExited(MenuDragMouseEvent event)
482 EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
484 for (int i = 0; i < ll.length; i++)
485 ((MenuDragMouseListener) ll[i]).menuDragMouseExited(event);
489 * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
491 * @param event The event signifying that mouse is being dragged over the menuItem
493 protected void fireMenuDragMouseDragged(MenuDragMouseEvent event)
495 EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
497 for (int i = 0; i < ll.length; i++)
498 ((MenuDragMouseListener) ll[i]).menuDragMouseDragged(event);
502 * This method fires a MenuDragMouseEvent to all the MenuItem's MouseInputListeners.
504 * @param event The event signifying that mouse was released while it was dragged over the menuItem
506 protected void fireMenuDragMouseReleased(MenuDragMouseEvent event)
508 EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
510 for (int i = 0; i < ll.length; i++)
511 ((MenuDragMouseListener) ll[i]).menuDragMouseReleased(event);
515 * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
517 * @param event The event signifying that key associated with this menu was pressed
519 protected void fireMenuKeyPressed(MenuKeyEvent event)
521 EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
523 for (int i = 0; i < ll.length; i++)
524 ((MenuKeyListener) ll[i]).menuKeyPressed(event);
528 * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
530 * @param event The event signifying that key associated with this menu was released
532 protected void fireMenuKeyReleased(MenuKeyEvent event)
534 EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
536 for (int i = 0; i < ll.length; i++)
537 ((MenuKeyListener) ll[i]).menuKeyTyped(event);
541 * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
543 * @param event The event signifying that key associated with this menu was typed.
544 * The key is typed when it was pressed and then released
546 protected void fireMenuKeyTyped(MenuKeyEvent event)
548 EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
550 for (int i = 0; i < ll.length; i++)
551 ((MenuKeyListener) ll[i]).menuKeyTyped(event);
555 * Method of the MenuElement interface.
556 * This method is invoked by MenuSelectionManager when selection of
557 * this menu item has changed. If this menu item was selected then
558 * arm it's model, and disarm the model otherwise. The menu item
559 * is considered to be selected, and thus highlighted when its model
562 * @param changed indicates selection status of this menu item. If changed is
563 * true then menu item is selected and deselected otherwise.
565 public void menuSelectionChanged(boolean changed)
567 Component parent = this.getParent();
570 model.setArmed(true);
572 if (parent != null && parent instanceof JPopupMenu)
573 ((JPopupMenu) parent).setSelected(this);
577 model.setArmed(false);
579 if (parent != null && parent instanceof JPopupMenu)
580 ((JPopupMenu) parent).getSelectionModel().clearSelection();
585 * Method of the MenuElement interface.
587 * @return $MenuElement[]$ Returns array of sub-components for this menu
588 * item. By default menuItem doesn't have any subcomponents and so
589 * empty array is returned instead.
591 public MenuElement[] getSubElements()
593 return new MenuElement[0];
597 * Returns reference to the component that will paint this menu item.
599 * @return $Component$ Component that will paint this menu item.
600 * Simply returns reference to this menu item.
602 public Component getComponent()
608 * Adds a MenuDragMouseListener to this menu item. When mouse
609 * is dragged over the menu item the MenuDragMouseEvents will be
610 * fired, and these listeners will be called.
612 * @param listener The new listener to add
614 public void addMenuDragMouseListener(MenuDragMouseListener listener)
616 listenerList.add(MenuDragMouseListener.class, listener);
620 * Removes a MenuDragMouseListener from the menuItem's listener list.
622 * @param listener The listener to remove
624 public void removeMenuDragMouseListener(MenuDragMouseListener listener)
626 listenerList.remove(MenuDragMouseListener.class, listener);
630 * Returns all added MenuDragMouseListener objects.
632 * @return an array of listeners
636 public MenuDragMouseListener[] getMenuDragMouseListeners()
638 return (MenuDragMouseListener[]) listenerList.getListeners(MenuDragMouseListener.class);
642 * Adds an MenuKeyListener to this menu item. This listener will be
643 * invoked when MenuKeyEvents will be fired by this menu item.
645 * @param listener The new listener to add
647 public void addMenuKeyListener(MenuKeyListener listener)
649 listenerList.add(MenuKeyListener.class, listener);
653 * Removes an MenuKeyListener from the menuItem's listener list.
655 * @param listener The listener to remove
657 public void removeMenuKeyListener(MenuKeyListener listener)
659 listenerList.remove(MenuKeyListener.class, listener);
663 * Returns all added MenuKeyListener objects.
665 * @return an array of listeners
669 public MenuKeyListener[] getMenuKeyListeners()
671 return (MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class);
675 * Returns a string describing the attributes for the <code>JMenuItem</code>
676 * component, for use in debugging. The return value is guaranteed to be
677 * non-<code>null</code>, but the format of the string may vary between
680 * @return A string describing the attributes of the <code>JMenuItem</code>.
682 protected String paramString()
684 // calling super seems to be sufficient here...
685 return super.paramString();
689 * Returns the object that provides accessibility features for this
690 * <code>JMenuItem</code> component.
692 * @return The accessible context (an instance of
693 * {@link AccessibleJMenuItem}).
695 public AccessibleContext getAccessibleContext()
697 if (accessibleContext == null)
699 AccessibleJMenuItem ctx = new AccessibleJMenuItem();
700 addChangeListener(ctx);
701 accessibleContext = ctx;
704 return accessibleContext;
708 * Provides the accessibility features for the <code>JMenuItem</code>
711 * @see JMenuItem#getAccessibleContext()
713 protected class AccessibleJMenuItem extends AccessibleAbstractButton
714 implements ChangeListener
716 private static final long serialVersionUID = 6748924232082076534L;
718 private boolean armed;
719 private boolean focusOwner;
720 private boolean pressed;
721 private boolean selected;
724 * Creates a new <code>AccessibleJMenuItem</code> instance.
726 AccessibleJMenuItem()
732 * Receives notification when the menu item's state changes and fires
733 * appropriate property change events to registered listeners.
735 * @param event the change event
737 public void stateChanged(ChangeEvent event)
739 // This is fired in all cases.
740 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
741 Boolean.FALSE, Boolean.TRUE);
743 ButtonModel model = getModel();
745 // Handle the armed property.
751 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
752 AccessibleState.ARMED, null);
760 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
761 null, AccessibleState.ARMED);
765 // Handle the pressed property.
766 if (model.isPressed())
771 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
772 AccessibleState.PRESSED, null);
780 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
781 null, AccessibleState.PRESSED);
785 // Handle the selected property.
786 if (model.isSelected())
791 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
792 AccessibleState.SELECTED, null);
800 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
801 null, AccessibleState.SELECTED);
805 // Handle the focusOwner property.
811 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
812 AccessibleState.FOCUSED, null);
820 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
821 null, AccessibleState.FOCUSED);
827 * Returns the accessible role for the <code>JMenuItem</code> component.
829 * @return {@link AccessibleRole#MENU_ITEM}.
831 public AccessibleRole getAccessibleRole()
833 return AccessibleRole.MENU_ITEM;