Java swing popup menu and jlist

21,325

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);
    }
};
Share:
21,325
VaclavDedik
Author by

VaclavDedik

Updated on July 09, 2022

Comments

  • VaclavDedik
    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
    Andrew Thompson almost 13 years
    Not a big fan of untested code, but was compelled to give an up vote for checking isPopupTrigger() - good call.
  • Boro
    Boro almost 13 years
    To be honest I would go with if(e.getButton() == MouseEvent.BUTTON3) instead of if(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
    Andrew Thompson almost 13 years
    @Boro: Interesting point ( & yet another affirmation for 'tested code' ;). What OS/Java are you running?
  • Alba Mendez
    Alba Mendez almost 13 years
    I've updated the code to check both press and release events.
  • Boro
    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 Mac if(e.getButton() == MouseEvent.BUTTON3) would work?
  • Boro
    Boro almost 13 years
    Ok 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
    Rangi Keen over 10 years
    Note that avoiding setComponentPopupMenu in this way means that you won't be able to invoke the popup menu via the keyboard.
  • Alba Mendez
    Alba Mendez over 10 years
    @RangiKeen good point. Maybe it could be done more cleanly with an overriden JPopupMenu.
  • Alba Mendez
    Alba Mendez over 10 years
    @RangiKeen Oh god, I just realized this is what your answer is about! Upvoted.
  • palindrom
    palindrom over 7 years
    the missing part is jList=new JList<ContactItem>() { @Override public Point getPopupLocation(MouseEvent event) { return JListUtility.getPopupLocation(this, event); } };
  • Rangi Keen
    Rangi Keen over 7 years
    Thanks, @palindrom. I updated my answer to include the getPopupLocation usage