I have a button on a toolbar that when pressed starts a timer. If the mouse button is released before the timer
expires, the action assigned to first item in a popup menu is invoked. If the timer expires, the popup menu is
displayed and a selection can be made by releasing the mouse over a menu item.
This worked as expected for years until one day I tried a very short JFrame with a long JPopupMenu. The
items in the part of the popup menu below the bottom edge of the frame cannot be selected. When the mouse
button is released over one of them, the popup disappears, but no action is invoked. I fussed with this for a
while and ended up treating the items below the frame as a special case and remembering when the mouse
enters and exists them. It seems that there should be a better solution and that I really don't understand what
is going wrong.
When the mouse button is released after the delay, but still pointing at the toolbar button, the popup appears
and behaves as expected. (A mouse button press and release select an item.) Also, when the popup is started
from the main menu it works as expected. This means that having menu items that don't overlap the frame is
not bad, but something in the way I start the popup is amiss.
( This is on 1.6.0_17, Linux 2.6.32.26-175.fc12.i686 )
import java.awt.Component;
import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.*;
public class TestPopup extends JFrame {
static TestPopup thistest;
static JPopupMenu curlink;
final static int link_popup_delay = 300;
static Action linkCurrentAction = new LinkCurrentAction();
public TestPopup() {
super("Test Popup Menu");
// Create a menu:
JMenuBar viewerMenuBar = new JMenuBar();
JMenuItem item;
JMenu m = new JMenu("Actions");
item = new JMenuItem(linkCurrentAction);
m.add(item);
viewerMenuBar.add(m);
setJMenuBar(viewerMenuBar);
// the static JPopMenu may be shared in multiple windows
curlink = new JPopupMenu();
linkCurrentAction.setEnabled(false);
// create a toolbar with a button:
JToolBar toolbar = new JToolBar();
JButton linkButton = toolbar.add(linkCurrentAction);
linkButton.setFocusable(false);
linkButton.setText("button");
linkButton.setBorderPainted(false);
linkButton.addMouseListener(new PopupMouseAdapter());
getContentPane().add(toolbar, BorderLayout.NORTH);
toolbar.validate();
setLocationByPlatform(true);
setSize(500, 150); // not very tall
// put some data in the popup for testing
setCurrentLinkPopupMenu();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class PopupMouseAdapter extends MouseAdapter {
long whenPressed = 0;
boolean activate = false;
public void mouseReleased(MouseEvent e) {
JButton but = (JButton)(e.getComponent());
int w = but.getWidth();
int h = but.getHeight();
int x = e.getX();
int y = e.getY();
if (x < 0 || y < 0 || x > w || y > h) {
// do nothing
System.out.println("button released outside of button");
activate = false;
curlink.setVisible(false);
return;
}
long whenReleased = e.getWhen();
long tdelta = whenReleased - whenPressed;
if (tdelta < link_popup_delay) {
// select the first link
activate = false;
curlink.setVisible(false);
System.out.println("move to first item");
return;
}
// wait for popup choice
}
public void mousePressed(MouseEvent e) {
final JButton but = (JButton)(e.getComponent());
activate = true;
whenPressed = e.getWhen();
Timer timer = new Timer(link_popup_delay, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (activate) {
popupLinkMenu(but, curlink);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
static void popupLinkMenu(Component c, JPopupMenu pm) {
if (c instanceof javax.swing.JMenuItem) {
pm.show(thistest,32,32);
} else {
int h = c.getHeight();
pm.show(c,0,h);
}
}
final static class LinkCurrentAction extends AbstractAction {
static final long serialVersionUID = 110;
LinkCurrentAction() {
putValue(NAME, "Currentlink");
putValue(SHORT_DESCRIPTION, "Links of selected event.");
putValue(ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_J, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
}
public void actionPerformed(ActionEvent e) {
Component c = (Component)e.getSource();
if (c instanceof javax.swing.JMenuItem) {
int count = curlink.getComponentCount();
if (count == 0)
return;
if (count == 1) {
System.out.println("move to index 1 (only)");
return;
}
popupLinkMenu(c,curlink);
}
System.out.println("curlink selected = "+curlink.getSelectionModel().getSelectedIndex());
}
}
static JMenuItem createCurrentLinkMenuItem(String s, int i) {
JMenuItem mi = new JMenuItem(s);
mi.setActionCommand(Integer.toString(i));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int index = Integer.parseInt(e.getActionCommand());
System.out.println("move to index "+index);
}
});
return mi;
}
// Some test data
final static String[] menuValues = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven" };
static void setCurrentLinkPopupMenu() {
linkCurrentAction.setEnabled(true);
int i = 1;
for (String s : menuValues) {
JMenuItem mi = createCurrentLinkMenuItem(s, i++);
curlink.add(mi);
}
}
public static void main(final String[] args) {
thistest = new TestPopup();
thistest.setVisible(true);
}
}