Java swing popup menu and jlist
Solution 1
Don't do setComponentPopupMenu
. In the MouseAdapter
do the following:
public void mousePressed(MouseEvent e) {check(e);}
public void mouseReleased(MouseEvent e) {check(e);}
public void check(MouseEvent e) {
if (e.isPopupTrigger()) { //if the event shows the menu
jList.setSelectedIndex(jList.locationToIndex(e.getPoint())); //select the item
jPopupMenu.show(jList, e.getX(), e.getY()); //and show the menu
}
}
This should work.
EDIT: The code now checks both press
and release
events, because some platforms show popups when mouse presses and some other on release. See the Swing tutorial for more info.
Solution 2
If you want to continue to use setComponentPopupMenu
(which is nice because it handles mouse and keyboard invocations of the popup in a cross platform way), you could override JPopupMenu.show(Component, int, int)
to select the appropriate row.
JPopupMenu jPopupMenu = new JPopupMenu() {
@Override
public void show(Component invoker, int x, int y) {
int row = jList.locationToIndex(new Point(x, y));
if (row != -1) {
jList.setSelectedIndex(row);
}
super.show(invoker, x, y);
}
};
jList.setComponentPopupMenu(jPopupMenu);
Note that when your popup is invoked via the keyboard (and you don't also override getPopupLocation
on your target component), the x, y location you get in JPopupMenu.show
will be the midpoint of your component. If there's already a selection in this case you probably don't want to change the selection.
The solution I came up with to solve the keyboard vs. mouse invocation problem was to set a client property on the component in an override of getPopupLocation
and then check it when showing the popup. The argument to getPopupLocation
will be null
when invoked via the keyboard. Here's the core code (perhaps implemented in a utility class available to your component and its popup menu).
private static final String POPUP_TRIGGERED_BY_MOUSE_EVENT = "popupTriggeredByMouseEvent"; // NOI18N
public static Point getPopupLocation(JComponent invoker, MouseEvent event)
{
boolean popupTriggeredByMouseEvent = event != null;
invoker.putClientProperty(POPUP_TRIGGERED_BY_MOUSE_EVENT, Boolean.valueOf(popupTriggeredByMouseEvent));
if (popupTriggeredByMouseEvent)
{
return event.getPoint();
}
return invoker.getMousePosition();
}
public static boolean isPopupTriggeredByMouseEvent(JComponent invoker)
{
return Boolean.TRUE.equals(invoker.getClientProperty(POPUP_TRIGGERED_BY_MOUSE_EVENT));
}
Then override getPopupLocation
in your component:
@Override
public Point getPopupLocation(MouseEvent event)
{
return PopupMenuUtils.getPopupLocation(this, event);
}
and call isPopupTriggeredByMouseEvent
in an override of JPopupMenu.show
to determine whether or not to select the row at the popup location (or whatever action may make sense for the underlying component):
JPopupMenu jPopupMenu = new JPopupMenu() {
@Override
public void show(Component invoker, int x, int y) {
int row = jList.locationToIndex(new Point(x, y));
if (row != -1 && PopupMenuUtils.isPopupTriggeredByMouseEvent((JComponent) invoker)) {
jList.setSelectedIndex(row);
}
super.show(invoker, x, y);
}
};
VaclavDedik
Updated on July 09, 2022Comments
-
VaclavDedik almost 2 years
here is my problem: I have a jList and a popup menu. When I right click the jList, the popup menu shows. The problem is that the jList item which the mouse is pointing at won't select. And I want it to do that. When I point my cursor at an item in the list and press the right button, I want two things to happen. Select the item on which I clicked and show the popup menu.
I tried this:
jLists.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { jList.setSelectedIndex(jList.locationToIndex(e.getPoint())); } }); jList.setComponentPopupMenu(jPopupMenu);
But it only shows the popup menu. If I delete this line:
jList.setComponentPopupMenu(jPopupMenu);
then the right-click select works (but the popup menu doesn't show).
So, what do you think is the best way to make these two functions (both) work ?
Thanks and sorry for my english.
-
Andrew Thompson almost 13 yearsNot a big fan of untested code, but was compelled to give an up vote for checking
isPopupTrigger()
- good call. -
Boro almost 13 yearsTo be honest I would go with
if(e.getButton() == MouseEvent.BUTTON3)
instead ofif(e.isPopupTrigger())
for a very simple reason the trigger method returns false for me always. Otherwise I would code it 100% the same @jmendeth +1. -
Andrew Thompson almost 13 years@Boro: Interesting point ( & yet another affirmation for 'tested code' ;). What OS/Java are you running?
-
Alba Mendez almost 13 yearsI've updated the code to check both
press
andrelease
events. -
Boro almost 13 years@Andrew Thompson Win7 Java 6u25 even after re-coding in a similar fashion (one method for all mouse event types) I always have false from
e.isPopupTrigger()
method. Can anyone confirm if on Linux and/or Macif(e.getButton() == MouseEvent.BUTTON3)
would work? -
Boro almost 13 yearsOk now it is fine I forgot to remove the ` table.setComponentPopupMenu(popup);` thus on release I didn't get the trigger. So on Wins you need the release too. Good to know :)
-
Rangi Keen over 10 yearsNote that avoiding
setComponentPopupMenu
in this way means that you won't be able to invoke the popup menu via the keyboard. -
Alba Mendez over 10 years@RangiKeen good point. Maybe it could be done more cleanly with an overriden
JPopupMenu
. -
Alba Mendez over 10 years@RangiKeen Oh god, I just realized this is what your answer is about! Upvoted.
-
palindrom over 7 yearsthe missing part is
jList=new JList<ContactItem>() { @Override public Point getPopupLocation(MouseEvent event) { return JListUtility.getPopupLocation(this, event); } };
-
Rangi Keen over 7 yearsThanks, @palindrom. I updated my answer to include the
getPopupLocation
usage