Main Page

Previous Next

Pop-up Menus

The javax.swing package defines a class, JPopupMenu, that represents a menu that you can pop up at any position within a component, but conventionally you display it at the current mouse cursor position when a particular mouse button is pressed. There are two constructors in the PopupMenu class: one to which you pass a String object that defines a name for the menu, and a default constructor that defines a menu without a name. If you specify a name for a pop-up menu with a statement such as,

generalPopup = new PopupMenu("General");

the name is primarily for identification purposes and is not always displayed when the menu is popped up: it depends on your environment. Under Windows for instance it doesn't appear. This is different from a menu on a menu bar where the string you pass to the constructor is what appears on the menu bar.

Let's add a pop-up menu to the SketchFrame class by adding a data member of type JPopupMenu:

private JPopupMenu popup = new JPopupMenu("General");       // Window pop-up

To populate a pop-up menu with menu items, you add JMenuItem objects or Action objects by passing each of them to the add() method for the JPopupMenu object. You can also pass a String object to add(), which will create a JMenuItem object and add it to the pop-up. A reference to the menu item object is always returned by the various overloaded add() methods. Handling the events for the menu items is an identical process to that for regular menu items, and Action objects handle their own events as we have seen.

We will now add menu items to the pop-up we created above by adding the following code to the class constructor:

// Create pop-up menu


This adds the element menu items to the pop-up. We could also add the font choice menu item but you can't add a JMenuItem object to two different menus. You could either create an Action object that would pop up the font dialog, or you could add a different menu item to the pop-up that did the same thing when it was clicked.

Displaying a Pop-up Menu

You can display a pop-up within the coordinate system of any component, by calling the show()method for the JPopupMenu object. The method requires three arguments to be specified, a reference to the component that is the context for the pop-up, and the x and y coordinates where the menu is to be displayed, relative to the origin of the parent. For example:, xCoord, yCoord);

This displays the pop-up at position (xCoord, yCoord) in the coordinate system for the component, view.

A pop-up menu is usually implemented as a context menu. The principal idea of a context menu is that it's not just a single menu: it displays a different set of menu items depending on the context – that is, what is under the mouse cursor when the button is pressed. The mouse button that you press to display a context menu is sometimes called a pop-up trigger, simply because pressing it triggers the display of the pop-up. On systems that support the notion of a pop-up trigger, the pop-up trigger is fixed, but it can be different between systems. It is usually the right mouse button on a two- or three-button mouse, and on systems with a one-button mouse, you typically have to hold down a modifier key while pressing the mouse button.

The MouseEvent class has a special method, isPopupTrigger(), that returns true when the event should display a pop-up menu. This method will only return true in the mousePressed() or mouseReleased() methods. It will always return false in methods corresponding to other mouse events. This method helps to get over the problem of different mouse buttons being used on different systems to display a popup. If you use this method to decide when to display a popup, you've got them covered – well, almost. You would typically use this with the following code to display a pop-up.

public void mouseReleased(MouseEvent e) {
    // Code to display the pop-up menu...

We have shown conceptual code for the mouseReleased() method here. This would be fine for Windows but unfortunately it may not work on some other systems – Solaris for instance. This is because in some operating system environments the isPopupTrigger() only returns true when the button is pressed, not when it is released. The pop up trigger is not just a particular button – it is a mouse-pressed or mouse-released event with a particular button. This implies that if you want your code to work on a variety of systems using the 'standard' mouse button to trigger the pop-up in every case, you must implement the code to call isPopupTrigger() and pop the menu in both the mousePressed() and mouseReleased() methods. The method will only return true in one or the other. Of course, you could always circumvent this by ignoring convention and pop the menu for a specific button press with code like this:

if((e.getButton() == e.BUTTON3)
  // Code to display the pop-up menu...

Now the pop-up would only operate with button 3, regardless of the convention for the underlying operating system, but the user may not be particularly happy about having to use a different popup trigger for your Java program compared to other applications on the same system.

We will try a pop-up menu in Sketcher assuming Windows is the applicable environment. You should be able to change it to suit your environment if it is different, or even add the same code for both MOUSE_PRESSED and MOUSE_RELEASED events if you wish.

Try It Out – Displaying a Pop-up Menu

In Sketcher, the pop-up menu would sensibly operate in the area where the sketch is displayed – in other words triggering the pop-up menu has to happen in the view. Assuming you have already added the code that we discussed in the previous section to SketchFrame, we just need to add a method to SketchFrame to make the pop-up available to the view:

// Retrieve the pop-up menu
public JPopupMenu getPopup() {  
  return popup;  

Now a SketchView object can get a reference to the pop-up in the SketchFrame object by using the application object to get to this method.

We will implement the pop-up triggering in the mouseReleased() method consistent with Windows, but remember, all you need to do to make your code general is to put it in the mousePressed() method too. Here's how mouseReleased() should be in the MouseHandler inner class to SketchView:

    public void mouseReleased(MouseEvent e) {
      if(e.isPopupTrigger()) {
        start = e.getPoint();
                                                      start.x, start.y); 
        start = null;
      } else if((e.getButton()==MouseEvent.BUTTON1) && 
                (theApp.getWindow().getElementType() != TEXT)) {
        button1Down = false;                    // Reset the button 1 flag
        if(tempElement != null) {
          theApp.getModel().add(tempElement);   // Add element to the model
          tempElement = null;                   // No temporary now stored
        if(g2D != null)  {                      // If there's a graphics context
          g2D.dispose();                        // ...release the resource
          g2D = null;                           // Set field to null
        start = last = null;                    // Remove the points

We get hold of a reference to the pop-up menu object by calling getPopup() for the object reference returned by the application object's getWindow() method. The component where the pop-up is to appear is identified in the first argument to the show() method for the pop-up by calling getSource() for the MouseEvent object, e. This will return a reference to the view, as type Object, so we need to cast this to the Component type since that is what the show() method for the pop-up expects. The position, which we store temporarily in start, is just the current cursor position when the mouse button is released. We could use the position stored in start by the mousePressed() method, but if the user drags the cursor before releasing the button, the menu will appear at a different position from where the button is released.

If you recompile Sketcher and run it again, you should get the pop-up menu appearing in response to a right button click, or whatever button triggers a context menu on your system.

Note how we get the Icons and the label for each of the menu items. This is because we have defined both in the Action objects that we used to generate the menu.

How It Works

The isPopupTrigger() method for the MouseEvent object returns true when the button corresponding to a context menu is pressed or released. In this case we call the show() method for the pop-up menu object that we created in the SketchFrame object. When you click on a menu item in the pop-up, or click elsewhere, the pop-up menu is automatically hidden. Now any element type or color is a couple of clicks away.

This is just a pop-up menu, not a context menu. A context menu should be different depending on what's under the cursor. We will now look more closely at how we could implement a proper context menu capability in Sketcher.

Implementing a Context Menu

As a context menu displays a different menu depending on the context, it follows that the program needs to know what is under the cursor at the time the right mouse button is pressed. Let's take the specific instance of the view in Sketcher where we are listening for mouse events. We could define two contexts for the cursor in the view – one when an already-drawn element is under the cursor, and another when there is no element under the cursor. In the first context, we could display a special pop-up menu that is particularly applicable to an element – with menu items to delete the element, or move it, for example. In the second context we could display the pop-up menu that we created in the previous example. Our context menu when a drawn element is under the cursor is going to look like that shown below.

For the context menu to be really useful, the user will need to know which element is under the cursor before they pop up the context menu, otherwise they can't be sure to which element the pop-up menu operations will apply. Deleting the wrong element could be irritating.

What we need is some visual feedback to show when an element is under the cursor – highlighting the element under the cursor by changing its color, for instance.

Try It Out – Highlighting an Element

To highlight an element, we will draw it in magenta rather than its normal color. Every element will need a boolean variable to indicate whether it is highlighted or not. This variable will be used to determine which color should be used in the draw() method. We can add this variable as a data member of the Element class:

protected boolean highlighted = false;         // Highlight flag

You can add this line immediately following the statement for the other data members in the Element class definition. The variable highlighted will be inherited by all of the sub classes of Element.

We can also add the method to set the highlighted flag to the Element class:

// Set or reset highlight color
public void setHighlighted(boolean highlighted) {
  this.highlighted = highlighted;

This method will also be inherited by all of the sub classes of Element.

To implement the basis for getting highlighting to work, you need to change one line in the draw() method for each of the sub classes of Element – that is, Element.Line, Element.Circle, Element.Curve, Element.Rectangle, and Element.Text. The line to change is the one that sets the drawing color – it's the first line in each of the draw() methods. You should change it to:

g2D.setPaint(highlighted ? Color.magenta : color);

Now each element can potentially be highlighted.

How It Works

The setHighlighted()method accepts a boolean value as an argument and stores it in the data member, highlighted. When you want an element to be highlighted, you just call this method with the argument as true. To switch highlighting off for an element, you call this method with the argument false.

Previously, the setPaint()statement just set the color stored in the data member, color, as the drawing color. Now, if highlighted is true, the color will be set to magenta, and if highlighted is false, the color stored in the data member, color, will be used.

To make use of highlighting to provide the visual feedback necessary for a user-friendly implementation of the context menu, we need to determine at all times what is under the cursor. This means we must track and analyze all mouse moves all the time!

Tracking Mouse Moves

Whenever the mouse is moved, the mouseMoved() method in the MouseMotionListener interface is called. We can therefore track mouse moves by implementing this method in the MouseHandler class that is an inner class to the SketchView class. Before we get into that, we need to decide what we mean by an element being under the cursor, and more crucially, how we are going to find out to which element, if any, this applies.

Click To expand

It's not going to be too difficult. We can arbitrarily decide that an element is under the cursor when the cursor position is inside the bounding rectangle for an element. This is not too precise a method, but it has the great attraction that it is extremely simple. Precise hit-testing on an element would carry considerably more processing overhead. Electing to add any greater complexity will not help us to understand the principles here, so we will stick with the simple approach.

So what is going to be the methodology for finding the element under the cursor? Brute force basically: whenever the mouse is moved, we can just search through the bounding rectangles for each of the elements in the document until we find one that encloses the current cursor position. We will then arrange for the first element that we find to be highlighted. If we get right through all the elements in the document without finding a bounding rectangle that encloses the cursor, then there isn't an element under the cursor.

To record a reference to the element that is under the cursor, we will add a data member of type Element to the SketchView class. If there isn't an element under the cursor, we will make sure that this data member is null.

Try It Out – Referring to Elements

Add the following statement after the statement declaring the theApp data member in the SketchView class definition:

private Element highlightElement;            // Highlighted element

The mouseMoved() method is going to be called very frequently, so we need to make sure it executes as quickly as possible. This means that for any given set of conditions, we execute the minimum amount of code. Here's the implementation of the mouseMoved() method in the MouseHandler class in SketchView:

// Handle mouse moves
public void mouseMoved(MouseEvent e) {
  Point currentCursor = e.getPoint();  // Get current cursor position
  Iterator elements = theApp.getModel().getIterator();
  Element element = null;                             // Stores an element

  while(elements.hasNext())  {                        // Go through the list
    element = (Element);               // Get the next element
    if(element.getBounds().contains(currentCursor)) { // Under the cursor?
      if(element==highlightElement)            // If it's already highlighted
        return;                                // we are done
      g2D = (Graphics2D)getGraphics();         // Get graphics context
      if(highlightElement!=null)   {           // If an element is highlighted
        highlightElement.setHighlighted(false);// un-highlight it and
        highlightElement.draw(g2D);            // draw it normal color
      element.setHighlighted(true);           // Set highlight for new element
      highlightElement = element;             // Store new highlighted element
      element.draw(g2D);                      // Draw it highlighted 
      g2D.dispose();                      // Release graphic context resources
      g2D = null;

  // Here there is no element under the cursor so...
  if(highlightElement!=null)   {            // If an element is highlighted
    g2D = (Graphics2D)getGraphics();        // Get graphics context
    highlightElement.setHighlighted(false); // ...turn off highlighting
    highlightElement.draw(g2D);             // Redraw the element
    highlightElement = null;                // No element highlighted
    g2D.dispose();                        // Release graphic context resources
    g2D = null;

To check that highlighting works, recompile Sketcher and run it again. If you draw a few elements, you should see them change color as the cursor moves over them.

How It Works

This method is a fair amount of code so let's work through it step by step. The first statement saves the current cursor position in the local variable, currentCursor. The next two statements obtain a Graphics2D object and declare a variable, element, which we will use to store each element that we retrieve from the model. The variable, g2D will be passed to the draw()method for any element that we need to redraw highlighted or un-highlighted as the case may be.

We use an iterator we get from the model to go through the elements – you have seen how this works previously. In the loop, we obtain the bounding rectangle for each element by calling its getBounds() method, and then call the contains() method for the rectangle that is returned. This will return true if the rectangle encloses the point, currentCursor, that is passed as an argument. When we find an element under the cursor, it is quite possible that the element is already highlighted because the element was found last time the mouseMoved() method was called. This will occur when you move the cursor within the rectangle bounding an element. In this case we don't need to do anything, so we return from the method.

If the element found is not the same as last time, we obtain a graphics context for the view since we definitely need it to draw the new element we have found under the cursor in the highlight color. We then check that the variable highlightElement is not null – it will be null if the cursor just entered the rectangle for an element and previously none was highlighted. If highlightElement is not null we must un-highlight the old element before we highlight the new one. To do this we call its setHighlighted() method with the argument false, and call its draw()method. We don't need to involve the paint() method for the view here since we are not adding or removing elements – we are simply redrawing an element that is already displayed. To highlight the new element, we call its setHighlighted() method with the argument true, store a reference to the element in highlightElement and call its draw() method to get it drawn in the highlight color. We then release the graphics context resources by calling the dispose() method for g2D, set the variable back to null and return.

The next block of code in the method is executed if we exit the while loop because no element is under the cursor. In this case we must check if there was an element highlighted last time around. If there was, we un-highlight it, redraw it in its normal color and reset highlightElement to null.

Defining the Other Context Menu

The context menu when the cursor is over an element should be implemented in the view. We already have the menu defined in SketchFrame for when the cursor is not over an element. All we need is the context menu for when it is – plus the code to decide which menu to display when isPopupTrigger() returns true for a mouse event.

You already know that we will have four menu items in the element context menu:

  • Move – to move the element under the cursor to a new position. This will work by dragging it with the left mouse button down (button 1).

  • Delete – this will delete the element under the cursor.

  • Rotate – this will allow you to rotate the element under the cursor about the top-left corner of its bounding rectangle by dragging it with the left mouse button down.

  • Send-to-back – this is to overcome the problem of an element not being accessible, never highlighted that is, because it is masked by the bounding rectangle of another element.

Since we highlight an element by searching the list from the beginning, an element towards the end may never be highlighted if the rectangle for an earlier element completely encloses it. Moving the earlier element that is hogging the highlighting to the end of the list will allow the formerly masked element to be highlighted.

Try It Out – Creating Context Menus

We will add the necessary data members to the SketchView class to store the element pop-up reference, and the JMenuItem objects that will be the pop-up menu items:

private JPopupMenu elementPopup = new JPopupMenu("Element");
private JMenuItem moveItem, deleteItem,rotateItem, sendToBackItem;

We must add import statements for JPopupMenu and JMenuItem:

import javax.swing.JPopupMenu;
import javax.swing.JMenuItem;

We will create the elementPopup context menu in the SketchView constructor:

public SketchView(Sketcher theApp) {
    this.theApp = theApp;
    MouseHandler handler = new MouseHandler();    // create the mouse listener
    addMouseListener(handler);                    // Listen for button events
    addMouseMotionListener(handler);              // Listen for motion events

    // Add the pop-up menu items
    moveItem = elementPopup.add("Move");
    deleteItem = elementPopup.add("Delete");
    rotateItem = elementPopup.add("Rotate");
    sendToBackItem = elementPopup.add("Send-to-back");
    // Add the menu item listeners

We add the menu items using the add() method that accepts a String argument, and returns a reference to the JMenuItem object that it creates. We then use these references to add the view object as the listener for all the menu items in the pop-up.

We must make sure the SketchView class declares that it implements the ActionListener interface:

class SketchView extends JComponent
                 implements Observer, Constants, ActionListener {

We can add the actionPerformed() method to SketchView that will handle action events from the menu items.


As with the new data members above, be careful to add this to the SketchView class and not inside the inner MouseHandler class by mistake!

  public void actionPerformed(ActionEvent e ) {
    Object source = e.getSource();
    if(source == moveItem) {
      // Process a move...

    } else if(source == deleteItem) {
      // Process a delete...

    } else if(source == rotateItem) {
      // Process a rotate

    } else if(source == sendToBackItem) {
      // Process a send-to-back...

Of course, we will need two more import statements in the file:

import java.awt.event.ActionEvent;    
import java.awt.event.ActionListener;    

To pop the menu we need to modify the code in the mouseReleased()method of the MouseHandler inner class a little:

    public void mouseReleased(MouseEvent e) {
      if(e.isPopupTrigger()) {
        start = e.getPoint();

        if(highlightElement == null)
JavaScript Editor
 Java Tutorials Free JavaScript Editor