ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Java Swing, 2nd Edition

Java Swing: Menus and Toolbars, Part 3

by Robert Eckstein, Marc Loy, Dave Wood, James Elliott, Brian Cole

In part three in this book excerpt series on Swing menus and toolbars from Java Swing, 2nd Edition, learn about the JMenuItem class.

The JMenuItem Class

Before discussing menus, we should introduce the JMenuItem class. Figure 14-6 shows the class diagram for the JMenuItem component.

Figure 14-6. JMenuItem class diagram

Related Reading

Java Swing
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole

A JMenuItem serves as a wrapper for strings and images to be used as elements in a menu. The JMenuItem class is essentially a specialized button and extends the AbstractButton class. Its behavior, however, is somewhat different from standalone buttons. When the mouse pointer is dragged over a menu item, Swing considers the menu item to be selected. If the user releases the mouse button while over the menu item, it is considered to be chosen and should perform its action.

There is an unfortunate conflict in terminology here. Swing considers a menu item selected when the mouse moves over it, as updated by the MenuSelectionManager and classes that implement the MenuElement interface. On the other hand, Swing considers a button selected when it remains in one of two persistent states, such as a checkbox button remaining in the checked state until clicked again. So when a menu item is selected, its button model is really armed. Conversely, when a menu item is deselected, its button model is disarmed. Finally, when the user releases the mouse button over the menu item, the button is considered to be clicked, and the AbstractButton's doClick( ) method is invoked.

Menu Item Shortcuts

Menu items can take both keyboard accelerators and (on some platforms) mnemonics. Mnemonics are an artifact of buttons; they appear as a single underline below the character that represents the shortcut. Keyboard accelerators, on the other hand, are inherited from JComponent. With menu items, they have the unique side effect of appearing in the menu item. (Their exact appearance and location is up to the L&F.) Figure 14-7 shows both mnemonics and keyboard accelerators.

Figure 14-7. Mnemonics and keyboard accelerators

Keyboard accelerators and mnemonics perform the same function: users can abbreviate common GUI actions with keystrokes. However, a mnemonic can be activated only when the button (or menu item) it represents is visible on the screen. Menu item keyboard accelerators can be invoked any time the application has the focus-- whether the menu item is visible or not. Also, as noted, accelerators work on all platforms and all L&Fs while mnemonics are less universal. Menus may be assigned both at once.

Let's look at programming both cases. Keyboard accelerators typically use a variety of keystrokes: function keys, command keys, or an alphanumeric key in combination with one or more modifiers (e.g., Shift, Ctrl, or Alt). All of these key combinations can be represented by the javax.swing.KeyStroke class, but only some of them are appropriate for the platform and L&F in use. Hence, you can assign a keyboard accelerator to a menu item by setting its accelerator property with a KeyStroke object configured using the default toolkit's menuShortcutKeyMask property, as follows:

JMenuItem m = new JMenuItem("Copy");
m.setAccelerator(KeyStroke.getKeyStroke('C',
    Toolkit.getDefaultToolkit(  ).getMenuShortcutKeyMask(  ), false));

Under Metal, this sets the accelerator to Ctrl-C, which is the letter C typed in combination with the Ctrl key. The accelerator appears at the right side of the menu item (though again, the position is up to the L&F). The KeyStroke class is covered in more detail in Chapter 27.

The second, less universal, way to set a shortcut is through the mnemonic property of the AbstractButton superclass:

JMenuItem mi = new JMenuItem("Copy");
mi.setMnemonic('C');

The mnemonic property underlines the character you pass into the setMnemonic( ) method. Note that mnemonic characters cannot take modifiers; they are simple letters. Be sure to use a letter that exists in the menu item's label. Otherwise, nothing is underlined, and the user will not know how to activate the keyboard shortcut. Also be sure to set up mnemonics only if you're running on a platform and L&F that support them.

As of SDK 1.4, you can use the displayedMnemonicIndex property to cope with menu items containing multiple copies of the character you're using as a mnemonic, if it makes more sense for a later instance to be underlined (for example, the common Save As menu item in which the uppercase "A" should get the underline). To achieve this, once you set up the mnemonic, call setDisplayedMnemonicIndex with a value of 5.

Images

In Swing, menu items can contain (or consist entirely of) icons. This can be a visual aid if the icon can convey the intended meaning more clearly. You can pass an Icon object to the constructor of the JMenuItem class as follows:

JMenu menu = new JMenu("Justify");

// The first two menu items contain text and an image. The third
// uses only the image.
menu.add(new JMenuItem("Center", new ImageIcon("center.gif")));
menu.add(new JMenuItem("Right", new ImageIcon("right.gif")));
menu.add(new JMenuItem(new ImageIcon("left.gif")));

By default, the text is placed to the left of the image. This is shown on the left in Figure 14-8. As you can see, this often misaligns the images to the right of the text, especially if there is a menu item consisting only of an image. If the menu item images are all the same width, you can improve the appearance of your menus by altering the text's position using the setHorizontalTextAlignment( ) method:

JMenu menu = new JMenu("Justify");

// The first two menu items contain text and an image. The third
// uses only the image. The text is now set to the right.
JMenuItem item1= new JMenuItem("Center", new ImageIcon("center.gif")));
item1.setHorizontalTextAlignment(SwingConstants.RIGHT);
JMenuItem item2= new JMenuItem("Right", new ImageIcon("right.gif")));
item2.setHorizontalTextAlignment(SwingConstants.RIGHT);

// Now add the menu items to the menu.
menu.add(item1);
menu.add(item2);
menu.add(new JMenuItem(new ImageIcon("left.gif")));

Figure 14-8. Image and text placement in menu items

This positions the text on the other side of the images, as shown on the right of Figure 14-8. You can trace the setHorizontalTextAlignment( ) method up the class hierarchy to the AbstractButton class. As we mentioned before, the JMenuItem class is a button object with respect to its text and image. AbstractButton contains a setVerticalTextAlignment( ) method as well, so if the accompanying image is taller than the menu item text, you can use this method to set the text's vertical position as well. (See the AbstractButton class in Chapter 5 and the OverlayLayout class in Chapter 11 for more information about alignment with menu items and buttons.) The image is placed to the left of the text if you construct a menu item from an Action object (more on this later in the chapter).

Java supports image transparency, so if you require some parts of an image to be transparent, you can specify a "transparent" color in the GIF file (many paint programs allow you to do this), or you can create a specialized color filter that seeks out specific pixel colors and changes their opacity before passing the resulting Image onto the menus. The former is much easier.

Event Handling

There are a number of ways to process events from menu items. Because menu items inherit ActionEvent functionality from AbstractButton, one approach is to assign an action command to each menu item (this is often done automatically with named components) and attach all of the menu items to the same ActionListener. Then, in the actionPerformed( ) method of the listener, use the event's getActionCommand( ) method to obtain the action command of the menu item generating the event. This tells the listener which menu item has been clicked, allowing it to react accordingly. This is the approach used in IntroExample.java earlier in this chapter and PopupMenuExample.java, which is discussed later.

Alternatively, you can register a separate ActionListener class with each menu item, which takes the guesswork out of determining the menu item selected. However, Swing allows you to go a step further. The most object-oriented approach is to create a specialized Action class that corresponds to each of the tasks a user might request of your application. This lets you bundle the code for each program action together with the action's name, icon, keystrokes, and other attributes in one place. You can then use this Action to create the menu item, which automatically sets the item's text, image, accelerator, and so on.

This technique is particularly powerful if you want to be able to invoke the same action in multiple ways (such as from a toolbar as well as a menu). You can use the same Action instance to create the menu item and toolbar button, and they'll both have appropriate labels and appearances. If the application needs to disable the action because it's not currently appropriate, calling setEnabled on the Action instance automatically updates all user interface elements associated with the action (thus dimming both your menu item and toolbar button). Similarly, changing other attributes of the action, such as its name or icon, automatically updates any associated user-interface components.

Although prior to SDK 1.3 it wasn't possible to construct a JMenuItem from an Action directly, adding the Action to a JMenu or JPopupMenu had the same effect: the menu would create and configure an appropriate JMenuItem for you.

Properties

The properties for the JMenuItem class are shown in Table 14-4. Most of the properties shown are superclass properties reconfigured to ensure that the menu item's "button" acts like a menu item should. The borderPainted property is always false; menu items never take a border. The focusPainted property is also false to ensure that a focus rectangle is never drawn around the menu item. horizontalTextPosition and horizontalAlignment are both initialized to JButton.LEFT. This places the text to the left of the image icon and places the text and image icon on the left side of the menu item. (See the previous example for information on how to reconfigure this.)

Table 14-4: JMenuItem properties

Property

Data type

get

is

set

Default value

acceleratorb

KeyStroke

·

 

·

null

accessibleContexto

Accessible Context

·

 

 

JMenuItem.AccessibleJMenuItem( )

armedb, o

boolean

 

· ·

false

borderPaintedo

boolean

 

· ·

false

componento

Component

·

 

 

 

enabledo

boolean

 

· ·

true

focusPaintedo

boolean

 

· ·

false

horizontalAlignmento

int

·

 

·

JButton.LEFT

horizontalTextPositiono

int

·

 

·

JButton.LEFT

menuDragMouseListeners1.4

MenuDragMouseListener[ ]

·

 

 

 

menuKeyListeners1.4

MenuKeyListener[ ]

·

 

 

 

modelo

ButtonModel

·

 

·

DefaultButtonModel( )

subElementso

MenuElement[ ]

·

 

 

 

UIb

MenuItemUI

 

 

· From L&F

UIClassIDo

String

·

 

 

"MenuItemUI"

1.4since 1.4, bbound, ooverridden
See also properties from the AbstractButton class (Table 5-4).

The accelerator property sets the keyboard accelerator for the menu item; the accelerator is typically drawn to the right of the menu item string. The armed property simply maps a boolean down to the armed state of the component model, ButtonModel. You can use this to programmatically select the menu item, if needed. The enabled property is a boolean that indicates whether the user can select the menu item. If the menu item is disabled, JMenuItem automatically grays the text and associated image. As discussed earlier, the most powerful way to control the enabled state of a menu item is to associate it with an Action object so that it automatically tracks the action's enabled state. The subElements property provides an array of submenus contained in this menu item.

Constructors

JMenuItem(  )
JMenuItem(Action action)
JMenuItem(Icon icon)
JMenuItem(String string)
JMenuItem(String string, Icon icon)
JMenuItem(String string, int mnemonic)

Create a menu item with the appropriate icon or string. You also have the option to specify a mnemonic if you initialize with a string. Since Version 1.3, you can use the properties of an Action to directly configure the properties of the JMenuItem.

Events

JMenuItems send many different kinds of events. Perhaps the most important are ActionEvents, which are fired when an item is selected. ChangeEvents are fired when button properties change. Methods for adding and removing listeners for these events are inherited from AbstractButton.

JMenuItem also uses special events for reporting mouse motions and key presses on top of the menu item. These are the MenuDragMouseEvent and MenuKeyEvent. Here are the methods for registering listeners for these events:

addMenuDragMouseListener (MenuDragMouseListener 1)
removeMenuDragMouseListener (MenuDragMouseListener 1)

These methods add or remove a specific MenuDragMouseListener interested in being notified when there is a MenuDragMouseEvent.

addMenuKeyListener (MenuKeyListener 1)
removeMenuKeyListener (MenuKeyListener 1)

These methods add or remove a specific MenuKeyListener interested in being notified when there is a MenuKeyEvent.

The following methods provide support for firing these events, though you will probably never need to call them:

public void processMenuDragMouseEvent (MenuDragMouseEvent e)

Fire a specific MenuDragMouseEvent notification based on the type of MouseEvent that was observed. If the MouseEvent listed was MOUSE_ENTERED, for example, the menu invokes the fireMenuDragMouseEntered( ) method.

public void processMenuKeyEvent (MenuKeyEvent e)

Fire a specific MenuKeyEvent notification based on the type of MenuKeyEvent that was observed. If the MenuKeyEvent listed was KEY_RELEASED, for example, the menu invokes the fireMenuKeyReleased( ) method.

Method

public void updateUI(  )

Force the current UI manager to reset the current delegate for the component, thus updating the component's L&F.

Menu Element Interface

public void menuSelectionChanged(boolean isIncluded)
public MenuElement[ ] getSubElements(  )
public Component getComponent(  )
public void processMouseEvent(MouseEvent event, 
   MenuElement path[ ], MenuSelectionManager manager)
public void processKeyEvent(KeyEvent event, MenuElement path[ ], 
   MenuSelectionManager manager)

Implement the MenuElement interface, discussed later in this chapter.

The MenuDragMouseEvent Class

Swing generates a series of events while the mouse is dragging across an open menu. One event, MenuDragMouseEvent, describes the drag in relation to a particular menu item. You can listen for these events by adding an object that implements MenuDragMouseListener to the addMenuDragMouseListener( ) method of JMenuItem. The object implementing MenuDragMouseListener will have four separate methods that can be invoked in response to a mouse drag inside a menu; each one indicates exactly what happened with the drag. Table 14-5 shows the properties of the MenuDragMouseEvent.

Properties

Table 14-5: MenuDragMouseEvent properties

Property

Data type

get

is

set

Default value

clickCounto

int

·

 

 

 

ido

int

·

 

·

 

manager

MenuSelectionManager

·

 

 

 

modifierso

Object

·

 

·

 

path

MenuElement[ ]

·

 

 

 

popupTriggero

boolean

 

·

 

 

source

Object

·

 

 

 

wheno

long

·

 

 

 

xo

int

·

 

 

 

yo

int

·

 

·

 

ooverridden
See also java.awt.event.MouseEvent.

There are no defaults for the event; all properties are set in the constructor. The source property indicates the object that sent the event. The id property describes the type of event that was fired. The when property gives the event a timestamp. The modifiers property allows you to test various masks to see which mouse button is being pressed, as well as the Alt, Ctrl, Shift, and Meta keys. The x and y properties give the current location of the mouse pointer relative to the component in question. The clickCount property describes how many times a mouse button has been clicked prior to this drag. The popupTrigger property indicates whether this mouse event should cause a popup menu to appear. The path property gives an ordered array of MenuElement objects, describing the path to this specific menu. Finally, the manager property contains a reference to the current MenuSelectionManager for this menu system.

Constructor

public MenuDragMouseEvent(Component source, int id, long when, int modifiers, 
   int x, int y, int clickCount, boolean popupTrigger, MenuElement[ ] path, 
   MenuSelectionManager manager)

Initialize each of the properties described in Table 14-5 with the specified values.

The MenuDragMouseListener Interface

The MenuDragMouseListener interface, which is the conduit for receiving the MenuDragMouseEvent objects, contains four methods. One method is called when the mouse is dragged inside the menu item, the second when the mouse is released inside the menu item. Finally, the last two are called when the mouse is dragged into a menu item, or dragged out of a menu item.

Methods

public abstract void menuDragMouseDragged(PopupMenuEvent e)

Called when the mouse is dragged inside of a menu item.

public abstract void menuDragMouseReleased(PopupMenuEvent e)

Called when the mouse has been released inside of a menu item.

public abstract void menuDragMouseEntered(PopupMenuEvent e)

Called when the mouse is being dragged, and has entered a menu item.

public abstract void menuDragMouseExited(PopupMenuEvent e)

Called when the mouse is being dragged, and has exited a menu item.

The MenuKeyEvent Class

Swing also generates an event when a specific menu item receives a key event. Note that the key event does not have to be directed at the specific menu (i.e., an accelerator or mnemonic). Instead, the menu item responds to any key events generated while the menu pop up containing it is showing on the screen. You can listen for these events by adding an object that implements MenuKeyListener to the addMenuKeyListener( ) method of JMenuItem. The object implementing MenuKeyListener will have three separate methods that can be invoked in response to a menu key event.

Table 14-6 shows the properties of MenuKeyEvent. There are no defaults for the event; all properties are set in the constructor. The source property indicates the object that sent the event. The id property describes the type of event that was fired. The when property gives the event a timestamp. The modifiers property allows you to test various masks to see which mouse button is being pressed, as well as the Alt, Ctrl, Shift, and Meta keys. The keyCode and keyChar properties describe the key that was actually pressed. The path property gives an ordered array of MenuElement objects, describing the path to this specific menu. Finally, the manager property contains a reference to the current MenuSelectionManager.

Table 14-6: MenuKeyEvent properties

Property

Data Type

get

is

set

Default Value

ido

int

·

 

·

 

keyCharo

char

·

 

·

 

keyCodeo

int

·

 

 

 

manager

MenuSelectionManager

·

 

 

 

modifierso

Object

·

 

·

 

path

MenuElement[ ]

·

 

 

 

source

Object

·

 

 

 

wheno

long

·

 

 

 

ooverridden
See also java.awt.event.keyEvent.

Constructor

public MenuDragMouseEvent(Component source, int id, long when, 
   int keyCode, char keyChar, MenuElement[ ] path, MenuSelectionManager manager)

This constructor takes each of the properties described in Table 14-6.

The MenuKeyListener Interface

The MenuKeyListener interface, which is the conduit for receiving the MenuKeyEvent objects, contains three methods. One method is called when a key is typed (i.e., pressed and released) while the second is called after a key is pressed. This third is called after a key is released. Note that if a key is pressed and held down for a few seconds, Swing emulates the traditional key behavior: it considers the key both "typed" and "pressed" again.

Methods

public abstract void menuKeyTyped(MenuKeyEvent e)

Called when a key intended for this menu element is both pressed and released.

public abstract void menuKeyPressed(MenuKeyEvent e)

Called when a key intended for this menu element is pressed.

public abstract void menuKeyReleased(MenuKeyEvent e)

Called when a key intended for this menu element is released.

Menu items cannot exist by themselves; they must be embedded in menus. Swing implements two closely related styles of menus: anchored menus and pop-up menus. Swing uses the JMenu and JPopupMenu classes to implement these menus.

In the next installment, learn about the JPopupMenu Class.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.