Search code examples
javawindowsawtsystem-traytrayicon

Changing the enabled state of a TrayIcon menu item dynamically


I have code that successfully uses SystemTray, TrayIcon, PopupMenu, MenuItem. I'm trying to get the enabled state of the MenuItem to be accurate based on the state of something that's happening in several other threads. I have the ability to run a process (which I called getUnknownConditionFromTheThreads()) that can tell me if I need to enable or disable the menu item at that particular instant in time.

The problem is that when the user right-clicks the tray icon, the menu items are displayed before the setEnabled() is run.

In other words, the user right-clicks the tray icon, the menu shows with the enable/disable state at the time, then the mouseClicked() method runs and the setEnabled() runs, but it's too late!

So I could attempt to pump the state into the tray icon so it would be ready in advance, but that would mean a lot of complexity, because there are many independent threads and no one of them "knows" what the enable/disable state of the menu item should be.

My question: Is there a "trick" to get the popup menu item enabled/disabled based on an event generated by the user in the tray icon?

The code below is generally what I'm doing. There's nothing "fancy" here, just to show that when the user right-clicks, enabling/disabling the menu item doesn't reflect.

Here's how the code works now: Say the default for the menu item is enabled, and next call getUnknownConditionFromTheThreads() would return false. The user would see the item enabled the first time right-clicking. The next right-click, it would be disabled.

PopupMenu popup = new PopupMenu();
final MenuItem itemToDisable = new MenuItem("Testing");
ActionListener listener = new ActionListener(){
    public void actionPerformed(ActionEvent arg0) {
    .....
    }
};
itemToDisable.addActionListener(listener);
popup.add(itemToDisable);
trayIcon = new TrayIcon(xImg, xMsg, popup);
trayIcon.addActionListener(listener);
trayIcon.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        boolean someCondition = getUnknownConditionFromTheThreads();
        itemToDisable.setEnabled(someCondition);  // Not reflected first time
    }
});
SystemTray.getSystemTray().add(trayIcon);

Solution

  • Instead of mouseClicked, use mousePressed:

    trayIcon.addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            boolean someCondition = getUnknownConditionFromTheThreads();
            itemToDisable.setEnabled(someCondition);  // Reflected first time
        }
    });