views:

1232

answers:

4

In the past, when one made a JPopupMenu visible it's first item would get selected by default: http://weblogs.java.net/blog/alexfromsun/archive/2008/02/jtrayicon_updat.html

Nowadays the default behavior is to pop up the menu without any item selected. I would like create a JPopupMenu with a single item that will pop up selected and centered under the mouse pointer. I have managed to get the item to pop up centered under the mouse but I the JMenuItem refuses to render as if it is selected. If I move the mouse out of the item and back in it selects properly.

Any ideas?

Here is my testcase:

import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

public class Test extends JFrame
{
    public static void main(String[] args)
    {
     JFrame frame = new JFrame();
     frame.setSize(800, 600);
     frame.getContentPane().addMouseListener(new MouseAdapter()
     {
      @Override
      public void mousePressed(MouseEvent e)
      {
       if (e.isPopupTrigger())
        popupTriggered(e);
      }

      @Override
      public void mouseReleased(MouseEvent e)
      {
       if (e.isPopupTrigger())
        popupTriggered(e);
      }

      private void popupTriggered(MouseEvent e)
      {
       JPopupMenu menu = new JPopupMenu();
       final JMenuItem item = new JMenuItem("This is a JMenuItem");
       menu.add(item);
       Point point = e.getPoint();
       int x = point.x - (item.getPreferredSize().width / 2);
       int y = point.y - (item.getPreferredSize().height / 2);
       menu.show((Component) e.getSource(), x, y);
      }
     });
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
     frame.setVisible(true);
    }
}
+1  A: 

Nowadays the default behavior is to pop up the menu without any item selected.

Actually, I would argue that this is the correct behavior, at least in Windows. Other non-Java applications do this too. I don't think it's worth breaking this convention even if there is only one item in the menu. If you feel otherwise, you can set the selection index as in sean.bright's answer.


So, I finally got the chance to try it out on Java 1.6.0_11, and found some inconsistent behavior: If the popup menu juts out of the parent frame, the item is automatically selected; if the popup menu appears entirely within the parent frame, nothing is selected. Sounds like a Swing bug, which at least warrants an RFE for consistent behavior.

Zach Scrivena
+1: The right behavior is whatever the particular platform does for other (non-Java) programs.
Software Monkey
Zach, I agree with your general assessment but unfortunately this doesn't answer my question. I *want* to break away from the conventional behavior but I'm finding it impossible to do so. I'm trying to find out if this is a Swing bug or user error.
Gili
I filed a bug report with Sun: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6799989
Gili
A: 

This is weird.

I tried it with Windows, and with Java 1.5.0_08 and even 1.6.0_07 the first Element is selected automatically, as you expected it to be.

So I tried it with 1.6.0_11, and it does not work any more, the first element is not selected initially. Selecting the element in the selectionModel does not seem to help.

One workaround (that I'm not at all proud of) is to move the mouse automatically after displaying the popup menu, using the coordinates of the MouseEvent. Maybe someone's got a better idea?

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

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

public class SelectedPopupMenu extends JFrame {

    public SelectedPopupMenu() {
     addMouseListener(new MouseAdapter() {
         public void mouseClicked(final MouseEvent e) {
          JPopupMenu popupMenu = new JPopupMenu();
          popupMenu.add(new JMenuItem("Test-Item"));
          popupMenu.add(new JMenuItem("Test-Item-2"));
          // do not care to really hit the center of the popup
          popupMenu.show(SelectedPopupMenu.this, e.getX() - 30, e.getY() - 10);
          try {
                    // shake mouse, so that first element is selected even in Java 1.6.0_11
                    Robot robot = new Robot();
                    robot.mouseMove(e.getX() + 1, e.getY());
                    robot.mouseMove(e.getX(), e.getY());
             } catch (AWTException ex) {
              ex.printStackTrace();
          }
         }
     });
    }

    public static void main(String[] args) {
     JFrame frame = new SelectedPopupMenu();
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setSize(800, 600);
     frame.setVisible(true);
    }
}
Peter Lang
A: 

Secret turns out to be MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, ...});

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;

/**
 * Demonstrates programmatic {@link JMenuItem} selection;
 * specifically how to make the first item selected by default
 */
public class TestPopup extends JFrame {
  public static void main(String[] args) {
    final JFrame frame = new JFrame("TestPopup");
    frame.setSize(640, 480);
    frame.getContentPane().addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent e) {
        if (e.isPopupTrigger()) {
          popupTriggered(e);
        }
      }
      private void popupTriggered(MouseEvent e) {
        final JPopupMenu menu = new JPopupMenu();
        final JMenuItem item0 = new JMenuItem("JMenuItem 0");
        final JMenuItem item1 = new JMenuItem("JMenuItem 1");
        menu.add(item0);
        menu.add(item1);
        menu.pack();
        // use invokeLater or just do this after the menu has been shown
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0});
          }
        });
        int x = (int) ((int) (frame.getSize().width - (menu.getPreferredSize().width / 2.)) / 2.);
        int y = (int) ((int) (frame.getSize().height - (menu.getPreferredSize().height / 2.)) / 2.);
        menu.show(frame, x, y);
        // doesn't work:
        //item0.setSelected(true);
        // doesn't work:
        //menu.getSelectionModel().setSelectedIndex(0);
        // bingo; see also MenuKeyListener / MenuKeyEvent
//        MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0});
      }
    });
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}
Luke Nezda
A: 

Agree with Luke Nezda.

MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{...});

does the thing

Anton