1

I'm not sure if the problem is with my code or with Java 1.7.

In the below code (based largely on the Java Popup Demo), the popup will appear upon a mouse right click. The popup menu item will get highlighted upon mouse roll-over, and clicking on the JmenuItem gets the popup to disappear; however, the actionEvent of the JMenuItem is not fired upon clicking (which should be reported in the JTextArea).

Other nuggets: If I type the mnemonic for the JMenuItem (here "a"), then the actionEvent is fired (the event is reported in the JTextArea).

If I do not attach a custom Popup(Factory), then a mouse click fires the actionEvent as expected.

I'm using OSX 10.7.5

This problem occurs with:

    java version "1.7.0_07"
    Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
    Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)

This code behaves fine with:

    java version "1.6.0_33"
    Java(TM) SE Runtime Environment (build 1.6.0_33-b03-424-11M3720)
    Java HotSpot(TM) 64-Bit Server VM (build 20.8-b03-424, mixed mode)

Any help/thoughts are greatly appreciated! Self-contained code example is below.

Thanks

Andrew

import java.awt.*;
import java.awt.event.*;
import javax.swing.*; 

public class PopupMenuDemo implements ActionListener  {
    JTextArea output;
    JScrollPane scrollPane;
    String newline = "\n";

    public Container createContentPane() {
        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.setOpaque(true);
        output = new JTextArea(5, 30);
        output.setEditable(false);
        scrollPane = new JScrollPane(output);
        contentPane.add(scrollPane, BorderLayout.CENTER); 
        return contentPane;
    }

    public void createPopupMenu() {
        JMenuItem menuItem;
        JPopupMenu popup = new JPopupMenu();
        menuItem = new JMenuItem("A popup menu item", 'a');
        menuItem.addActionListener(this);
        popup.add(menuItem);
        MouseListener popupListener = new PopupListener(popup);
        output.addMouseListener(popupListener);
    }

    public void actionPerformed(ActionEvent e) {
        JMenuItem source = (JMenuItem)(e.getSource());
        String s = "Action event detected."
        + newline
        + "    Event source: " + source.getText()
        + " (an instance of " + source.getClass().getName() + ")";
        output.append(s + newline);
        output.setCaretPosition(output.getDocument().getLength());
    }

    private void createAndShowGUI() {
        JFrame frame = new JFrame("PopupMenuDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    PopupFactory.setSharedInstance(new MyPopupFactory());
        PopupMenuDemo demo = new PopupMenuDemo();
        frame.setContentPane(demo.createContentPane());

        demo.createPopupMenu();

        frame.setSize(450, 260);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            new PopupMenuDemo().createAndShowGUI();
        }
        });
    }

    class PopupListener extends MouseAdapter {
        JPopupMenu popup;

        PopupListener(JPopupMenu popupMenu) {
            popup = popupMenu;
        }

        public void mousePressed(MouseEvent e) {
            maybeShowPopup(e);
        }

        public void mouseReleased(MouseEvent e) {
            maybeShowPopup(e);
        }

        private void maybeShowPopup(MouseEvent e) {
            if (e.isPopupTrigger()) {
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
        }
    }

    class MyPopupFactory extends PopupFactory {
    public Popup getPopup(Component owner, Component contents, int x, int y) throws IllegalArgumentException {
        return new MyPopup(owner, contents, x, y);
    }
    }

    class MyPopup extends Popup {
    private JWindow popupWindow;

    MyPopup(Component owner, Component contents, int ownerX, int ownerY) {
        popupWindow = new JWindow();
        popupWindow.setLocation(ownerX, ownerY);
        popupWindow.getContentPane().add(contents, BorderLayout.CENTER);
        contents.invalidate();
    }

    public void show() {
        popupWindow.setVisible(true);
        popupWindow.pack();
    }

    public void hide() {
        popupWindow.setVisible(false);
        popupWindow.removeAll();
        popupWindow.dispose();
    }
    }
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • if your real goal is to get translucent popups, a [recent QA](http://stackoverflow.com/q/12801035/203657) might be worth a look – kleopatra Oct 15 '12 at 11:24

2 Answers2

1

The problem is the use of the constructor new JWindow(), which makes the new window unrelated to the owner component. So a click into the popup window is handled like a click into any unrelated part of the UI, canceling the popup.

You have to use something like new JWindow(SwingUtilities.getWindowAncestor(owner)) to make the popup window a child window of the owner’s window.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

Firstly, I would do away with using a MouseListener to monitor the popup trigger, you're better off using JComponent#setComponentPopupMenu and save you're self the hassle.

Secondly, I don't know why you wanted to supply your own popup factory, but it's causing you the problems. If you comment out the popupWindow.dispose line, it will work. As to why, I can't tell you, may IDE is playing up so I can't debug the code :P

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for the tip regarding setComponentPopupMenu. I'm creating my own popup factory in order to get per-pixel transparency in my pop-ups. Neither UIManager.put("ToolTip.background", new Color(0,0,0,0)); or UIManager.put("PopupMenu.background", new Color(0,0,0,0)); worked. So i'm forcing all popups to be JWindows with background color = (0,0,0,0). It was working in 1.6. Any other approaches would be very welcome. Thanks again. – user1743927 Oct 14 '12 at 03:10
  • If I put a PopupMenuListener on the popup, under Java 1.7 with my custom popup/popupfactory, then popupMenuCanceled() is called, followed by popupMenuWillBecomeInvisible(), when the JMenuItem is clicked. Under Java 1.7 without my custom popup/popupfactory (or under Java 1.6 with OR without my custom popup/popupfactory) only popupMenuWillBecomeInvisible() is called when the JMenuItem is clicked. Let me know if it would be useful to post code. It doesn't seem like popupMenuCanceled() should get called when I click on a menu item, no? – user1743927 Oct 14 '12 at 20:37
  • I agree, popup canceled seems wrong. The question is why does you're factory cause this behaviour – MadProgrammer Oct 14 '12 at 20:40
  • Right .. why does it cause it in 1.7? and/or why did it not cause it in 1.6? This is getting into murky depths of Java for me .. any thoughts or work-arounds would be most welcome .. – user1743927 Oct 14 '12 at 21:05
  • The best workaround I've come up with so far is: 1) extend default PopupFactory, 2) have custom PopupFactory retrieve a Popup by making call to default PopupFactory, 3) traverse returned Popup looking for JWindow component, 4) when found, modify background color as desired, 5) finally, return modified Popup from custom PopupFactory. – user1743927 Oct 15 '12 at 16:44
  • Yep, there's some weird focus management going on that I've not seen before (nor which I think we can reliably break into) which is basically things that focus has moved from one window to another :P – MadProgrammer Oct 16 '12 at 04:28
  • 1
    It’s an old question but since I ran into the same issue recently, I [found the problem](https://stackoverflow.com/a/67884745/2711488)… – Holger Jun 08 '21 at 09:15