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


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

Java Swing: Menus and Toolbars, Part 4

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

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

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, or Action objects to the pop-up menu with the add( ) and insert( ) methods. The JPopupMenu class 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 the addSeparator( ) method; these separators also count as an index. Figure 14-10 shows the class diagram for the JPopupMenu component. Starting with SDK 1.4, pop-up menus use the Popup class to actually draw themselves. This class is also used for other briefly displayed interface elements like tooltips.

Figure 14-10. JPopupMenu class diagram

Related Reading

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

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. The show( ) method sets the location and invoker properties 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 your MouseEvents to see if they're the pop-up trigger. A word to the wise: if a MouseEvent is 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 a processMouseEvent( ) 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( ) in java.awt.event.MouseEvent to check whether the mouse event is a trigger in a platform-independent way. Since SDK 1.3, JPopupMenu has 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 JPopupMenu class are shown in Table 14-7. Pop-up menus have many properties. The visible property tells whether the pop-up menu is currently showing on the screen; you can use the setVisible( ) method to show or hide the pop up, but if it is a free-floating pop up, it is much easier to use the show( ) method. The location property provides the coordinates on the screen where the pop-up menu is or has been raised. The read-only margin property 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

Accessible Context

·

 

 

JMenuItem.AccessibleJMenuItem( )

borderPaintedo

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 JMenuItem class (Table 14-4).

The invoker property is a reference to the component that is responsible for hosting the pop-up menu. The borderPainted property indicates whether the pop-up menu should paint its border. The label property 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 that label is a String and not a JLabel. componentAtIndex is an indexed property that returns the component at the specified index.

The lightWeightPopupEnabled property allows the programmer to enable or disable the potential use of lightweight components to represent the pop-up menu. If the property is set to true, 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 static setDefaultLightWeightPopupEnabled( ) method.

Events

JPopupMenu objects fire a PopupMenuEvent under two conditions: when the menu becomes visible or invisible, or is canceled without a menu item selection. The class contains the standard addPopupMenuListener( ) and removePopupMenuListener( ) methods for maintaining a list of PopupMenuEvent subscribers.

public void addPopupMenuListener(PopupMenuListener l)
public void removePopupMenuListener(PopupMenuListener l)

Add or remove a PopupMenuListener from 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 popupMenuWillBecomeInvisible handler.

Constructors

public JPopupMenu( )
public JPopupMenu(String title)

Create an empty pop-up menu. The second constructor accepts a String as 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 JMenuItem or JComponent can be added, but the latter functions best if it implements the MenuElement interface. If you specify an Action, its many properties are used to derive an appropriate JMenuItem, 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 resulting JMenuItem is 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 JComponent or an Action to these methods. If you use a JComponent, it's best if it implements the MenuElement interface. If you specify an Action, its various properties are used to derive an appropriate JMenuItem, and its text is placed to the right of any image icon. As usual, the item retains its association with the action. The resulting JMenuItem is 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( ), and setVisible( ).

public void setPopupSize(int width, int height)

An alternate way to establish a preferred size for the pop up. (The other way is the popupSize property, which takes a Dimension.)

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 lightWeightPopupEnabled property.

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 lightWeightPopupEnabled property, 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 MenuElement interface, which is covered later in this chapter.

Using Pop-up Menus

Here is a program that demonstrates the use of the JPopupMenu class. 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 trigger
    class 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 occur
    class 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 overriding processMouseEvent( ) 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 PopupMenuListener will 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 PopupMenuListener interface, which is the conduit for receiving the PopupMenuEvent objects, 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 opport

unity to update the contents of the menu (or their enabled states) based on current application conditions.

In the next installment, learn about the JMenu Class.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.