package net.i2p.desktopgui; import java.awt.AWTException; import java.awt.Dimension; import java.awt.Font; import java.awt.Image; import java.awt.PopupMenu; import java.awt.SystemTray; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.net.URL; import javax.swing.JFrame; import javax.swing.JPopupMenu; import javax.swing.event.MenuKeyEvent; import javax.swing.event.MenuKeyListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import net.i2p.I2PAppContext; import net.i2p.desktopgui.i18n.DesktopguiTranslator; import net.i2p.util.SystemVersion; /** * Manages the tray icon life. */ abstract class TrayManager { protected final I2PAppContext _appContext; protected final Main _main; protected final boolean _useSwing; ///The tray area, or null if unsupported protected SystemTray tray; ///Our tray icon, or null if unsupported protected TrayIcon trayIcon; private static final String PNG_DIR = "/desktopgui/resources/images/"; private static final String MAC_ICON = "itoopie_black_24.png"; private static final String WIN_ICON = "itoopie_white_24.png"; private static final String LIN_ICON = "logo.png"; /** * Instantiate tray manager. */ protected TrayManager(I2PAppContext ctx, Main main, boolean useSwing) { _appContext = ctx; _main = main; _useSwing = useSwing; } /** * Add the tray icon to the system tray and start everything up. */ public synchronized void startManager() throws AWTException { if (!SystemTray.isSupported()) throw new AWTException("SystemTray not supported"); tray = SystemTray.getSystemTray(); // Windows typically has tooltips; Linux (at least Ubuntu) doesn't String tooltip = SystemVersion.isWindows() ? _t("I2P: Right-click for menu") : null; TrayIcon ti; if (_useSwing) ti = getSwingTrayIcon(tooltip); else ti = getAWTTrayIcon(tooltip); ti.setImageAutoSize(true); //Resize image to fit the system tray tray.add(ti); trayIcon = ti; } private TrayIcon getAWTTrayIcon(String tooltip) throws AWTException { PopupMenu menu = getMainMenu(); if (!SystemVersion.isWindows()) menu.setFont(new Font("Arial", Font.BOLD, 14)); TrayIcon ti = new TrayIcon(getTrayImage(), tooltip, menu); ti.addMouseListener(new MouseListener() { public void mouseClicked(MouseEvent m) {} public void mouseEntered(MouseEvent m) {} public void mouseExited(MouseEvent m) {} public void mousePressed(MouseEvent m) { updateMenu(); } public void mouseReleased(MouseEvent m) { updateMenu(); } }); return ti; } private TrayIcon getSwingTrayIcon(String tooltip) throws AWTException { // A JPopupMenu by itself is hard to get rid of, // so we hang it off a zero-size, undecorated JFrame. // http://stackoverflow.com/questions/1498789/jpopupmenu-behavior // http://stackoverflow.com/questions/2581314/how-do-you-hide-a-swing-popup-when-you-click-somewhere-else final JFrame frame = new JFrame(); // http://stackoverflow.com/questions/2011601/jframe-without-frame-border-maximum-button-minimum-button-and-frame-icon frame.setUndecorated(true); frame.setMinimumSize(new Dimension(0, 0)); frame.setSize(0, 0); final JPopupMenu menu = getSwingMainMenu(); menu.setFocusable(true); frame.add(menu); TrayIcon ti = new TrayIcon(getTrayImage(), tooltip, null); ti.addMouseListener(new MouseListener() { public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) { handle(e); } public void mouseReleased(MouseEvent e) { handle(e); } private void handle(MouseEvent e) { //System.out.println("Button " + e.getButton() + " Frame was visible? " + // frame.isVisible() + " menu was visible? " + menu.isVisible() + // " trigger? " + menu.isPopupTrigger(e)); // http://stackoverflow.com/questions/17258250/changing-the-laf-of-a-popupmenu-for-a-trayicon-in-java // menu visible check is never true if (!frame.isVisible() /* || !menu.isVisible() */ ) { frame.setLocation(e.getX(), e.getY()); frame.setVisible(true); menu.show(frame, 0, 0); } updateMenu(); } }); menu.addPopupMenuListener(new PopupMenuListener() { public void popupMenuCanceled(PopupMenuEvent e) { /* frame.setVisible(false); */ } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { frame.setVisible(false); } public void popupMenuWillBecomeVisible(PopupMenuEvent e) {} }); // this is to make it go away when we click elsewhere // doesn't do anything menu.addFocusListener(new FocusListener() { public void focusGained(FocusEvent e) {} public void focusLost(FocusEvent e) { frame.setVisible(false); } }); // this is to make it go away when we hit escape // doesn't do anything menu.addMenuKeyListener(new MenuKeyListener() { public void menuKeyPressed(MenuKeyEvent e) {} public void menuKeyReleased(MenuKeyEvent e) {} public void menuKeyTyped(MenuKeyEvent e) { if (e.getKeyChar() == (char) 0x1b) frame.setVisible(false); } }); return ti; } /** * Remove the tray icon from the system tray * * @since 0.9.26 */ public synchronized void stopManager() { if (tray != null && trayIcon != null) { tray.remove(trayIcon); tray = null; trayIcon = null; } } public synchronized void languageChanged() { if (trayIcon != null) { if (!_useSwing) trayIcon.setPopupMenu(getMainMenu()); // else TODO } } /** * Build a popup menu, adding callbacks to the different items. * @return popup menu */ protected abstract PopupMenu getMainMenu(); /** * Build a popup menu, adding callbacks to the different items. * @return popup menu * @since 0.9.26 */ protected abstract JPopupMenu getSwingMainMenu(); /** * Update the menu * @since 0.9.26 */ protected abstract void updateMenu(); /** * Get tray icon image from the desktopgui resources in the jar file. * @return image used for the tray icon * @throws AWTException if image not found */ private Image getTrayImage() throws AWTException { String img; if (SystemVersion.isWindows()) img = WIN_ICON; else if (SystemVersion.isMac()) img = MAC_ICON; else img = LIN_ICON; URL url = getClass().getResource(PNG_DIR + img); if (url == null) throw new AWTException("cannot load tray image " + img); Image image = Toolkit.getDefaultToolkit().getImage(url); return image; } protected String _t(String s) { return DesktopguiTranslator._t(_appContext, s); } /** * @since 0.9.26 */ protected String _t(String s, Object o) { return DesktopguiTranslator._t(_appContext, s, o); } }