|
|
|
|
Java Swing, 2nd EditionBy Marc Loy, Robert Eckstein, David Wood, James Elliott, Brian Cole2nd Edition November 2002 0-596-00408-7, 4087 1280 pages, $54.95 US, $85.95 CA |
Chapter 14
Menus and Toolbars
In this chapter:
Introducing Swing Menus
Menu Bar Selection Models
The JMenuBar Class
The JMenuItem Class
The JPopupMenu Class
The JMenu Class
Selectable Menu Items
Toolbars
This chapter discusses Swing menus and toolbars. Menus are the richer and more flexible of the two, so they encompass most of the chapter. They tend to be the first thing users explore in learning a new application, so it's fitting that Swing provides a great deal of freedom in laying out menu components.
Toolbars allow you to group buttons, combo boxes, and other elements together in repositionable panels; these tools can assist the user in performing many common tasks. You can add any component to a Swing toolbar, even non-Swing components. In addition, Swing allows the toolbar to be dragged from the frame and positioned inside a child window for convenience.
Introducing Swing Menus
Swing menu components are subclasses of
JComponent. Consequently, they have all the benefits of a Swing component, and you can treat them as such with respect to layout managers and containers.Here are some notable features of the Swing menu system:
- Icons can augment or replace menu items.
- Menu items can be radio buttons.
- Keyboard accelerators can be assigned to menu items; these appear next to the menu item text.
- Most standard Swing components can be used as menu items.
Swing provides familiar menu separators, checkbox menu items, pop-up menus, and submenus for use in your applications. In addition, Swing menus support keyboard accelerators and "underline" style (mnemonic) shortcuts, and you can attach menu bars to the top of Swing frames with a single function that adjusts the frame insets accordingly. On the Macintosh, your application can be configured so that this method places the menu bar at the top of the screen, where users expect to find it. Figure 14-1 defines the various elements that make up the menu system in Swing.
Figure 14-1. The elements of the Swing menu system
![]()
Note that not all platforms support underline-style mnemonics. Notably, on the Macintosh (which has never provided this sort of user interface) mnemonics do not appear at all in the system menu bar, and though they are visible in the actual menus, they do not work in either place. If your application uses mnemonics, you should consider grouping the code to set them up into a separate method that is invoked only when running on a platform that supports them.
All platforms do support accelerators (shortcuts) but have different conventions about the key used to invoke them. You can take advantage of the
ToolkitmethodgetMenuShortcutKeyMaskto always use the right key.Menu Hierarchy
The class diagram for Swing menus is shown in Figure 14-2.
Figure 14-2. Swing menu diagram
![]()
You might be surprised to find
AbstractButtonin the hierarchy, but menus and menu items have many features in common with Swing buttons. For example, menu items can be highlighted (when the mouse pointer passes over them), they can be clicked to indicate that the user has made a choice, they can be disabled and grayed like buttons, and they can be assigned action commands to assist with event handling.JCheckBoxMenuItemandJRadioButtonMenuItemcan even be toggled between two selection states. Since Swing menu components share much of the functionality of Swing buttons, it is appropriate and efficient that they inherit fromAbstractButton.It may also seem surprising that
JMenuinherits fromJMenuItem, instead of vice-versa. This is because eachJMenucontains an implicit menu item that serves as the title of the menu. You'll often hear this part of the menu called the title button. When the user presses or drags the mouse cursor over the title button, the corresponding menu appears. Note, however, that menus do not have to be anchored to a menu bar. You can embed them in other menus, where they act as submenus. This means that the title button must be able to act as a menu item, which would not be possible if the hierarchy was reversed. We discuss this behavior in more detail when we cover theJMenuclass later in this chapter.Almost all of the menu classes implement the
MenuElementinterface. TheMenuElementinterface outlines standardized methods that dictate how each Swing menu component behaves when it encounters user input, such as keyboard or mouse events. Swing menu classes typically process these mouse and keyboard events and pass notifications to the component delegates, which handle any necessary redrawing of the component. These methods work in tandem with theMenuSelectionManagerclass. While you rarely need to implement theMenuElementinterface, it helps to know how it works. We show how to implement this interface later in the chapter.Getting Your Feet Wet
Okay, it's time to jump in. Here is a flashy program that introduces much of the basic Swing menu functionality:
// IntroExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;public class IntroExample extends JMenuBar {String[ ] fileItems = new String[ ] { "New", "Open", "Save", "Exit" };String[ ] editItems = new String[ ] { "Undo", "Cut", "Copy", "Paste" };char[ ] fileShortcuts = { 'N','O','S','X' };char[ ] editShortcuts = { 'Z','X','C','V' };public IntroExample( ) {JMenu fileMenu = new JMenu("File");JMenu editMenu = new JMenu("Edit");JMenu otherMenu = new JMenu("Other");JMenu subMenu = new JMenu("SubMenu");JMenu subMenu2 = new JMenu("SubMenu2");// Assemble the File menus with mnemonics.ActionListener printListener = new ActionListener( ) { public void actionPerformed(ActionEvent event) {System.out.println("Menu item [" + event.getActionCommand( ) +"] was pressed.");}};for (int i=0; i < fileItems.length; i++) {JMenuItem item = new JMenuItem(fileItems[i], fileShortcuts[i]);item.addActionListener(printListener);fileMenu.add(item);}// Assemble the File menus with keyboard accelerators.for (int i=0; i < editItems.length; i++) {JMenuItem item = new JMenuItem(editItems[i]);item.setAccelerator(KeyStroke.getKeyStroke(editShortcuts[i],Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( ), false));item.addActionListener(printListener);editMenu.add(item);}// Insert a separator in the Edit menu in Position 1 after "Undo".editMenu.insertSeparator(1);// Assemble the submenus of the Other menu.JMenuItem item;subMenu2.add(item = new JMenuItem("Extra 2"));item.addActionListener(printListener);subMenu.add(item = new JMenuItem("Extra 1"));item.addActionListener(printListener);subMenu.add(subMenu2);// Assemble the Other menu itself.otherMenu.add(subMenu);otherMenu.add(item = new JCheckBoxMenuItem("Check Me"));item.addActionListener(printListener);otherMenu.addSeparator( );ButtonGroup buttonGroup = new ButtonGroup( );otherMenu.add(item = new JRadioButtonMenuItem("Radio 1"));item.addActionListener(printListener);buttonGroup.add(item);otherMenu.add(item = new JRadioButtonMenuItem("Radio 2"));item.addActionListener(printListener);buttonGroup.add(item);otherMenu.addSeparator( );otherMenu.add(item = new JMenuItem("Potted Plant",new ImageIcon("image.gif")));item.addActionListener(printListener);// Finally, add all the menus to the menu bar.add(fileMenu);add(editMenu);add(otherMenu);}public static void main(String s[ ]) {JFrame frame = new JFrame("Simple Menu Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setJMenuBar(new IntroExample( ));frame.pack( );frame.setVisible(true);}}This example creates a menu bar with three simple menus, attaching mnemonics to the menu items of the File menu and keyboard accelerators to the menu items of the Edit menu. Figure 14-3 shows a mosaic of the different menus that the program produces. It also shows how the Edit menu looks on two different platforms, with the proper accelerator key (Control or Command) used on each.
Figure 14-3. A sample of Swing menu effects
![]()
In the third menu, we've enhanced the last item with a GIF image of a potted plant. In addition, the first menu item in the Other menu is actually a submenu that pops out to a second submenu, underscoring the recursive nature of menus. If you select any of the menus, you are rewarded with a simple text output that tells you what you clicked:
Menu item [New] was pressed.Menu item [Radio 1] was pressed.Don't worry if you do not understand all the classes and methods at this point. We will examine each menu component in detail shortly.
Menu Bar Selection Models
In all GUI environments, menu components allow only one selection to be made at a time. Swing is no exception. Swing provides a data model that menu bars and menus can use to emulate this behavior: the
SingleSelectionModel.The SingleSelectionModel Interface
Objects implementing the
SingleSelectionModelinterface do exactly what its name suggests: they maintain an array of possible selections and allow one element in the array to be chosen at a time. The model holds the index of the selected element. If a new element is chosen, the model resets the index representing the chosen element and fires aChangeEventto each of the registered listeners.Properties
Objects implementing the
SingleSelectionModelinterface contain the properties shown in Table 14-1. Theselectedproperty is abooleanthat tells if there is a selection. TheselectedIndexproperty is an integer index that represents the currently selected item.
Table 14-1: SingleSelectionModel properties Property
Data type
get
is
set
Default value
selectedboolean
·
selectedIndex
int
·
·
Events
Objects implementing the
SingleSelectionModelinterface must fire aChangeEvent(not aPropertyChangeEvent) when the object modifies itsselectedIndexproperty, i.e., when the selection has changed. The interface contains the standardaddChangeListener( )andremoveChangeListener( )methods for maintaining a list ofChangeEventlisteners.
- void addChangeListener(ChangeListener listener)
- void removeChangeListener(ChangeListener listener)
- Add or remove the specified
ChangeListenerfrom the list of listeners receiving this model's change events.
Method
The
SingleSelectionModelinterface contains one other method:
- public void clearSelection( )
- Clear the selection value, forcing the
selectedproperty to returnfalse.
The DefaultSingleSelectionModel Class
Swing provides a simple default implementation of the
SingleSelectionModelinterface in theDefaultSingleSelectionModelclass.Properties
DefaultSingleSelectionModelcontains just the properties required by theSingleSelectionModelinterface, as shown in Table 14-2. TheselectedIndexproperty is an integer index that represents the currently selected item. The default value of-1indicates that there is no selection. Theselectedproperty is abooleanthat returnstrueif theselectedIndexis anything other than-1, andfalseotherwise.
Table 14-2: DefaultSingleSelectionModel properties Property
Data type
get
is
set
Default value
selected
boolean
·
false
selectedIndex
int
·
·
-1
Events and methods
The
DefaultSingleSelectionModelobject provides all the events and methods specified by theSingleSelectionModelinterface discussed earlier.The JMenuBar Class
Swing's
JMenuBarclass supersedes the AWTMenuBarclass. This class creates a horizontal menu bar component with zero or more menus attached to it.JMenuBaruses theDefaultSingleSelectionModelas its data model because the user can raise, or activate, only one of its menus at a given time. Once the mouse pointer leaves that menu, the class removes the menu from the screen (or cancels it, in Swing lingo), and all menus again become eligible to be raised. Figure 14-4 shows the class hierarchy for theJMenuBarcomponent.
Figure 14-4. JMenuBar class diagram
![]()
You can add
JMenuobjects to the menu bar with theadd( )method of theJMenuBarclass.JMenuBarthen assigns an integer index based on the order in which the menus were added. The menu bar displays the menus from left to right on the bar according to their assigned index. In theory, there is one exception: the help menu. You are supposed to be allowed to mark one menu as the help menu; the location of the help menu is up to the L&F. In practice, trying to do this results inJMenuBarthrowing anError.Menu Bar Placement
You can attach menu bars to Swing frames or applets in one of two ways. First, you can use the
setJMenuBar( )method ofJFrame,JDialog,JApplet, orJInternalFrame:JFrame frame = new JFrame("Menu");JMenuBar menuBar = new JMenuBar( );// Attach the menu bar to the frame.frame.setJMenuBar(menuBar);The
setJMenuBar( )method is analogous to thesetMenuBar( )method ofjava.awt.Frame. Like its predecessor,setJMenuBar( )allows the L&F to determine the location of the menu (typically, it anchors the menu bar to the top of a frame, adjusting the frame's internalInsetsaccordingly). BothJAppletandJDialogcontain asetJMenuBar( )method-- this means that you can add menu bars to both applets and dialogs. Either way, be sure not to confuse thesetJMenuBar( )method with the oldersetMenuBar( )method of AWT when working with Swing menus, or the compiler complains bitterly.If your application is running on a Macintosh, the Mac L&F can be configured to place menu bars at the top of the screen, where Mac users expect to find them. Setting the system property
com.apple.macos.useScreenMenuBartotrueactivates this behavior. It's disabled by default because most Java programs do not expect this behavior, and they must be coded properly to deal with it. Notably, the Aqua Human Interface Guidelines require that the menu bar is always visible. If your application has any frames that lack menu bars, whenever one of these gains focus, it causes the menu bar to disappear, much to the user's consternation. The most common way of dealing with this is to write a menu factory that generates an identical menu bar for each frame your application uses. Although this is a little extra work, the familiarity and comfort it brings your Mac users is probably worth it.The second way to add a menu bar is much less common. Recall that the
JMenuBarclass extendsJComponent. This means it can be positioned by a Swing layout manager like other Swing components. For example, we could replace the call tosetJMenuBar( )with the following code:menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));frame.getContentPane( ).add(menuBar, BorderLayout.SOUTH);This places the menu bar at the bottom of the frame, as shown in Figure 14-5. (Note that we set a beveled border around the menu bar to help outline its location.) It would even be possible to add two or three menu bars in different locations. Swing does not require a single menu bar to be anchored to the top of a frame. Because they extend
JComponent, multiple menu bars can be positioned anywhere inside a container.
Figure 14-5. JMenuBar positioned as a Swing component
![]()
TIP: You have to add at least one named menu to a menu bar for it to gain any thickness. Otherwise, it appears as a thin line--similar to a separator.
Of course, you'd never actually want to do this without a very compelling reason. It robs the L&F of its opportunity to place the menu bar in the appropriate location. Moving something as fundamental as a menu bar is almost certain to cause confusion and usability challenges for your users; having multiple menu bars would be baffling.
Properties
The properties of the
JMenuBarclass are shown in Table 14-3.menuis an indexed property that references eachJMenuattached to the menu bar. The read-onlymenuCountproperty maintains a count of these attached menus. Remember that the single selection model allows only one menu to be activated at a time. If any menu is currently activated, theselectedproperty returnstrue; otherwise, the property returnsfalse. ThecomponentAtIndexproperty accesses the menu associated with the given index. It is similar to the indexedmenuproperty, except the contents are cast to aComponent. If there is no component associated with that index, thegetComponentAtIndex( )accessor returnsnull. Thecomponentproperty returns a reference tothis(i.e., the menu bar itself);subElementsreturns an array consisting of the menus on the menu bar.The
marginproperty controls the amount of space between the menu bar's border and its menus while theborderPaintedproperty can be used to suppress the painting of the menu bar's border even if theborderproperty has a non-nullvalue. SettingborderPaintedtofalseprevents the normal painting of the border. For more information about Swing borders, see Chapter 13.WARNING: The
helpMenuproperty is supposed to allow you to designate oneJMenuas the help menu (which has a special location in some operating systems), but this property has never been implemented, and using it throws anErroreven in SDK 1.4. You can take advantage of the fact that the menu bar uses aBoxLayoutto insert "glue" to position your (ordinary) help menu at the right edge when appropriate, but this shifts the burden of knowing when to do that (based on the current L&F) to your code, which is unfortunate.Constructor
- public JMenuBar( )
- Create and initialize an empty
JMenuBarobject.
Menu
- public JMenu add(JMenu menu)
- You can use this method to attach a
JMenuto the menu bar set. Because of theBoxLayoutofJMenuBar, menus are displayed on the menu bar from left to right in the order that youadd( )them. The method returns a reference to theJMenuthat was passed in, allowing you to string together calls--for example,menubar.add(menu).add(menuitem).
Miscellaneous
- public int getComponentIndex(Component c)
- Return the index associated with the component reference passed in. If there is no match to the component, the method returns a
-1. The only type of component it makes sense to pass in isJMenu.
- public void setSelected(Component c)
- Force the menu bar (and its associated model) to select a particular menu, which fires a
ChangeEventin the menu bar's single selection model. This method, for example, is called when a mnemonic key for a particular menu is pressed. Note that this is different than thebooleanselectedproperty listed in Table 14-3.
- public void updateUI( )
- Force the
UIManagerto refresh the L&F of the component, based on the current UI delegate.
JMenuBaralso implements the methods specified by theMenuElementinterface, which is covered later in this chapter.The JMenuItem Class
Before discussing menus, we should introduce the
JMenuItemclass. Figure 14-6 shows the class diagram for theJMenuItemcomponent.
Figure 14-6. JMenuItem class diagram
![]()
A
JMenuItemserves as a wrapper for strings and images to be used as elements in a menu. TheJMenuItemclass is essentially a specialized button and extends theAbstractButtonclass. 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
MenuSelectionManagerand classes that implement theMenuElementinterface. 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 theAbstractButton'sdoClick( )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.KeyStrokeclass, 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 itsacceleratorproperty with aKeyStrokeobject configured using the default toolkit'smenuShortcutKeyMaskproperty, 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
KeyStrokeclass is covered in more detail in Chapter 27.The second, less universal, way to set a shortcut is through the
mnemonicproperty of theAbstractButtonsuperclass:JMenuItem mi = new JMenuItem("Copy");mi.setMnemonic('C');The
mnemonicproperty underlines the character you pass into thesetMnemonic( )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
displayedMnemonicIndexproperty 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, callsetDisplayedMnemonicIndexwith a value of5.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
Iconobject to the constructor of theJMenuItemclass 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 theAbstractButtonclass. As we mentioned before, theJMenuItemclass is a button object with respect to its text and image.AbstractButtoncontains asetVerticalTextAlignment( )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 theAbstractButtonclass in Chapter 5 and theOverlayLayoutclass 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 anActionobject (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
Imageonto 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
ActionEventfunctionality fromAbstractButton, 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 sameActionListener. Then, in theactionPerformed( )method of the listener, use the event'sgetActionCommand( )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
ActionListenerclass 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 specializedActionclass 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 thisActionto 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
Actioninstance 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, callingsetEnabledon theActioninstance 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
JMenuItemfrom anActiondirectly, adding theActionto aJMenuorJPopupMenuhad the same effect: the menu would create and configure an appropriateJMenuItemfor you.Properties
The properties for the
JMenuItemclass 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. TheborderPaintedproperty is alwaysfalse; menu items never take a border. ThefocusPaintedproperty is alsofalseto ensure that a focus rectangle is never drawn around the menu item.horizontalTextPositionandhorizontalAlignmentare both initialized toJButton.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.)The
acceleratorproperty sets the keyboard accelerator for the menu item; the accelerator is typically drawn to the right of the menu item string. Thearmedproperty simply maps abooleandown to the armed state of the component model,ButtonModel. You can use this to programmatically select the menu item, if needed. Theenabledproperty is abooleanthat indicates whether the user can select the menu item. If the menu item is disabled,JMenuItemautomatically 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 anActionobject so that it automatically tracks the action's enabled state. ThesubElementsproperty 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
Actionto directly configure the properties of theJMenuItem.
Events
JMenuItems send many different kinds of events. Perhaps the most important areActionEvents, which are fired when an item is selected.ChangeEventsare fired when button properties change. Methods for adding and removing listeners for these events are inherited fromAbstractButton.
JMenuItemalso uses special events for reporting mouse motions and key presses on top of the menu item. These are theMenuDragMouseEventandMenuKeyEvent. Here are the methods for registering listeners for these events:
- addMenuDragMouseListener (MenuDragMouseListener 1)
- removeMenuDragMouseListener (MenuDragMouseListener 1)
- These methods add or remove a specific
MenuDragMouseListenerinterested in being notified when there is aMenuDragMouseEvent.
- addMenuKeyListener (MenuKeyListener 1)
- removeMenuKeyListener (MenuKeyListener 1)
- These methods add or remove a specific
MenuKeyListenerinterested in being notified when there is aMenuKeyEvent.
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
MenuDragMouseEventnotification based on the type ofMouseEventthat was observed. If theMouseEventlisted wasMOUSE_ENTERED, for example, the menu invokes thefireMenuDragMouseEntered( )method.
- public void processMenuKeyEvent (MenuKeyEvent e)
- Fire a specific
MenuKeyEventnotification based on the type ofMenuKeyEventthat was observed. If theMenuKeyEventlisted wasKEY_RELEASED, for example, the menu invokes thefireMenuKeyReleased( )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
MenuElementinterface, 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 implementsMenuDragMouseListenerto theaddMenuDragMouseListener( )method ofJMenuItem. The object implementingMenuDragMouseListenerwill 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 theMenuDragMouseEvent.Properties
There are no defaults for the event; all properties are set in the constructor. The
sourceproperty indicates the object that sent the event. Theidproperty describes the type of event that was fired. Thewhenproperty gives the event a timestamp. Themodifiersproperty allows you to test various masks to see which mouse button is being pressed, as well as the Alt, Ctrl, Shift, and Meta keys. Thexandyproperties give the current location of the mouse pointer relative to the component in question. TheclickCountproperty describes how many times a mouse button has been clicked prior to this drag. ThepopupTriggerproperty indicates whether this mouse event should cause a popup menu to appear. Thepathproperty gives an ordered array ofMenuElementobjects, describing the path to this specific menu. Finally, themanagerproperty contains a reference to the currentMenuSelectionManagerfor 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
MenuDragMouseListenerinterface, which is the conduit for receiving theMenuDragMouseEventobjects, 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
MenuKeyListenerto theaddMenuKeyListener( )method ofJMenuItem. The object implementingMenuKeyListenerwill 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. Thesourceproperty indicates the object that sent the event. Theidproperty describes the type of event that was fired. Thewhenproperty gives the event a timestamp. Themodifiersproperty allows you to test various masks to see which mouse button is being pressed, as well as the Alt, Ctrl, Shift, and Meta keys. ThekeyCodeandkeyCharproperties describe the key that was actually pressed. Thepathproperty gives an ordered array ofMenuElementobjects, describing the path to this specific menu. Finally, themanagerproperty contains a reference to the currentMenuSelectionManager.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
MenuKeyListenerinterface, which is the conduit for receiving theMenuKeyEventobjects, 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
JMenuandJPopupMenuclasses to implement these menus.The JPopupMenu Class
Pop-up menus are an increasingly popular user-interface feature. These menus are not attached to a menu bar; instead, they are free-floating menus that associate themselves with an underlying component. This component is called the invoker. Linked to specific interface elements, pop-up menus are nicely context-sensitive. They are brought into existence by a platform-dependent pop-up trigger event that occurs while the mouse is over the invoking component. In AWT and Swing, this trigger is typically a mouse event. Once raised, the user can interact with the menu normally. Figure 14-9 is an example of a pop-up menu in Swing.
Figure 14-9. A pop-up menu in Swing
![]()
You can add or insert
JMenuItem,Component, orActionobjects to the pop-up menu with theadd( )andinsert( )methods. TheJPopupMenuclass assigns an integer index to each menu item and orders them based on the layout manager of the pop-up menu. In addition, you can add separators to the menu by using theaddSeparator( )method; these separators also count as an index. Figure 14-10 shows the class diagram for theJPopupMenucomponent. Starting with SDK 1.4, pop-up menus use thePopupclass to actually draw themselves. This class is also used for other briefly displayed interface elements like tooltips.
Figure 14-10. JPopupMenu class diagram
![]()
Displaying the Pop-up Menu
Pop-up menus are usually raised by invoking the
show( )method in response to a platform-specific pop-up trigger. Theshow( )method sets thelocationandinvokerproperties of the menu before making it visible. Pop ups are automatically canceled by a variety of events, including clicking a menu item; resizing an invoking component; or moving, minimizing, maximizing, or closing the parent window. (You won't need to worry about canceling pop-up menus.) You raise the pop-up menu at the right time by checking all yourMouseEvents to see if they're the pop-up trigger. A word to the wise: if aMouseEventis the pop-up trigger, be sure not to pass it on to your superclass, or Swing could cancel the pop-up menu immediately after raising it! Also, be sure to check both pressed and released events because some platforms use one or the other. The easiest way to do that is to check all mouse events. Here's aprocessMouseEvent( )method that raises a pop-up menu upon receiving the appropriate trigger:public void processMouseEvent(MouseEvent e) {if (e.isPopupTrigger( )) {popup.show(this, e.getX( ), e.getY( ));}else {super.processMouseEvent(e);}}Note the use of
isPopupTrigger( )injava.awt.event.MouseEventto check whether the mouse event is a trigger in a platform-independent way. Since SDK 1.3,JPopupMenuhas an equivalent method you can use in the same way.When the mouse moves outside the component, Swing no longer sends pop-up trigger events to that component, and its pop-up menu cannot be raised. This gives you the opportunity to define different pop-up menus for different underlying components, adding context sensitivity to your interface.
Properties
The properties of the
JPopupMenuclass are shown in Table 14-7. Pop-up menus have many properties. Thevisibleproperty tells whether the pop-up menu is currently showing on the screen; you can use thesetVisible( )method to show or hide the pop up, but if it is a free-floating pop up, it is much easier to use theshow( )method. Thelocationproperty provides the coordinates on the screen where the pop-up menu is or has been raised. The read-onlymarginproperty gives the amount of space between the pop-up window border and an imaginary rectangle surrounding the individual menu items.
Table 14-7: JPopupMenu properties Property
Data type
get
is
set
Default value
accessibleContexto
AccessibleContext
·
JPopupMenu.accessibleJPopupMenu( )
borderPainted
boolean
·
·
true
component
Component
·
componentAtIndexi
Component
·
invoker
Component
·
·
labelb
String
·
·
""
layouto
LayoutManager
·
·
GridBagLayout( )
lightWeightPopupEnabled
boolean
·
·
getDefaultLightWeightPop-upEnabled( )
locationo
Point
·
margin
Insets
·
popupMenuListeners1.4
PopupMenuListener[ ]
·
popupSize
Dimension
·
selectionModel
SingleSelectionModel
·
DefaultSingleSelectionMo-del( )
subElements
MenuElement[ ]
·
UIb
PopupMenuUI
·
·
BasicPopupMenuUI( )
UIClassIDo
String
·
"PopupMenuUI"
visibleb, o
boolean
·
·
false
1.4since 1.4, bbound, iindexed, ooverridden See also properties from the
JMenuItemclass (Table 14-4).The
invokerproperty is a reference to the component that is responsible for hosting the pop-up menu. TheborderPaintedproperty indicates whether the pop-up menu should paint its border. Thelabelproperty gives each pop-up menu a specific label; the individual L&F is free to use or ignore this property as it sees fit. Note thatlabelis aStringand not aJLabel.componentAtIndexis an indexed property that returns the component at the specified index.The
lightWeightPopupEnabledproperty allows the programmer to enable or disable the potential use of lightweight components to represent the pop-up menu. If the property is set totrue, Swing uses a lightweight component when the pop-up is inside the top-level component's drawing space, and a heavyweight when the pop-up extends beyond its space. If your interface uses any heavyweight components, they interfere with lightweight pop ups, so you should turn off this feature. You can set the default value of this property for all pop-up menus using the staticsetDefaultLightWeightPopupEnabled( )method.Events
JPopupMenuobjects fire aPopupMenuEventunder two conditions: when the menu becomes visible or invisible, or is canceled without a menu item selection. The class contains the standardaddPopupMenuListener( )andremovePopupMenuListener( )methods for maintaining a list ofPopupMenuEventsubscribers.
- public void addPopupMenuListener(PopupMenuListener l)
- public void removePopupMenuListener(PopupMenuListener l)
- Add or remove a
PopupMenuListenerfrom the object's event queue.
The ability to be notified right before the pop-up menu becomes visible gives you the opportunity to tweak the state and contents of the menu based on the current state of your application, which can make your interface even more helpful and context-sensitive.
Note that when the pop-up menu is canceled, it also becomes invisible, so two events are potentially triggered. The cancelation event itself seems to be fired rarely in current implementations, though. If you need to know when the menu goes away, use the
popupMenuWillBecomeInvisiblehandler.Constructors
- public JPopupMenu( )
- public JPopupMenu(String title)
- Create an empty pop-up menu. The second constructor accepts a
Stringas the title of the pop-up menu.
Menu Items
- public JMenuItem add(JMenuItem menuItem)
- public Component add(Component c)
- public JMenuItem add(Action a)
- Add various elements to the pop-up menus. Objects extending either
JMenuItemorJComponentcan be added, but the latter functions best if it implements theMenuElementinterface. If you specify anAction, its many properties are used to derive an appropriateJMenuItem, and its text is placed to the right of any image icon. The item retains its association with the action so that updates to the action (changes in name, icon, enabled state, etc.) are reflected by the item. The resultingJMenuItemis then returned, which you can use to alter its formatting.
- public JMenuItem insert(Action a, int index)
- public Component insert(Component component, int index)
- Insert a specific menu item at a particular index. You can pass in a
JComponentor anActionto these methods. If you use aJComponent, it's best if it implements theMenuElementinterface. If you specify anAction, its various properties are used to derive an appropriateJMenuItem, and its text is placed to the right of any image icon. As usual, the item retains its association with the action. The resultingJMenuItemis then returned, which you can use to alter its formatting. All menu item indices that were previously at or after the specified position are incremented.
- public void addSeparator( )
- Add a separator to the pop-up menu. Typically, a separator consists of a single horizontal line drawn across the pop-up menu. Note that, like menu items, the separator counts as an index in the menu. The separator used is an instance of an inner class, not the regular
JSeparator; it is always horizontal.
Display
- public void show(Component invoker, int x, int y)
- Paint the pop-up menu at the requested coordinates. The method takes a reference to the invoking component. It is functionally equivalent to the following calls:
setInvoker( ),setLocation( ), andsetVisible( ).
- public void setPopupSize(int width, int height)
- An alternate way to establish a preferred size for the pop up. (The other way is the
popupSizeproperty, which takes aDimension.)
Miscellaneous
- public int getComponentIndex(Component c)
- Return the index associated with the component reference
c. If there is no match to the component passed in, the method returns-1.
- public static boolean getDefaultLightWeightEnabled
- Return the default value for the
lightWeightPopupEnabledproperty.
- public boolean isPopupTrigger(MouseEvent e)
- Since SDK 1.3, an alternate way to check whether a given mouse event should trigger a pop-up menu in the current L&F.
- public static void setDefaultLightWeightPopupEnabled(boolean aFlag)
- Set the default value of the
lightWeightPopupEnabledproperty, which controls whether a lightweight or heavyweight component is used for the pop up.
- public void setSelected(Component c)
- Force the pop-up menu's model to select a particular menu item. This forces a property change event in the pop-up menu's single selection model.
- public void updateUI( )
- Force the default user interface manager to update itself, thus resetting the delegate to display a new
PopupMenuUI.
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
MenuElementinterface, which is covered later in this chapter.
Using Pop-up Menus
Here is a program that demonstrates the use of the
JPopupMenuclass. The example is similar to the one that generated Figure 14-9, except that the pop up communicates events from the pop-up menu and from each of its menu items.// PopupMenuExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;import javax.swing.event.*;public class PopupMenuExample extends JPanel {public JPopupMenu popup;public PopupMenuExample( ) {popup = new JPopupMenu( );ActionListener menuListener = new ActionListener( ) {public void actionPerformed(ActionEvent event) {System.out.println("Popup menu item [" +event.getActionCommand( ) + "] was pressed.");}};JMenuItem item;popup.add(item = new JMenuItem("Left", new ImageIcon("left.gif")));item.setHorizontalTextPosition(JMenuItem.RIGHT);item.addActionListener(menuListener);popup.add(item = new JMenuItem("Center", new ImageIcon("center.gif")));item.setHorizontalTextPosition(JMenuItem.RIGHT);item.addActionListener(menuListener);popup.add(item = new JMenuItem("Right", new ImageIcon("right.gif")));item.setHorizontalTextPosition(JMenuItem.RIGHT);item.addActionListener(menuListener);popup.add(item = new JMenuItem("Full", new ImageIcon("full.gif")));item.setHorizontalTextPosition(JMenuItem.RIGHT);item.addActionListener(menuListener);popup.addSeparator( );popup.add(item = new JMenuItem("Settings . . ."));item.addActionListener(menuListener);popup.setLabel("Justification");popup.setBorder(new BevelBorder(BevelBorder.RAISED));popup.addPopupMenuListener(new PopupPrintListener( ));addMouseListener(new MousePopupListener( ));}// An inner class to check whether mouse events are the pop-up triggerclass MousePopupListener extends MouseAdapter {public void mousePressed(MouseEvent e) { checkPopup(e); }public void mouseClicked(MouseEvent e) { checkPopup(e); }public void mouseReleased(MouseEvent e) { checkPopup(e); }private void checkPopup(MouseEvent e) {if (e.isPopupTrigger( )) {popup.show(PopupMenuExample.this, e.getX( ), e.getY( ));}}}// An inner class to show when pop-up events occurclass PopupPrintListener implements PopupMenuListener {public void popupMenuWillBecomeVisible(PopupMenuEvent e) {System.out.println("Popup menu will be visible!");}public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {System.out.println("Popup menu will be invisible!");}public void popupMenuCanceled(PopupMenuEvent e) {System.out.println("Popup menu is hidden!");}}public static void main(String s[ ]) {JFrame frame = new JFrame("Popup Menu Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setContentPane(new PopupMenuExample( ));frame.setSize(300, 300);frame.setVisible(true);}}The interesting parts of this program are the methods of
MousePopupListener. These call a private method,checkPopup( ), to see if we've received an event that should raise the pop-up menu. If we get a valid trigger event, we show the pop up at the mouse location. This is an alternative to the approach of overridingprocessMouseEvent( )that was demonstrated in "Displaying the Pop-up Menu."The PopupMenuEvent Class
This is a simple event that tells listeners that the target pop-up menu is about to become visible or invisible, or that it has been canceled. Note that it doesn't tell which one has occurred. The object implementing
PopupMenuListenerwill define three separate methods that can be called by a pop-up menu; each one indicates exactly what happened with the target pop-up menu object.Constructor
- public PopupMenuEvent(Object source)
- The constructor takes a reference to the object that fired the event.
The PopupMenuListener Interface
The
PopupMenuListenerinterface, which is the conduit for receiving thePopupMenuEventobjects, contains three methods. One method is called when the pop up is canceled, and the other two indicate that the pop up is about to show or hide itself. This interface must be implemented by any listener object that wishes to be notified of changes to the pop-up menu.Methods
- public abstract void popupMenuCanceled(PopupMenuEvent e)
- Called when the target pop-up menu is canceled or removed from the screen. (This seems to be called rarely in practice.)
- public abstract void popupMenuWillBecomeInvisible(PopupMenuEvent e)
- Called when the pop-up menu is about to be removed from the screen.
- public abstract void popupMenuWillBecomeVisible(PopupMenuEvent e)
- Called when the pop-up menu is about show itself on the screen. This is an excellent opportunity to update the contents of the menu (or their enabled states) based on current application conditions.
The JMenu Class
TheJMenuclass represents the anchored menus attached to aJMenuBaror anotherJMenu. 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.
Figure 14-11. Top-level menu and submenu
![]()
JMenuis a curious class. It contains aMenuUIdelegate, but it uses aButtonModelfor 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.JMenuactually extends theJMenuItemclass, 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 themnemonicproperty of theJMenuItemsuperclass to define a shortcut for the menu's title and, consequently, the menu. In addition, you can use theenabledproperty ofJMenuItemto disable the menu if desired.As with pop-up menus, you can add or insert
JMenuItem,Component, orActionobjects in the pop-up portion of the menu by calling theadd( )andinsert( )methods. You can also add a simple string to the menu;JMenucreates the correspondingJMenuItemobject for you internally. TheJMenuclass 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 theaddSeparator( )method.WARNING: You cannot use keyboard accelerators with
JMenuobjects (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 thesetMnemonic( )method to set a shortcut to bring up the menu, but the only universal, reliable approach is to assign keyboard accelerators to the non-submenuJMenuItems that trigger program actions.You can programmatically cause the submenu to pop up on the screen by setting the
popupMenuVisibleproperty totrue. Be aware that the pop up does not appear if the menu's title button is not showing.Figure 14-12 shows the class diagram for the
JMenucomponent.
Figure 14-12. JMenu class diagram
![]()
Properties
The
JMenuproperties are listed in Table 14-8.JMenuuses aJPopupMenuto represent its list of menu items. If you wish to access that underlying menu, you can do so using thepopupMenuproperty. ThepopupMenuVisibleproperty tracks whether the menu's pop-up portion is currently visible. As noted, setting this totruewhen the title button is visible causes the pop up to appear.JMenualso contains aselectedproperty, which indicates if the user has selected the title button of the menu. Both properties should mirror each other.
Table 14-8: JMenu properties Property
Data type
get
is
set
Default value
accessibleContexto
AccessibleContext
·
JMenu.accessibleJMenu( )
component
Component
·
componentOrientation1.4, o
ComponentOrientation
·
·
From L&F
delay
int
·
·
0
itemCount
int
·
0
itemi
JMenuItem
·
null
layouto
LayoutManager
·
·
OverlayLayout( )
menuComponentCount
int
·
0
menuComponenti
Component
·
null
menuComponents
Component[ ]
·
menuListeners1.4
MenuListener[ ]
·
modelo
ButtonModel
·
·
DefaultButtonModel( )
popupMenu
JPopupMenu
·
popupMenuVisible
boolean
·
·
false
selected
boolean
·
·
false
subElements
MenuElement[ ]
·
tearOffu
boolean
·
Throws an
ErrortopLevelMenu
boolean
·
UIb
MenuUI
·
From L&F
UIClassID
String
·
"MenuUI"
1.4since 1.4, bbound, iindexed, ooverridden, uunimplemented See also properties from the
JMenuItemclass (Table 14-4).The
topLevelMenuproperty has the valuetrueif thisJMenuis directly attached to a menu bar and is not a submenu.itemis an indexed property that allows access to each of theJMenuItemobjects in the menu, whileitemCountmaintains a count of all of theJMenuItemobjects that are present. Thedelayproperty 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, orsetDelay( )throws anIllegalArgumentException.The
menuComponentproperty is a more generalized version of theitemproperty; it returns the component at the given index as aComponentrather than as aJMenuItem. In addition, themenuComponentCountproperty retains a count of the menu items, separators, and other components currently in the menu. ThemenuComponentsproperty lets you access each of the items in the menu, returned as an array ofComponentobjects.The
componentOrientationproperty is used to accommodate non-Western languages in which text does not flow left to right.JMenuoverrides this property in order to properly pass changes on to theJPopupMenudelegate it uses.WARNING: The
tearOffproperty is not yet implemented and is reserved for (increasingly dubious) future use in Swing. Trying to use it throws anError(rather than something more appropriate like anUnsupportedOp-erationException). Since anErroris supposed to indicate a catastrophic failure of the virtual machine, using this property will almost certainly crash your application.Constructor
- public JMenu( )
- public JMenu(Action a)
- public JMenu(String s)
- public JMenu(String s, boolean b)
- Initialize a default
JMenu. You have the option of specifying a string for theJMenuto display--as well as abooleanfor thetearOffproperty (which is ignored)--or binding it to anAction.
Menu Items
- public JMenuItem add(JMenuItem menuItem)
- public Component add(Component c)
- public void add(String s)
- public JMenuItem add(Action a)
- Add various elements to the menus. Objects from both
JMenuItemandJComponentcan be added, but the latter functions best if it implements theMenuElementinterface. If you specify aStringas the parameter, a menu item with the appropriate label is created. If you specify anAction, its text and icon properties are used to derive an appropriateJMenuItem, 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 resultingJMenuItemis 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.
- public void insert(String s, int index)
- public JMenuItem insert(JMenuItem mi, int index)
- public JMenuItem insert(Action a, int index)
- Insert a specific menu item at a particular index. The index must be positive, or the method throws an
IllegalArgumentException. You can pass in aJMenuItem, aString, or anActionto these methods. If you specify aStringas the parameter, a menu item with the appropriate label is created. If you specify anAction, its text and icon properties are used to derive an appropriateJMenuItem, and its text is placed to the right of the icon. As usual, the menu retains its association with the action. The resultingJMenuItemis 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
JMenuItempassed 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
cis present anywhere in the menu. This method searches all submenus as well.
- public String paramString( )
- Return a
Stringspecifying the current state of the menu properties (intended for debugging purposes).
Event
JMenuobjects fire aMenuEventwhen the user has selected or deselected the menu's title button. TheJMenuobject contains the standardaddChangeListener( )andremoveChangeListener( )methods for maintaining a list ofMenuEventsubscribers.
- public void addMenuListener(MenuListener listener)
- public void removeMenuListener(MenuListener listener)
- Add or remove a
MenuListenerfrom the list of listeners receiving this menu's events.
MenuElement Interface
- public void menuSelectionChanged(boolean isIncluded)
- public MenuElement[ ] getSubElements( )
- public Component getComponent( )
- public void processKeyEvent(KeyEvent event, MenuElement path[ ], MenuSelectionManager manager)
- Implement the
MenuElementinterface, which is covered later in this chapter.
Working with Menus
Here is a program that demonstrates the use of the
JMenuclass. In this program, we use Swing'sActionclass to process the menu events. (We'll also use actions for toolbars later in this chapter.)//MenuExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class MenuExample extends JPanel {public JTextPane pane;public JMenuBar menuBar;public MenuExample( ) {menuBar = new JMenuBar( );JMenu formatMenu = new JMenu("Justify");formatMenu.setMnemonic('J');MenuAction leftJustifyAction = new MenuAction("Left",new ImageIcon("left.gif"));MenuAction rightJustifyAction = new MenuAction("Right",new ImageIcon("right.gif"));MenuAction centerJustifyAction = new MenuAction("Center",new ImageIcon("center.gif"));MenuAction fullJustifyAction = new MenuAction("Full",new ImageIcon("full.gif"));JMenuItem item;item = formatMenu.add(leftJustifyAction);item.setMnemonic('L');item = formatMenu.add(rightJustifyAction);item.setMnemonic('R');item = formatMenu.add(centerJustifyAction);item.setMnemonic('C');item = formatMenu.add(fullJustifyAction);item.setMnemonic('F');menuBar.add(formatMenu);menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));}class MenuAction extends AbstractAction {public MenuAction(String text, Icon icon) {super(text,icon);}public void actionPerformed(ActionEvent e) {try { pane.getStyledDocument( ).insertString(0 ,"Action ["+e.getActionCommand( )+"] performed!\n", null);} catch (Exception ex) { ex.printStackTrace( ); }}}public static void main(String s[ ]) {MenuExample example = new MenuExample( );example.pane = new JTextPane( );example.pane.setPreferredSize(new Dimension(250, 250));example.pane.setBorder(new BevelBorder(BevelBorder.LOWERED));JFrame frame = new JFrame("Menu Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setJMenuBar(example.menuBar);frame.getContentPane( ).add(example.pane, BorderLayout.CENTER);frame.pack( );frame.setVisible(true);}}Our
Actions are all instances of the inner classMenuActions. As we add eachActionto the menu, it creates an appropriateJMenuItem(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
ActionEventto be processed by theactionPerformed( )method of ourMenuActionclass. As in the previous examples, this results in the name of the menu item being printed. For variety, we have added a simpleJTextPaneto display the results of our menu choice, instead of using the system output. See Chapters 19 and 22 and for more information onJTextPane.
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
TheMenuListenerinterface, which is the conduit for receivingMenuEvents, 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.
Selectable Menu Items
So far, we've covered traditional menu items that produce a simple, text-oriented label associated with an action. But that's not the only type of item to which users are accustomed. Swing provides for two selectable menu items: the checkbox menu item and the radio button menu item.
The JCheckBoxMenuItem Class
Checkbox menu items are represented by the
JCheckBoxMenuItemclass. As you might have guessed, this object behaves similarly to theJCheckBoxobject. By clicking on a checkbox menu item, you can toggle a UI-defined checkmark that generally appears to the left of the menu item's label. There is no mutual exclusion between adjoiningJCheckBoxMenuItemobjects-- the user can check any item without affecting the state of the others. Figure 14-14 shows the class diagram for theJCheckBoxMenuItemcomponent.
Figure 14-14. JCheckBoxMenuItem class diagram
![]()
Properties
Table 14-9 shows the properties of the
JCheckBoxMenuItemclass.JCheckBoxMenuIteminherits theJMenuItemmodel (ButtonModel) and its accessors. TheJCheckBoxMenuItemclass also contains two additional component properties. Thestateproperty has the valuetrueif the menu item is currently in the checked state, andfalseif it is not. TheselectedObjectsproperty contains anObjectarray of size one, consisting of the text of the menu item if it is currently in the checked state. If it is not,getSelectedObjects( )returnsnull. ThegetSelectedObjects( )method exists for compatibility with AWT'sItemSelectableinterface.
Table 14-9: JCheckBoxMenuItem properties Property
Data type
get
is
set
Default value
accessibleContexto
AccessibleContext
·
JCheckBoxMenuItem.AccessibleJCheckBoxMenuItem( )
selectedObjectso
Object[ ]
·
state
boolean
·
·
false
UIb
CheckBoxMenuItemUI
·
·
From L&F
UIClassIDo
String
·
"CheckBoxMenuItem"
bbound, ooverridden See also properties from the
JMenuItemclass (Table 14-4).Constructors
- public JCheckBoxMenuItem( )
- public JCheckBoxMenuItem(Action action)
- public JCheckBoxMenuItem(Icon icon)
- public JCheckBoxMenuItem(String text)
- public JCheckBoxMenuItem(String text, Icon icon)
- public JCheckBoxMenuItem(String text, boolean checked)
- public JCheckBoxMenuItem(String text, Icon icon, boolean checked)
- These constructors initialize the
JCheckBoxMenuItemwith a specified action (since Version 1.3), icon, or string. The additionalbooleanvalue initializes thestateproperty, specifying whether the menu item is initially checked.
Miscellaneous
- public void updateUI( )
- Force the current UI manager to reset and repaint the delegate for the component, thus updating the component's L&F.
Using Checkbox Menu Items
Here's a program using the
JCheckBoxMenuItemclass. It is similar to theJMenuexample, except that each menu item now has a checkmark next to it. We've done nothing to make the items mutually exclusive; that comes next. We have, however, reworked the code to use more-portable keyboard accelerators rather than mnemonics. Note that we used M (middle) as the accelerator for the Center option because C is generally reserved for Copy. Figure 14-15 shows the result.// CheckBoxMenuItemExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class CheckBoxMenuItemExample extends JPanel {public JTextPane pane;public JMenuBar menuBar;public JToolBar toolBar;public CheckBoxMenuItemExample( ) {menuBar = new JMenuBar( );JMenu justifyMenu = new JMenu("Justify");ActionListener actionPrinter = new ActionListener( ) {public void actionPerformed(ActionEvent e) {try { pane.getStyledDocument( ).insertString(0 ,"Action ["+e.getActionCommand( )+"] performed!\n", null);} catch (Exception ex) { ex.printStackTrace( ); }}};JCheckBoxMenuItem leftJustify = newJCheckBoxMenuItem("Left", new ImageIcon("left.gif"));leftJustify.setHorizontalTextPosition(JMenuItem.RIGHT);leftJustify.setAccelerator(KeyStroke.getKeyStroke('L',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));leftJustify.addActionListener(actionPrinter);JCheckBoxMenuItem rightJustify = newJCheckBoxMenuItem("Right", new ImageIcon("right.gif"));rightJustify.setHorizontalTextPosition(JMenuItem.RIGHT);rightJustify.setAccelerator(KeyStroke.getKeyStroke('R',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));rightJustify.addActionListener(actionPrinter);JCheckBoxMenuItem centerJustify = newJCheckBoxMenuItem("Center", new ImageIcon("center.gif"));centerJustify.setHorizontalTextPosition(JMenuItem.RIGHT);centerJustify.setAccelerator(KeyStroke.getKeyStroke('M',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));centerJustify.addActionListener(actionPrinter);JCheckBoxMenuItem fullJustify = newJCheckBoxMenuItem("Full", new ImageIcon("full.gif"));fullJustify.setHorizontalTextPosition(JMenuItem.RIGHT);fullJustify.setAccelerator(KeyStroke.getKeyStroke('F',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));fullJustify.addActionListener(actionPrinter);justifyMenu.add(leftJustify);justifyMenu.add(rightJustify);justifyMenu.add(centerJustify);justifyMenu.add(fullJustify);menuBar.add(justifyMenu);menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));}public static void main(String s[ ]) {CheckBoxMenuItemExample example = new CheckBoxMenuItemExample( );example.pane = new JTextPane( );example.pane.setPreferredSize(new Dimension(250, 250));example.pane.setBorder(new BevelBorder(BevelBorder.LOWERED));JFrame frame = new JFrame("Menu Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setJMenuBar(example.menuBar);frame.getContentPane( ).add(example.pane, BorderLayout.CENTER);frame.pack( );frame.setVisible(true);}}
Figure 14-15. A series of checkbox menu items
![]()
The JRadioButtonMenuItem Class
Swing implements radio button menu items with the
JRadioButtonMenuItemclass. As you might expect, it shares the characteristics of theJRadioButtonclass and is intended to represent a group of mutually exclusive choices. Some L&Fs indicate this exclusivity visually by showing circular "buttons" to the left of the selectable choices.TIP: Even though L&Fs visually distinguish between checkbox and radio button items, the distinction can be subtle and unfamiliar to users, so it's a good idea to use separators (see "The JSeparator Class" later in this chapter) as well as other visual cues that suggest the logical grouping of mutually exclusive items within the menu.
Although you might expect otherwise, radio button menu items don't enforce mutual exclusion by themselves. Instead, you need to use a
ButtonGroupobject to limit the user to a single selection. Figure 14-16 shows the class diagram for theJRadioButtonMenuItemcomponent.
Figure 14-16. Radio button menu item class diagram
![]()
Properties
Table 14-10 shows the properties of the
JRadioButtonMenuItemclass. UnlikeJCheckBoxMenuItem, there is nostateproperty that indicates the current selection state of the menu item. Instead, you typically use this class in conjunction with aButtonGroup, which contains agetSelected( )method for extracting the correct object.
Table 14-10: JRadioButtonMenuItem properties Property
Data type
get
is
set
Default value
accessibleContexto
AccessibleContext
·
JRadioButtonMenuItem.AccessibleJRadioButtonMenu-Item( )
UIb
RadioButtonMenuItemUI
·
·
From L&F
UIClassIDo
String
·
"RadioButtonMenuItem"
bbound, ooverridden See also properties from the
JMenuItemclass (Table 14-4).Constructor
- public JRadioButtonMenuItem( )
- public JRadioButtonMenuItem(Action action)
- public JRadioButtonMenuItem(Icon icon)
- public JRadioButtonMenuItem(String text)
- public JRadioButtonMenuItem(String text, Icon icon)
- Initialize the
JRadioButtonMenuItemwith the specified action (since Version 1.3), icon, or string.
Miscellaneous
- public void updateUI( )
- Force the current UI manager to reset and repaint the delegate for the component, thus updating the component's L&F.
Enforcing Mutual Exclusion
The following program shows how to implement the mutually exclusive nature of radio button menu items:
// RadioButtonMenuItemExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class RadioButtonMenuItemExample extends JPanel {public JTextPane pane;public JMenuBar menuBar;public JToolBar toolBar;public RadioButtonMenuItemExample( ) {menuBar = new JMenuBar( );JMenu justifyMenu = new JMenu("Justify");ActionListener actionPrinter = new ActionListener( ) {public void actionPerformed(ActionEvent e) {try { pane.getStyledDocument( ).insertString(0 ,"Action ["+e.getActionCommand( )+"] performed!\n", null);} catch (Exception ex) { ex.printStackTrace( ); }}};JRadioButtonMenuItem leftJustify = newJRadioButtonMenuItem("Left", new ImageIcon("left.gif"));leftJustify.setHorizontalTextPosition(JMenuItem.RIGHT);leftJustify.setAccelerator(KeyStroke.getKeyStroke('L',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));leftJustify.addActionListener(actionPrinter);JRadioButtonMenuItem rightJustify = newJRadioButtonMenuItem("Right", new ImageIcon("right.gif"));rightJustify.setHorizontalTextPosition(JMenuItem.RIGHT);rightJustify.setAccelerator(KeyStroke.getKeyStroke('R',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));rightJustify.addActionListener(actionPrinter);JRadioButtonMenuItem centerJustify = newJRadioButtonMenuItem("Center", new ImageIcon("center.gif"));centerJustify.setHorizontalTextPosition(JMenuItem.RIGHT);centerJustify.setAccelerator(KeyStroke.getKeyStroke('M',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));centerJustify.addActionListener(actionPrinter);JRadioButtonMenuItem fullJustify = newJRadioButtonMenuItem("Full", new ImageIcon("full.gif"));fullJustify.setHorizontalTextPosition(JMenuItem.RIGHT);fullJustify.setAccelerator(KeyStroke.getKeyStroke('F',Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));fullJustify.addActionListener(actionPrinter);ButtonGroup group = new ButtonGroup( );group.add(leftJustify);group.add(rightJustify);group.add(centerJustify);group.add(fullJustify);justifyMenu.add(leftJustify);justifyMenu.add(rightJustify);justifyMenu.add(centerJustify);justifyMenu.add(fullJustify);menuBar.add(justifyMenu);menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));}public static void main(String s[ ]) {RadioButtonMenuItemExample example = newRadioButtonMenuItemExample( );example.pane = new JTextPane( );example.pane.setPreferredSize(new Dimension(250, 250));example.pane.setBorder(new BevelBorder(BevelBorder.LOWERED));JFrame frame = new JFrame("Menu Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setJMenuBar(example.menuBar);frame.getContentPane( ).add(example.pane, BorderLayout.CENTER);frame.pack( );frame.setVisible(true);}}Figure 14-17 shows the result. We use a
ButtonGroupobject to make ourJRadioButtonMenuItems mutually exclusive. Selecting any of the menu items deselects the others. Since text justification is mutually exclusive, this example shows how you would implement a real justification menu.
Figure 14-17. An example of radio button menu items
![]()
The JSeparator Class
You may have noticed that both
JMenuandJPopupMenucontainaddSeparator( )methods to add separators to menus. In doing so, each class instantiates aJSeparatorobject and positions it in the menu. However,JSeparatorexists as a component unto itself outside of menus, and, because it extendsJComponent, it can be positioned inside a container like any other Swing component.JSeparatoris a simple component that provides separation between logical groups of menu items. In some L&Fs it shows up as a horizontal line drawn across its entire width; in others, it is invisible and just adds a little extra space between elements. It has no model, only a delegate.Properties
Table 14-11 shows the properties of
JSeparator.Constructor
- JSeparator( )
- JSeparator(int orientation)
- Create a separator. By default, this separator is horizontal; if you specify an orientation, it should be either
SwingConstants.HORIZONTALorSwingConstants.VERTICAL.
Miscellaneous
- public void updateUI( )
- Force the current UI manager to reset and repaint the delegate for the component, thus updating the component's L&F.
Using a Separator Outside of a Menu
We've already seen how a separator can be used in menus to highlight the grouping of menu items. However, separators are components in themselves and can be used for a variety of tasks. Here is a program that adds a separator between a series of buttons:
// SeparatorExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class SeparatorExample extends JPanel {public SeparatorExample( ) {super(true);setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));Box box1 = new Box(BoxLayout.X_AXIS);Box box2 = new Box(BoxLayout.X_AXIS);Box box3 = new Box(BoxLayout.X_AXIS);box1.add(new JButton("Press Me"));box1.add(new JButton("No Me!"));box1.add(new JButton("Ignore Them!"));box2.add(new JSeparator( ));box3.add(new JButton("I'm the Button!"));box3.add(new JButton("It's me!"));box3.add(new JButton("Go Away!"));add(box1);add(box2);add(box3);}public static void main(String s[ ]) {SeparatorExample example = new SeparatorExample( );JFrame frame = new JFrame("Separator Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setContentPane(example);frame.pack( );frame.setVisible(true);}}This code yields the interface shown in Figure 14-18. Note that on platforms where separators are invisible, they are difficult or impossible to notice when used in this unorthodox way. (Even in this example, in which the separator has a visual representation, it's pretty hard to see!)
Figure 14-18. A standalone separator between two groups of buttons
![]()
The MenuElement Interface
As we saw in the previous examples, one nice feature of Swing menus is that we are not constrained to using text for menu items. However, the possibilities don't have to stop with icons, either. In fact, with a little work you can create or extend any Java component to serve as a menu item. There is one catch: your new menu item must implement the
MenuElementinterface. Swing declares five methods in theMenuElementinterface; these methods are called by Swing's internalMenuSelectionManagerwhen various actions take place.Why is this necessary? Let's look at the traditional menu item, such as the Paste item in the Edit menu. When the user raises the Edit menu, and the mouse passes over the Paste menu item, the menu item typically highlights itself, usually by changing color. This tells the user that releasing (or clicking, depending on the L&F) the mouse button chooses the Paste option. When the mouse leaves the menu item, it returns to its normal color. However, what if we wanted to make the text bold instead of highlighting it? What if we wanted to substitute another icon image in the menu item when the mouse passed over it? By calling the methods of this interface, menus allow menu items to define their own unique behavior.
Methods
- public void processMouseEvent(MouseEvent event,MenuElement path[ ], MenuSelectionManager manager)
- This method handles events triggered by the mouse. In addition to the
MouseEvent, the current path of selected menu elements is provided, as well as a reference to the current menu selection manager. You can take whatever action you feel is necessary with this method.
- public void processKeyEvent(KeyEvent event, MenuElement path[ ], MenuSelectionManager manager)
- This method handles events triggered by keystrokes. In addition to the
KeyEvent, the current path of selected menu elements is provided as well as a reference to the current menu selection manager. You can take whatever action you feel is necessary in this method.
- public void menuSelectionChanged(boolean isIncluded)
- Called when the menu element is added or removed from the current target menu.
- public MenuElement[ ] getSubElements( )
- Return an array of subelements for the target
MenuElement. This is needed in the event that a particular menu element has a submenu.
- public Component getComponent( )
- Return a reference to the component responsible for painting the menu item.
Making Arbitrary Components into Menu Elements
It is relatively easy to convert any Swing component into a menu element and drop it in a menu. Here is a program that places a
JSliderinside a pop-up menu and uses it as a hidden control for an underlying component.// MenuElementExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;import javax.swing.event.*;public class MenuElementExample extends JPanel {public JPopupMenu popup;SliderMenuItem slider;int theValue = 0;public MenuElementExample( ) {popup = new JPopupMenu( );slider = new SliderMenuItem( );popup.add(slider);popup.add(new JSeparator( ));JMenuItem ticks = new JCheckBoxMenuItem("Slider Tick Marks");ticks.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent event) {slider.setPaintTicks(!slider.getPaintTicks( ));}});JMenuItem labels = new JCheckBoxMenuItem("Slider Labels");labels.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent event) {slider.setPaintLabels(!slider.getPaintLabels( ));}});popup.add(ticks);popup.add(labels);popup.addPopupMenuListener(new PopupPrintListener( ));addMouseListener(new MousePopupListener( ));}// Inner class to check whether mouse events are the pop-up triggerclass MousePopupListener extends MouseAdapter {public void mousePressed(MouseEvent e) { checkPopup(e); }public void mouseClicked(MouseEvent e) { checkPopup(e); }public void mouseReleased(MouseEvent e) { checkPopup(e); }private void checkPopup(MouseEvent e) {if (e.isPopupTrigger( )) {popup.show(MenuElementExample.this, e.getX( ), e.getY( ));}}}// Inner class to print information in response to pop-up eventsclass PopupPrintListener implements PopupMenuListener {public void popupMenuWillBecomeVisible(PopupMenuEvent e) { }public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {theValue = slider.getValue( );System.out.println("The value is now " + theValue);}public void popupMenuCanceled(PopupMenuEvent e) {System.out.println("Popup menu is hidden!");}}public static void main(String s[ ]) {JFrame frame = new JFrame("Menu Element Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setContentPane(new MenuElementExample( ));frame.setSize(300, 300);frame.setVisible(true);}// Inner class that defines our special slider menu itemclass SliderMenuItem extends JSlider implements MenuElement {public SliderMenuItem( ) {setBorder(new CompoundBorder(new TitledBorder("Control"),new EmptyBorder(10, 10, 10, 10)));setMajorTickSpacing(20);setMinorTickSpacing(10);}public void processMouseEvent(MouseEvent e, MenuElement path[ ],MenuSelectionManager manager) {}public void processKeyEvent(KeyEvent e, MenuElement path[ ],MenuSelectionManager manager) {}public void menuSelectionChanged(boolean isIncluded) {}public MenuElement[ ] getSubElements( ) {return new MenuElement[0];}public Component getComponent( ) {return this;}}}As with our previous pop-up example,
PopupMenuExample, we implementMouseListenerand check incoming mouse events to see whether to show the pop up. The inner classSliderMenuItemimplements theMenuElementinterface, and is the focus of our example. In this case, it's fairly easy. Our menu slider never has subelements, doesn't have a concept of a selection, and doesn't need to do anything special with mouse or key events.The interface resulting from our example is shown in Figure 14-19. We provide a
JSliderobject, a separator, and twoJCheckBoxMenuItemobjects, which control the state of the slider. The slider is also surrounded by a titled border. When the user adjusts the slider and dismisses the pop up, we print the current value of the slider to the standard output. With a little bit of imagination, you can do just about anything with a pop-up menu. Of course, if it's something unexpected, you should carefully consider whether it is likely to confuse or confound your users.
Figure 14-19. A JSlider masquerading as a pop-up menu element in two L&Fs
![]()
Toolbars
Toolbars are another approach to providing access to commonly used application features. They are more likely than menus to use graphical representations of commands. Because they remain on-screen at all times (unlike menus, which drop down only when activated) they can provide a useful "dashboard" for indicating the current state of the application. On the other hand, they take up more room than menu bars, so it's good to let the user decide whether they should be visible at all.
Toolbars have the ability to "tear" themselves from their location within a frame and embed their components in a moveable standalone window. This gives the user the freedom to drag the toolbar anywhere on the screen. In addition, toolbars can "dock" in locations where the layout manager can support them.
The JToolBar Class
Like the menu bar, the
JToolBarclass is a container for various components. You can add any component to the toolbar, including buttons, combo boxes, and even additional menus. Like menus, the toolbar is easiest to work with when paired withActionobjects.When a component is added to the toolbar, it is assigned an integer index that determines its display order from left to right. While there is no restriction on the type of component that can be added, the toolbar generally looks best if it uses components that are the same vertical height. Note that toolbars have a default border installed by the L&F. If you don't like the default, you can override the border with one of your own using the
setBorder( )method. Alternatively, you can deactivate the drawing of the border by setting theborderPaintedproperty tofalse.
JToolBarhas its own separator that inserts a blank space on the toolbar; you can use theaddSeparator( )method to access this separator. Separators are useful if you want to add space between groups of related toolbar components. The separator for toolbars is actually an inner class. Be sure not to confuse this separator with theJSeparatorclass.Figure 14-20 shows the class diagram for the
JToolBarcomponent.
Figure 14-20. JToolBar class diagram
![]()
Floating toolbars
Although toolbars can be easily positioned in Swing containers, they do not have to stay there. Instead, you can "float" the toolbar by holding the mouse button down while the cursor is over an empty section of the toolbar (that is, not over any of its components) and dragging. This places the toolbar in a moveable child window; you can position it anywhere in the viewing area. Toolbars can then reattach themselves to specific locations, or hotspots, within the frame. Letting go of the toolbar while dragging it over a hotspot anchors the toolbar back into the container. Figure 14-21 is an example of a floating toolbar.
Figure 14-21. A floating toolbar
![]()
It is best to place a toolbar in a container that supports the
BorderLayout. If you intend to make the toolbar floatable, place it along either the north, south, east, or west side of the container, and leave the remaining sides open. This allows the toolbar to define anchor spots when it is being dragged and ensures that the resulting layout is not ambiguous.If you want to disable floating, you can reset the
floatableproperty tofalse:JToolBar toolBar = new JToolBar( );toolBar.setFloatable(false);Properties
The properties of the
JToolBarclass are shown in Table 14-12. TheborderPaintedproperty defines whether the toolbar should paint its border. TheJToolBarconstructor resets the layout manager of the component to aBoxLayoutalong the x axis (this becomes a y-axisBoxLayoutif the orientation isVERTICAL), which it uses to place any child components. Themarginproperty defines the insets that appear between the toolbar's edges and its components. Thefloatableproperty defines whether the toolbar can be separated from the container and "floated" in a standalone window. You can use the indexedcomponentAtIndexproperty to access any of the components on the toolbar. Theorientationproperty determines whether the toolbar is horizontal or vertical. Its value must be eitherHORIZONTALorVERTICAL(constants defined inSwingConstants). Attempting to setorientationto some other value throws anIllegalArgumentException. Finally, starting with SDK 1.4,rollovercan be set to cause the toolbar's button borders to be drawn only when the mouse is hovering over them, a style of interface popularized by dynamic web sites. Not all L&Fs honor atruevalue forrollover.Event
JToolbargenerates aPropertyChangeEventwhen any of its bound properties are changed.Constructor
- public JToolBar( )
- public JToolBar(int orientation)
- public JToolBar(String name)
- public JToolBar(String name, int orientation)
- Create a
JToolBar, optionally supplying a name that appears as a title when the toolbar is floating (the ability to assign a name started with SDK 1.3). The orientation is horizontal by default; if you specify an orientation, it must beSwingConstants.HORIZONTALorSwingConstants.VERTICAL.
Adding actions
- public JButton add(Action a)
- Add an
Actionto the toolbar. The method creates a simpleJButtonwith the text of the action placed below its image.[1] It then returns theJButton, allowing you to reset any of the button's attributes. (As you'd expect, the method for adding a component to a toolbar is inherited fromContainer.)
Miscellaneous
- public void updateUI( )
- Force the current
UIManagerto repaint the UI delegate for the component, updating the component's L&F.
- public int getComponentIndex(Component c)
- Return the integer index of the component
c, or-1if it's not found. Note that any separators in the toolbar take up index positions.
- public void addSeparator( )
- public void addSeparator(Dimension size)
- Add a separator to the toolbar. Be sure not to confuse the toolbar separator with
JSeparator, which is a separate Swing component. The toolbar separator created by this method is simply a blank area of space used to provide spacing between groups of toolbar components. The size is normally up to the toolbar, though you can specify the separator's size explicitly if you wish.
Creating a Toolbar
The following example adds a toolbar to the
JMenuexample and provides a glimpse of the true power of usingActionobjects for building a user interface.To add some interesting complexity, we also allow the user to choose a font from a combo box, showing that you can use other kinds of components in a toolbar. Note that we add the combo box and a
JLabelfor it as separate components and that the combo box uses its ownactionPerformed( )method.// ToolBarExample.java//import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;import javax.swing.event.*;public class ToolBarExample extends JPanel {public JTextPane pane;public JMenuBar menuBar;public JToolBar toolBar;String fonts[ ] = {"Serif","SansSerif","Monospaced","Dialog","DialogInput"};public ToolBarExample( ) {menuBar = new JMenuBar( );// Create a set of actions to use in both the menu and toolbar.DemoAction leftJustifyAction = new DemoAction("Left",new ImageIcon("left.gif"), "Left justify text", 'L');DemoAction rightJustifyAction = new DemoAction("Right",new ImageIcon("right.gif"), "Right justify text", 'R');DemoAction centerJustifyAction = new DemoAction("Center",new ImageIcon("center.gif"), "Center justify text", 'M');DemoAction fullJustifyAction = new DemoAction("Full",new ImageIcon("full.gif"), "Full justify text", 'F');JMenu formatMenu = new JMenu("Justify");formatMenu.add(leftJustifyAction);formatMenu.add(rightJustifyAction);formatMenu.add(centerJustifyAction);formatMenu.add(fullJustifyAction);menuBar.add(formatMenu);toolBar = new JToolBar("Formatting");toolBar.add(leftJustifyAction);toolBar.add(rightJustifyAction);toolBar.add(centerJustifyAction);toolBar.add(fullJustifyAction);toolBar.addSeparator( );JLabel label = new JLabel("Font");toolBar.add(label);toolBar.addSeparator( );JComboBox combo = new JComboBox(fonts);combo.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent e) {try { pane.getStyledDocument( ).insertString(0,"Font [" + ((JComboBox)e.getSource( )).getSelectedItem( ) +"] chosen!\n", null);} catch (Exception ex) { ex.printStackTrace( ); }}});toolBar.add(combo);// Disable one of the Actions.fullJustifyAction.setEnabled(false);}public static void main(String s[ ]) {ToolBarExample example = new ToolBarExample( );example.pane = new JTextPane( );example.pane.setPreferredSize(new Dimension(250, 250));example.pane.setBorder(new BevelBorder(BevelBorder.LOWERED));example.toolBar.setMaximumSize(example.toolBar.getSize( ));JFrame frame = new JFrame("Menu Example");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setJMenuBar(example.menuBar);frame.getContentPane( ).add(example.toolBar, BorderLayout.NORTH);frame.getContentPane( ).add(example.pane, BorderLayout.CENTER);frame.pack( );frame.setVisible(true);}class DemoAction extends AbstractAction {public DemoAction(String text, Icon icon, String description,char accelerator) {super(text, icon);putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(accelerator,Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( )));putValue(SHORT_DESCRIPTION, description);}public void actionPerformed(ActionEvent e) {try { pane.getStyledDocument( ).insertString(0,"Action [" + getValue(NAME) + "] performed!\n", null);} catch (Exception ex) { ex.printStackTrace( ); }}}}Note the efficiency we've achieved: by creating a single set of
Actions to represent our justification modes, we can create a corresponding menu entry or toolbar button in a single line of code. Each has the appropriate label and/or icon, accelerator key (for menus), and tooltip. Try holding your mouse over the buttons to see the tooltip. Also notice that by disabling the Full justify action we automatically disabled both the corresponding menu item and toolbar button. When an action is disabled, all the associated components are notified of the property change. In our program, both the menu item and the toolbar button for full justification are grayed, as shown in Figure 14-22.
Figure 14-22. Disabling actions automatically grays the toolbar and menu representations
![]()
To see the toolbar float, click on the textured area at the left edge and drag it outside the window. You can also drag it to any edge of the frame to anchor it at that position.
A
JToolBaris a regular Swing component, so you can use more than one in an application. If you do so, and you wish to make the toolbars floatable, it is best to place each toolbar in a concentricBorderLayoutcontainer, leaving the other three sides unpopulated. This ensures that the toolbars maintain their respective positions if they are both dragged to a new side.
1. In SDK 1.3, Sun discouraged the use of this method without actually deprecating it. People have pointed out that there is no adequate replacement, and this has been acknowledged. So use it, but keep your eye out for news.
Back to: Java Swing, 2nd Edition
© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com