Java Swing: Menus and Toolbars, Part 5

In part five in this book excerpt series on Swing menus and toolbars from Java Swing, 2nd Edition, learn how to attach menus to menu bars with the JMenu class.

The JMenu Class

The JMenu class represents the anchored menus attached to a JMenuBar or another JMenu. Menus directly attached to a menu bar are called top-level menus. Submenus, on the other hand, are not attached to a menu bar but to a menu item that serves as its title. This menu item title is typically marked by a right arrow, indicating that its menu appears alongside the menu item if the user selects it. See Figure 14-11.

JMenu is a curious class. It contains a MenuUI delegate, but it uses a ButtonModel for its data model. To see why this is the case, it helps to visualize a menu as two components: a menu item and a pop-up menu. The menu item serves as the title. When it is pressed, it signals the pop-up menu to show itself either below or directly to the right of the menu item. JMenu actually extends the JMenuItem class, which makes it possible to implement the title portion of the menu. This, in effect, makes it a specialized button. On some platforms you can use the mnemonic property of the JMenuItem superclass to define a shortcut for the menu's title and, consequently, the menu. In addition, you can use the enabled property of JMenuItem to disable the menu if desired.

As with pop-up menus, you can add or insert JMenuItem, Component, or Action objects in the pop-up portion of the menu by calling the add( ) and insert( ) methods. You can also add a simple string to the menu; JMenu creates the corresponding JMenuItem object for you internally. The JMenu class assigns an integer index to each menu item and orders them based on the layout manager used for the menu. You can also add separators to the menu by using the addSeparator( ) method.

Warning: You cannot use keyboard accelerators with JMenu objects (top-level or submenu), because accelerators trigger actual program actions, not simply the display of a menu from which actions can be chosen. On some platforms you can use the setMnemonic( ) method to set a shortcut to bring up the menu, but the only universal, reliable approach is to assign keyboard accelerators to the non-submenu JMenuItems that trigger program actions.

You can programmatically cause the submenu to pop up on the screen by setting the popupMenuVisible property to true. Be aware that the pop up does not appear if the menu's title button is not showing.

Properties

The JMenu properties are listed in Table 14-8. JMenu uses a JPopupMenu to represent its list of menu items. If you wish to access that underlying menu, you can do so using the popupMenu property. The popupMenuVisible property tracks whether the menu's pop-up portion is currently visible. As noted, setting this to true when the title button is visible causes the pop up to appear. JMenu also contains a selected property, which indicates if the user has selected the title button of the menu. Both properties should mirror each other.

The topLevelMenu property has the value true if this JMenu is directly attached to a menu bar and is not a submenu. item is an indexed property that allows access to each of the JMenuItem objects in the menu, while itemCount maintains a count of all of the JMenuItem objects that are present. The delay property specifies the amount of time, in milliseconds, that the underlying menu waits to appear or disappear after receiving the corresponding event. The delay must be set to a positive integer, or setDelay( ) throws an IllegalArgumentException.

The menuComponent property is a more generalized version of the item property; it returns the component at the given index as a Component rather than as a JMenuItem. In addition, the menuComponentCount property retains a count of the menu items, separators, and other components currently in the menu. The menuComponents property lets you access each of the items in the menu, returned as an array of Component objects.

The componentOrientation property is used to accommodate non-Western languages in which text does not flow left to right. JMenu overrides this property in order to properly pass changes on to the JPopupMenu delegate it uses.

Warning: The tearOff property is not yet implemented and is reserved for (increasingly dubious) future use in Swing. Trying to use it throws an Error (rather than something more appropriate like an UnsupportedOp-erationException). Since an Error is supposed to indicate a catastrophic failure of the virtual machine, using this property will almost certainly crash your application.

Menu Items

Add various elements to the menus. Objects from both JMenuItem and JComponent can be added, but the latter functions best if it implements the MenuElement interface. If you specify a String as the parameter, a menu item with the appropriate label is created. If you specify an Action, its text and icon properties are used to derive an appropriate JMenuItem, and its text is placed to the right of the icon. It retains its association with the action and is updated to reflect changes to its properties. The resulting JMenuItem is returned, which you can use to alter its formatting.

public void 468addSeparator( )

Add a separator to the menu. Typically, a separator consists of a single horizontal line drawn across the menu.

Insert a specific menu item at a particular index. The index must be positive, or the method throws an IllegalArgumentException. You can pass in a JMenuItem, a String, or an Action to these methods. If you specify a String as the parameter, a menu item with the appropriate label is created. If you specify an Action, its text and icon properties are used to derive an appropriate JMenuItem, and its text is placed to the right of the icon. As usual, the menu retains its association with the action. The resulting JMenuItem is returned, which you can use to alter its formatting. All menu items that were previously at or after the specified position are increased by one.

public void insertSeparator(int index)

Insert a horizontal separator at the position specified by the integer index. The index must be positive, or the method throws an IllegalArgumentException. All menu items' indices that were previously at or after the specified position are increased by one.

public void remove(JMenuItem item)
public void remove(int index)

Remove the menu item that matches the JMenuItem passed in or that currently occupies the specified integer index. If there are no matches (or if the position does not exist), no changes are made to the menu. If the function is successful, all menu items' indices following the removed menu item are reduced by one.

public void removeAll( )

Remove all of the items from the menu.

Miscellaneous

public void updateUI( )

Force the default user interface manager to update itself, thus resetting the delegate to display a new MenuUI.

public void setMenuLocation(int x, int y)

Set a custom location at which the menu appears when shown.

public boolean isMenuComponent(Component c)

Determine whether the component c is present anywhere in the menu. This method searches all submenus as well.

public String paramString( )

Return a String specifying the current state of the menu properties (intended for debugging purposes).

Event

JMenu objects fire a MenuEvent when the user has selected or deselected the menu's title button. The JMenu object contains the standard addChangeListener( ) and removeChangeListener( ) methods for maintaining a list of MenuEvent subscribers.

Our Actions are all instances of the inner class MenuActions. As we add each Action to the menu, it creates an appropriate JMenuItem (with the image left-justified) and returns it to us. This allows us to manipulate the resulting menu item in any way we want; in this case, we add a mnemonic for each item. You can run this program on various platforms to see if they support mnemonics. You shouldn't rely on mnemonics as a key part of your user interface in a program intended for multiple platforms (in fact, you should avoid setting them at all unless you are sure the platform supports them).

The resulting program produces a menu bar with a single menu, as shown in Figure 14-13. The menu contains four menu items and is similar in appearance to the pop-up example. When the user clicks any menu item, Swing generates an ActionEvent to be processed by the actionPerformed( ) method of our MenuAction class. As in the previous examples, this results in the name of the menu item being printed. For variety, we have added a simple JTextPane to display the results of our menu choice, instead of using the system output. See Chapters 19 and 22 and for more information on JTextPane.

Figure 14-13.A set of menu items with icons and mnemonics

The MenuEvent Class

This is a simple event that tells listeners that the target menu has been raised, selected, or canceled. Note that it doesn't tell which one has occurred. The listener defines three separate methods that can be called to deliver the menu event; each one tells exactly what happened.

Constructor

public MenuEvent(Object source)

The constructor takes a reference to the object that fires the event.

The MenuListener Interface

The MenuListener interface, which is the conduit for receiving MenuEvents, specifies three methods. One method is called when the menu is canceled; the other two are called when the title button of the menu is selected or deselected. This interface must be implemented by any listener object that needs to be notified of changes to the menu object.

Methods

public abstract void menuCanceled(MenuEvent e)

This method is called when the menu is canceled or removed from the screen.

public abstract void menuDeselected(MenuEvent e)

This method is called when the target menu's title button is deselected.

public abstract void menuSelected(MenuEvent e)

This method is called when the target menu's title button is selected.