1

Currently, the PopupMenu will show up when I right-click on the TrayIcon in the SystemTray. However, I want it to do the same when I left-click on the TrayIcon.

I thought I might accomplish this by using a mouseListener on the TrayIcon, but I don't know what method to invoke in the mouseClicked event to achieve the desired results.

icon = new TrayIcon(img, tooltip, popup);

     icon.addMouseListener(
           new MouseAdapter() {
              public void mouseClicked(MouseEvent e) {
                 popup.setEnabled(true);
              }
           });

Using the setEnabled() method does not make the popup menu appear when I left-click the TrayIcon. It actually has no noticeable effect. I'm wondering what method I should use in the mouseClicked() body in order to make the popup show up when it is left-clicked.

fvgs
  • 21,412
  • 9
  • 33
  • 48
  • Got some example code? You might like to take a look at [Using a JPopupMenu in TrayIcon](http://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html)for some ideas. Ps you're looking for MouseEvent.BUTTON1 ;) – MadProgrammer Feb 03 '13 at 08:00
  • Added some code. And thanks, I'll look into using JMenus instead. Any particular benefit? Also, what do you mean by MouseEvent.BUTTON1? – fvgs Feb 03 '13 at 08:20
  • 1
    The `JPopupMenu` is a little more flexible in what you can add to it (like Icon's for example). In the `mouseClicked` event, you want to use the `MosueEvent` object to determine which button was clicked. You want to use `MouseEvent#getButton` and compare to `MouseEvent.Button1` to see if the are equal. Have a look at [How to write a Mouse Listener](http://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html) for more examples – MadProgrammer Feb 03 '13 at 08:40

2 Answers2

8

Basically, in your mouse listener, you need to determine which button was pressed (and optional, how many times).

The critical piece of code is this...

if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1) { ... }

I've also included some additional code that makes sure the popup does not cover the task bar and is displayed within the viewable area of the screen (it's a nit pick of mine ;))

public class TestTrayIcon02 {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }
                try {
                    final TrayIcon ti = new TrayIcon(ImageIO.read(getClass().getResource("/Smiley.png")), "Have a nice day");
                    final JPopupMenu popup = new JPopupMenu();

                    JMenuItem mi = new JMenuItem("Get me some");
                    mi.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            SystemTray.getSystemTray().remove(ti);
                            System.exit(0);
                        }
                    });

                    popup.add(mi);
                    ti.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseClicked(MouseEvent e) {
                            if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1) {
                                Rectangle bounds = getSafeScreenBounds(e.getPoint());
                                Point point = e.getPoint();
                                int x = point.x;
                                int y = point.y;
                                if (y < bounds.y) {
                                    y = bounds.y;
                                } else if (y > bounds.y + bounds.height) {
                                    y = bounds.y + bounds.height;
                                }
                                if (x < bounds.x) {
                                    x = bounds.x;
                                } else if (x > bounds.x + bounds.width) {
                                    x = bounds.x + bounds.width;
                                }
                                if (x + popup.getPreferredSize().width > bounds.x + bounds.width) {
                                    x = (bounds.x + bounds.width) - popup.getPreferredSize().width;
                                }
                                if (y + popup.getPreferredSize().height > bounds.y + bounds.height) {
                                    y = (bounds.y + bounds.height) - popup.getPreferredSize().height;
                                }
                                popup.setLocation(x, y);
                                popup.setVisible(true);
                            }
                        }
                    });

                    SystemTray.getSystemTray().add(ti);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static Rectangle getSafeScreenBounds(Point pos) {

        Rectangle bounds = getScreenBoundsAt(pos);
        Insets insets = getScreenInsetsAt(pos);

        bounds.x += insets.left;
        bounds.y += insets.top;
        bounds.width -= (insets.left + insets.right);
        bounds.height -= (insets.top + insets.bottom);

        return bounds;

    }

    public static Insets getScreenInsetsAt(Point pos) {
        GraphicsDevice gd = getGraphicsDeviceAt(pos);
        Insets insets = null;
        if (gd != null) {
            insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration());
        }
        return insets;
    }

    public static Rectangle getScreenBoundsAt(Point pos) {
        GraphicsDevice gd = getGraphicsDeviceAt(pos);
        Rectangle bounds = null;
        if (gd != null) {
            bounds = gd.getDefaultConfiguration().getBounds();
        }
        return bounds;
    }

    public static GraphicsDevice getGraphicsDeviceAt(Point pos) {

        GraphicsDevice device = null;

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice lstGDs[] = ge.getScreenDevices();

        ArrayList<GraphicsDevice> lstDevices = new ArrayList<GraphicsDevice>(lstGDs.length);

        for (GraphicsDevice gd : lstGDs) {

            GraphicsConfiguration gc = gd.getDefaultConfiguration();
            Rectangle screenBounds = gc.getBounds();

            if (screenBounds.contains(pos)) {

                lstDevices.add(gd);

            }

        }

        if (lstDevices.size() > 0) {
            device = lstDevices.get(0);
        } else {
            device = ge.getDefaultScreenDevice();
        }

        return device;

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • You can also take a look at [`SwingUtilities.isXxxMouseButton`](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html) methods which might help you detect which mouse button has been pressed – MadProgrammer Feb 08 '13 at 02:15
  • 1
    Is there a way to have the JPopupMenu appear like a typical windows menu other than customizing it extensively? I saw the line `UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());`, but it didn't seem to have any effect when I tried it. *I'll check in here tomorrow. Thanks for the help. – fvgs Feb 08 '13 at 02:17
  • That depends if you're using `JPopupMenu` or `PopupMenu` – MadProgrammer Feb 08 '13 at 02:45
  • How does it depend? I'm using JPopupMenu. – fvgs Feb 09 '13 at 06:13
  • Well, the PopupMenu uses a native peer/window, where as JPopupMenu uses a type of skinning to represent the UI. In my example, I use the system look and feel that should make it look like any other popup – MadProgrammer Feb 09 '13 at 06:15
  • MadProgrammer, how would you suggest to hide the popup when user clicks outside the popup like on the taskbar or another window for example. – km1 Jun 10 '13 at 17:11
1

What you're trying to do is apparently not possible:

You cannot show the PopupMenu with its show method since you need to specify a JComponent but your TrayIcon isn't one (weird enough though that TrayIcon still manages to do it, so apparently there is a way, don't ask me though..). So, as MadProgrammer suggested, you should try using JPopupMenu instead. Don't add it to your TrayIcon for that won't be possible, but display your JPopupMenu by adding a MouseListener to your TrayIcon. That should do the trick:

final TrayIcon tray = new TrayIcon( img, tooltip, null);
final JPopupMenu menu = new JPopupMenu();
... // your menu initialization.
tray.addMouseListener( new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent evt) {
        menu.setLocation( evt.getPoint() );
        menu.setVisible( true );
    }
}
Danyel
  • 2,180
  • 18
  • 32
  • 1- this doesn't check to see which button is pressed, which as the original question. 2- it doesn't take into account that the popup may be shown offscreen (nit picky, but that would be the next question) – MadProgrammer Feb 03 '13 at 20:16
  • Since OP wanted it to be the left trigger, I just left it out. Doesn't the PopupMenu adjust itself according to screen location? I thought it did. – Danyel Feb 04 '13 at 05:38
  • It can, but it won't take into account the task bar. So while it won't necessarily pop off the screen, it may cover the task bar, depending on the it's location. This might not be desirable behavior. You example will also pop the popup on any mouse click ;) – MadProgrammer Feb 04 '13 at 05:51
  • Yes, it wasn't a fully shippable example, only a "snippet". I probably should have added the `evt.isLeftTrigger()`(?), you're right. But it gets OP closer to what he wants, nonetheless. – Danyel Feb 04 '13 at 05:55
  • Given the OP's question about `MouseEvent.BUTTON1` I wouldn't be convinced ;) – MadProgrammer Feb 04 '13 at 06:02