package esmska.gui; import esmska.Context; import esmska.data.Icons; import esmska.data.Queue; import esmska.utils.L10N; import esmska.utils.RuntimeUtils; import java.awt.AWTException; import java.awt.Image; import java.awt.MenuItem; import java.awt.PopupMenu; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFrame; import javax.swing.SwingUtilities; import java.awt.Dimension; import java.util.ResourceBundle; /** Display icon in the notification area (aka system tray) * * @author ripper */ public class NotificationIcon { private static NotificationIcon instance; private static boolean installed; private static final Logger logger = Logger.getLogger(NotificationIcon.class.getName()); private static final ResourceBundle l10n = L10N.l10nBundle; private static final Queue queue = Queue.getInstance(); private static final String pauseQueue = l10n.getString("Pause_sms_queue"); private static final String unpauseQueue = l10n.getString("Unpause_sms_queue"); private static final String showWindow = l10n.getString("Show_program"); private static final String hideWindow = l10n.getString("Hide_program"); private static Boolean supported = null; /** tray logo image */ private static Image trayImageDefault; private PopupMenu popup = null; private TrayIcon trayIcon = null; private MenuItem toggleItem, pauseQueueItem, historyItem, configItem, quitItem, separatorItem; private NotificationIcon() { // show/hide main window toggleItem = new MenuItem(l10n.getString("Show/hide_program")); toggleItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { toggleMainFrameVisibility(); } }); // pause/unpause sms queue pauseQueueItem = new MenuItem(l10n.getString("NotificationIcon.Pause/unpause_sending")); pauseQueueItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { queue.setPaused(!queue.isPaused()); } }); // show history historyItem = new MenuItem(l10n.getString("History")); historyItem.addActionListener(Actions.getHistoryAction()); // show settings configItem = new MenuItem(l10n.getString("Preferences")); configItem.addActionListener(Actions.getConfigAction()); // exit program quitItem = new MenuItem(l10n.getString("Quit")); quitItem.addActionListener(Actions.getQuitAction()); // separator separatorItem = new MenuItem("-"); // popup menu popup = new PopupMenu(); Context.mainFrame.add(popup); //every popup must have parent // populate menu popup.add(toggleItem); popup.add(pauseQueueItem); popup.add(historyItem); popup.add(configItem); popup.add(separatorItem); popup.add(quitItem); //unpopulate menu on some platforms switch (RuntimeUtils.detectOS()) { //on MAC, it's not needed to have items to system provided actions case MAC_OS_X: popup.remove(toggleItem); popup.remove(configItem); popup.remove(separatorItem); popup.remove(quitItem); break; } // add default action on left click MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { //single left click toggles window if (SwingUtilities.isLeftMouseButton(e) && !e.isPopupTrigger()) { toggleMainFrameVisibility(); } //update labels on items updateItems(); } }; //choose best icon size String logo = "esmska.png"; if (isSupported()) { Dimension size = SystemTray.getSystemTray().getTrayIconSize(); if (size.getWidth() <= 16 && size.getHeight() <= 16) { logo = "esmska-16.png"; } else if (size.getWidth() <= 32 && size.getHeight() <= 32) { logo = "esmska-32.png"; } else if (size.getWidth() <= 64 && size.getHeight() <= 64) { logo = "esmska-64.png"; } } // construct a TrayIcon trayImageDefault = Icons.get(logo).getImage(); trayIcon = new TrayIcon(trayImageDefault, "Esmska", popup); trayIcon.setImageAutoSize(true); trayIcon.addMouseListener(mouseAdapter); } /** Get instance of NotificationIcon. This class is singleton. * @return instance if notification area is supported, null otherwise */ public static NotificationIcon getInstance() { if (!isSupported()) { return null; } if (instance == null) { instance = new NotificationIcon(); } return instance; } /** Show or hide main window. Icon must be installed first. */ public static void toggleMainFrameVisibility() { if (!installed) { return; } logger.fine("Toggling mainframe visibility..."); MainFrame frame = Context.mainFrame; //if iconified, just deiconify and return if ((frame.getExtendedState() & JFrame.ICONIFIED) != 0) { int state = frame.getExtendedState() & ~JFrame.ICONIFIED; //erase iconify bit frame.setExtendedState(state); return; } //toggle visibility frame.setVisible(!frame.isVisible()); } /** Update labels in popup menu according to current program state */ private void updateItems() { MainFrame frame = Context.mainFrame; boolean queuePaused = queue.isPaused(); pauseQueueItem.setLabel(queuePaused ? unpauseQueue : pauseQueue); //visible if visible and not iconified boolean visible = frame.isVisible() && (frame.getExtendedState() & JFrame.ICONIFIED) == 0; toggleItem.setLabel(visible ? hideWindow : showWindow); } /** Install a new icon in the notification area. If an icon is already installed, * nothing happens. If notification area is not supported, nothing happens. */ public static void install() { logger.fine("Installing notification icon..."); if (!isSupported()) { logger.fine("Notification icon not supported"); return; } getInstance(); SystemTray tray = SystemTray.getSystemTray(); try { tray.remove(instance.trayIcon); tray.add(instance.trayIcon); installed = true; } catch (AWTException ex) { logger.log(Level.WARNING, "Can't install program icon in the notification area", ex); } } /** Remove an icon from notification area. If there is no icon installed, * nothing happens. If notification area is not supported, nothing happens. */ public static void uninstall() { logger.fine("Uninstalling notification icon..."); if (!isSupported()) { logger.fine("Notification icon not supported"); return; } getInstance(); //if mainframe currently hidden, show it before removing icon from system tray if (!Context.mainFrame.isVisible()) { toggleMainFrameVisibility(); } SystemTray tray = SystemTray.getSystemTray(); tray.remove(instance.trayIcon); installed = false; } /** Returns whether notification area is supported on this system. */ public static boolean isSupported() { if (supported == null) { supported = SystemTray.isSupported(); //doesn't work for Gnome 3: http://code.google.com/p/esmska/issues/detail?id=404 if (RuntimeUtils.isGnome3Desktop()) { supported = false; } } return supported; } /** Returns whether the notification icon is currently installed */ public static boolean isInstalled() { return installed; } /** Returns the popup menu on notification icon. */ public PopupMenu getPopup() { return popup; } }