JDK-8042109 : Setting popupmenu on TrayIcon do not work if done *after* adding TrayIcon
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7u9,8,9
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: os_x
  • Submitted: 2013-03-20
  • Updated: 2014-04-29
  • Resolved: 2014-04-29
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_09 " 
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
OS X 10.8.2 (12C60)

EXTRA RELEVANT SYSTEM CONFIGURATION :
MacBookPro8,2, Intel Core i7 4 core, multimonitor setup, 8 GB RAM,

A DESCRIPTION OF THE PROBLEM :
When clicking on the TrayIcon item in the status bar on Mac OS X, the associated popup menu is not opened. The issue seems to be that if the TrayIcon is added to the SystemTray before the PopupMenu is set on the TrayIcon, then the native menu is not constructed.

This issue may extend to updating the associated PopupMenu after adding the TrayIcon to the SystemTray


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
a) Run the attached test program.
b) Click on the  " red "  TrayIcon menu and observe the bug

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The submenu of the red TrayIcon should be shown
ACTUAL -
The submenu of the red TrayIcon is not shown

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.RenderingHints;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;

public class TrayIconBug {
  /**
   * This class demostrates a bug in the TrayIcon class in which
   * updates to a TrayIcon added to a SystemTray do not take effect.
   *
   * The issue seems to be that if the tray icon is added to the system
   * tray *before* setting the popup menu of the tray icon, then the popup
   * menu is not picked up by the native implementation of the system tray /
   * tray icon
   */
  
  private static final String bugId =  " 1 " ;
  private static final String applicationName =  " TrayIconBug "  + bugId;
  
  private static int trayIconImageSize = 32;
  private static int trayIconImageInset = 4;
  
  /**
   * Create a small image of a green/red circle to use as the icon for the tray icon
   *
   * @param addSystemTrayBeforeMenuItemsAreAdded if true, a red cirle is returned. If false, a green circle is returned
   * @return the Image created
   */
  private static Image createTrayIconImage(final boolean addSystemTrayBeforeMenuItemsAreAdded) {
    /**
     * Create a small image of a red circle to use as the icon for the tray icon
     */
    final BufferedImage trayImage = new BufferedImage(trayIconImageSize, trayIconImageSize, BufferedImage.TYPE_INT_ARGB);
    final Graphics2D trayImageGraphics = (Graphics2D) trayImage.getGraphics();
    
    trayImageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
    trayImageGraphics.setColor(new Color(255, 255, 255, 0));
    trayImageGraphics.fillRect(0, 0, trayImage.getWidth(), trayImage.getHeight());
    
    if (addSystemTrayBeforeMenuItemsAreAdded) {
      trayImageGraphics.setColor(Color.red);
    } else {
      trayImageGraphics.setColor(Color.green);
    }
    
    trayImageGraphics.fillOval
      ( trayIconImageInset,
        trayIconImageInset,
        trayImage.getWidth() - 2 * trayIconImageInset,
        trayImage.getHeight() - 2 * trayIconImageInset);

    trayImageGraphics.setColor(Color.darkGray);
    
    trayImageGraphics.drawOval
      ( trayIconImageInset,
        trayIconImageInset,
        trayImage.getWidth() - 2 * trayIconImageInset,
        trayImage.getHeight() - 2 * trayIconImageInset);
    
    return trayImage;
  }
  
  /**
   * Create popup menu for system tray. Creates a popup menu like:
   *
   * Menu Item 1
   * Menu Item 2
   * Sub menu
   *   Submenu Item 1
   *   Submenu Item 2
   *
   * @return the PopupMenu created
   */
  private static PopupMenu createTrayIconPopupMenu() {
    final PopupMenu trayIconPopupMenu = new PopupMenu();
    
    for (int i = 1; i <= 2; i++) {
      final String popupMenuItemText =  " Menu Item  "  + i;
      
      final MenuItem popupMenuItem = new MenuItem(popupMenuItemText);
      
      popupMenuItem.addActionListener(new ActionListener() {
        public void actionPerformed(final ActionEvent ae) {
          System.out.println(popupMenuItemText +  "  action listener called... " );
        }
      });
      
      trayIconPopupMenu.add(popupMenuItem);
    }
    
    final PopupMenu popupSubMenu = new PopupMenu( " Submenu " );

    for (int i = 1; i <= 2; i++) {
      final String popupMenuItemText =  " Submenu Item  "  + i;
      
      final MenuItem popupMenuItem = new MenuItem(popupMenuItemText);
      
      popupMenuItem.addActionListener(new ActionListener() {
        public void actionPerformed(final ActionEvent ae) {
          System.out.println(popupMenuItemText +  "  action listener called... " );
        }
      });
      
      popupSubMenu.add(popupMenuItem);
    }

    trayIconPopupMenu.add(popupSubMenu);
    
    return trayIconPopupMenu;
  }
  
  private static void createSystemTrayIcons() {
    for (final boolean addSystemTrayBeforeMenuItemsAreAdded : new Boolean[] {true, false}) {
      /**
       * Create a tray icon with two submenus:  " Menu item 1 "  &  " Menu item 2 " . This tray icon
       * is visible in the status bar on Mac OS X as a green / red circle. Clicking it will
       * open the submenu of the tray icon if running under Java 1.6. Running under Java 1.7u9
       * the submenu of the tray icon will *not* open.
       */
      final TrayIcon trayIcon = new TrayIcon(createTrayIconImage(addSystemTrayBeforeMenuItemsAreAdded));
      trayIcon.setImageAutoSize(true);
      trayIcon.setToolTip(applicationName +  "  system tray " );
      
      try {
        // Add tray icon to system tray *before* adding popup menu to demonstrate buggy behaviour
        if (addSystemTrayBeforeMenuItemsAreAdded) {
          SystemTray.getSystemTray().add(trayIcon);
        }
        
        // Add tray icon action listenr for logging clicks on the tray icon
        trayIcon.addActionListener(new ActionListener() {
          public void actionPerformed(final ActionEvent ae) {
            System.out.println( " Tray icon action listener called... " );
          }
        });
        
        trayIcon.setPopupMenu(createTrayIconPopupMenu());
        
        // Add tray icon to system tray *add* adding popup menu to demonstrate correct behaviour
        if (!addSystemTrayBeforeMenuItemsAreAdded) {
          SystemTray.getSystemTray().add(trayIcon);
        }
      } catch (final AWTException awte) {
        awte.printStackTrace();
      }
    }
  }
  
  private static void createFrame() {
    /**
     * Create a small window on the screen to make the application visible
     * to the user. A small explanation is included in the window shown.
     *
     * Clicking the close button of the window quits the application
     */
    final JPanel panel = new JPanel();
    panel.setBorder(new CompoundBorder(panel.getBorder(), new EmptyBorder(15, 15, 15, 15)));
    panel.setLayout(new BorderLayout());
    
    panel.add(new JLabel(applicationName +  "  demo application " ), BorderLayout.NORTH);
    panel.add(new JLabel( " Click the red tray icon in the status bar to observe the buggy behaviour. " ), BorderLayout.CENTER);
    panel.add(new JLabel( " Click the green tray icon in the status bar to observe the correct behaviour. " ), BorderLayout.SOUTH);

    final JFrame frame = new JFrame(applicationName +  "  information " );
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    
    frame.add(panel);

    frame.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosed(final WindowEvent we) {
        System.exit(0);
      }
    });
    
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
  
  public static void main(final String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        if (SystemTray.isSupported()) {
          createSystemTrayIcons();
          
          createFrame();
        }
      }
    });
  }
}

---------- END SOURCE ----------