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(); } } }); } }