I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit dd50b148 authored by zzz's avatar zzz
Browse files

DTG: Implement second TrayManager menu implementation in Swing.

Use Swing for non-Windows menus because AWT looks terrible on Linux
and the button handling there is almost impossible to fix
TODO: test on Mac
parent 390b2d40
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,8 @@ import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingWorker;
import net.i2p.I2PAppContext;
......@@ -20,20 +22,17 @@ import net.i2p.desktopgui.router.RouterManager;
*/
class ExternalTrayManager extends TrayManager {
public ExternalTrayManager(I2PAppContext ctx, Main main) {
super(ctx, main);
public ExternalTrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
super(ctx, main, useSwing);
}
@Override
public PopupMenu getMainMenu() {
PopupMenu popup = new PopupMenu();
MenuItem startItem = new MenuItem(_t("Start I2P"));
startItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.start();
......@@ -48,10 +47,36 @@ class ExternalTrayManager extends TrayManager {
//since that risks killing the I2P process as well.
tray.remove(trayIcon);
}
}.execute();
}
});
popup.add(startItem);
return popup;
}
public JPopupMenu getSwingMainMenu() {
JPopupMenu popup = new JPopupMenu();
JMenuItem startItem = new JMenuItem(_t("Start I2P"));
startItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.start();
return null;
}
@Override
protected void done() {
trayIcon.displayMessage(_t("Starting"), _t("I2P is starting!"), TrayIcon.MessageType.INFO);
//Hide the tray icon.
//We cannot stop the desktopgui program entirely,
//since that risks killing the I2P process as well.
tray.remove(trayIcon);
}
}.execute();
}
});
popup.add(startItem);
return popup;
......
......@@ -5,6 +5,9 @@ import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingWorker;
import net.i2p.desktopgui.router.RouterManager;
......@@ -23,14 +26,14 @@ class InternalTrayManager extends TrayManager {
private final RouterContext _context;
private final Log log;
private MenuItem _restartItem, _stopItem, _cancelItem;
private JMenuItem _jrestartItem, _jstopItem, _jcancelItem;
public InternalTrayManager(RouterContext ctx, Main main) {
super(ctx, main);
public InternalTrayManager(RouterContext ctx, Main main, boolean useSwing) {
super(ctx, main, useSwing);
_context = ctx;
log = ctx.logManager().getLog(InternalTrayManager.class);
}
@Override
public PopupMenu getMainMenu() {
PopupMenu popup = new PopupMenu();
......@@ -171,6 +174,146 @@ class InternalTrayManager extends TrayManager {
return popup;
}
public JPopupMenu getSwingMainMenu() {
JPopupMenu popup = new JPopupMenu();
JMenuItem browserLauncher = new JMenuItem(_t("Launch I2P Browser"));
browserLauncher.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
return null;
}
@Override
protected void done() {
try {
I2PDesktop.browse("http://localhost:7657");
} catch (BrowseException e1) {
log.log(Log.WARN, "Failed to open browser!", e1);
}
}
}.execute();
}
});
JMenu desktopguiConfigurationLauncher = new JMenu(_t("Configure I2P System Tray"));
JMenuItem configSubmenu = new JMenuItem(_t("Disable"));
configSubmenu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
configureDesktopgui(false);
return null;
}
}.execute();
}
});
final JMenuItem restartItem;
if (_context.hasWrapper()) {
restartItem = new JMenuItem(_t("Restart I2P"));
restartItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.restartGracefully(_context);
return null;
}
}.execute();
}
});
} else {
restartItem = null;
}
final JMenuItem stopItem = new JMenuItem(_t("Stop I2P"));
stopItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.shutDownGracefully(_context);
return null;
}
}.execute();
}
});
final JMenuItem restartItem2;
if (_context.hasWrapper()) {
restartItem2 = new JMenuItem(_t("Restart I2P Immediately"));
restartItem2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.restart(_context);
return null;
}
}.execute();
}
});
} else {
restartItem2 = null;
}
final JMenuItem stopItem2 = new JMenuItem(_t("Stop I2P Immediately"));
stopItem2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.shutDown(_context);
return null;
}
}.execute();
}
});
final JMenuItem cancelItem = new JMenuItem(_t("Cancel I2P Shutdown"));
cancelItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
RouterManager.cancelShutdown(_context);
return null;
}
}.execute();
}
});
popup.add(browserLauncher);
popup.addSeparator();
desktopguiConfigurationLauncher.add(configSubmenu);
popup.add(desktopguiConfigurationLauncher);
popup.addSeparator();
if (_context.hasWrapper())
popup.add(restartItem);
popup.add(stopItem);
if (_context.hasWrapper())
popup.add(restartItem2);
popup.add(stopItem2);
popup.add(cancelItem);
_jrestartItem = restartItem;
_jstopItem = stopItem;
_jcancelItem = cancelItem;
return popup;
}
/**
* Update the menu
* @since 0.9.26
......@@ -179,15 +322,23 @@ class InternalTrayManager extends TrayManager {
boolean x = RouterManager.isShutdownInProgress(_context);
if (_restartItem != null)
_restartItem.setEnabled(!x);
_stopItem.setEnabled(!x);
_cancelItem.setEnabled(x);
if (_stopItem != null)
_stopItem.setEnabled(!x);
if (_cancelItem != null)
_cancelItem.setEnabled(x);
if (_jrestartItem != null)
_jrestartItem.setEnabled(!x);
if (_jstopItem != null)
_jstopItem.setEnabled(!x);
if (_jcancelItem != null)
_jcancelItem.setEnabled(x);
}
/**
* @since 0.9.26 from removed gui/DesktopguiConfigurationFrame
*/
private void configureDesktopgui(boolean enable) {
String property = "desktopgui.enabled";
String property = Main.PROP_ENABLE;
String value = Boolean.toString(enable);
try {
......
......@@ -15,6 +15,7 @@ import net.i2p.desktopgui.util.*;
import net.i2p.router.RouterContext;
import net.i2p.router.app.RouterApp;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
import net.i2p.util.Translate;
import net.i2p.util.I2PProperties.I2PPropertyCallback;
......@@ -29,6 +30,8 @@ public class Main implements RouterApp {
private final Log log;
private ClientAppState _state = UNINITIALIZED;
private TrayManager _trayManager;
public static final String PROP_ENABLE = "desktopgui.enabled";
private static final String PROP_SWING = "desktopgui.swing";
/**
* @since 0.9.26
......@@ -60,10 +63,11 @@ public class Main implements RouterApp {
*/
private synchronized void startUp() throws Exception {
final TrayManager trayManager;
boolean useSwing = _appContext.getProperty(PROP_SWING, !SystemVersion.isWindows());
if (_context != null)
trayManager = new InternalTrayManager(_context, this);
trayManager = new InternalTrayManager(_context, this, useSwing);
else
trayManager = new ExternalTrayManager(_appContext, this);
trayManager = new ExternalTrayManager(_appContext, this, useSwing);
trayManager.startManager();
_trayManager = trayManager;
changeState(RUNNING);
......@@ -72,14 +76,12 @@ public class Main implements RouterApp {
if (_context != null) {
_context.addPropertyCallback(new I2PPropertyCallback() {
@Override
public void propertyChanged(String arg0, String arg1) {
if(arg0.equals(Translate.PROP_LANG)) {
trayManager.languageChanged();
}
}
});
}
}
......
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;
......@@ -21,6 +32,7 @@ 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
......@@ -29,37 +41,99 @@ abstract class TrayManager {
/**
* Instantiate tray manager.
*/
protected TrayManager(I2PAppContext ctx, Main main) {
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()) {
// TODO figure out how to get menu to pop up on left-click
// left-click does nothing by default
// MouseListener, MouseEvent, ...
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 = new TrayIcon(getTrayImage(), tooltip, getMainMenu());
trayIcon.setImageAutoSize(true); //Resize image to fit the system tray
tray.add(trayIcon);
// 16x16 on Windows, 24x24 on Linux, but that will probably vary
//System.out.println("Tray icon size is " + trayIcon.getSize());
trayIcon.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent m) { updateMenu(); }
public void mouseEntered(MouseEvent m) { updateMenu(); }
public void mouseExited(MouseEvent m) { updateMenu(); }
public void mousePressed(MouseEvent m) { updateMenu(); }
public void mouseReleased(MouseEvent m) { updateMenu(); }
});
} else {
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) {
// http://stackoverflow.com/questions/17258250/changing-the-laf-of-a-popupmenu-for-a-trayicon-in-java
// menu visible check is failsafe, for when menu gets cancelled
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) {}
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;
}
/**
......@@ -76,8 +150,11 @@ abstract class TrayManager {
}
public synchronized void languageChanged() {
if (trayIcon != null)
trayIcon.setPopupMenu(getMainMenu());
if (trayIcon != null) {
if (!_useSwing)
trayIcon.setPopupMenu(getMainMenu());
// else TODO
}
}
/**
......@@ -86,6 +163,13 @@ abstract class TrayManager {
*/
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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment