Changeset dd50b14


Ignore:
Timestamp:
May 6, 2016 1:45:30 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
ecafcdd
Parents:
390b2d4
Message:

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

Location:
apps/desktopgui/src/net/i2p/desktopgui
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • apps/desktopgui/src/net/i2p/desktopgui/ExternalTrayManager.java

    r390b2d4 rdd50b14  
    77import java.awt.event.ActionListener;
    88
     9import javax.swing.JMenuItem;
     10import javax.swing.JPopupMenu;
    911import javax.swing.SwingWorker;
    1012
     
    2123class ExternalTrayManager extends TrayManager {
    2224       
    23     public ExternalTrayManager(I2PAppContext ctx, Main main) {
    24         super(ctx, main);
     25    public ExternalTrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
     26        super(ctx, main, useSwing);
    2527    }
    2628
    27     @Override
    2829    public PopupMenu getMainMenu() {
    2930        PopupMenu popup = new PopupMenu();
    3031        MenuItem startItem = new MenuItem(_t("Start I2P"));
    3132        startItem.addActionListener(new ActionListener() {
    32 
    3333            @Override
    3434            public void actionPerformed(ActionEvent arg0) {
    3535                new SwingWorker<Object, Object>() {
    36 
    3736                    @Override
    3837                    protected Object doInBackground() throws Exception {
     
    4948                        tray.remove(trayIcon);
    5049                    }
    51                    
    5250                }.execute();
    5351            }
    54            
     52        });
     53        popup.add(startItem);
     54        return popup;
     55    }
     56
     57    public JPopupMenu getSwingMainMenu() {
     58        JPopupMenu popup = new JPopupMenu();
     59        JMenuItem startItem = new JMenuItem(_t("Start I2P"));
     60        startItem.addActionListener(new ActionListener() {
     61            @Override
     62            public void actionPerformed(ActionEvent arg0) {
     63                new SwingWorker<Object, Object>() {
     64                    @Override
     65                    protected Object doInBackground() throws Exception {
     66                        RouterManager.start();
     67                        return null;
     68                    }
     69                   
     70                    @Override
     71                    protected void done() {
     72                        trayIcon.displayMessage(_t("Starting"), _t("I2P is starting!"), TrayIcon.MessageType.INFO);
     73                        //Hide the tray icon.
     74                        //We cannot stop the desktopgui program entirely,
     75                        //since that risks killing the I2P process as well.
     76                        tray.remove(trayIcon);
     77                    }
     78                }.execute();
     79            }
    5580        });
    5681        popup.add(startItem);
  • apps/desktopgui/src/net/i2p/desktopgui/InternalTrayManager.java

    r390b2d4 rdd50b14  
    66import java.awt.event.ActionListener;
    77
     8import javax.swing.JMenu;
     9import javax.swing.JMenuItem;
     10import javax.swing.JPopupMenu;
    811import javax.swing.SwingWorker;
    912
     
    2427    private final Log log;
    2528    private MenuItem _restartItem, _stopItem, _cancelItem;
    26 
    27     public InternalTrayManager(RouterContext ctx, Main main) {
    28         super(ctx, main);
     29    private JMenuItem _jrestartItem, _jstopItem, _jcancelItem;
     30
     31    public InternalTrayManager(RouterContext ctx, Main main, boolean useSwing) {
     32        super(ctx, main, useSwing);
    2933        _context = ctx;
    3034        log = ctx.logManager().getLog(InternalTrayManager.class);
    3135    }
    3236
    33     @Override
    3437    public PopupMenu getMainMenu() {
    3538        PopupMenu popup = new PopupMenu();
     
    172175    }
    173176
     177    public JPopupMenu getSwingMainMenu() {
     178        JPopupMenu popup = new JPopupMenu();
     179       
     180        JMenuItem browserLauncher = new JMenuItem(_t("Launch I2P Browser"));
     181        browserLauncher.addActionListener(new ActionListener() {
     182            @Override
     183            public void actionPerformed(ActionEvent arg0) {
     184                new SwingWorker<Object, Object>() {
     185                    @Override
     186                    protected Object doInBackground() throws Exception {
     187                        return null;
     188                    }
     189                   
     190                    @Override
     191                    protected void done() {
     192                        try {
     193                            I2PDesktop.browse("http://localhost:7657");
     194                        } catch (BrowseException e1) {
     195                            log.log(Log.WARN, "Failed to open browser!", e1);
     196                        }   
     197                    }
     198                }.execute();
     199            }
     200        });
     201
     202        JMenu desktopguiConfigurationLauncher = new JMenu(_t("Configure I2P System Tray"));
     203        JMenuItem configSubmenu = new JMenuItem(_t("Disable"));
     204        configSubmenu.addActionListener(new ActionListener() {
     205            @Override
     206            public void actionPerformed(ActionEvent arg0) {
     207                new SwingWorker<Object, Object>() {
     208                    @Override
     209                    protected Object doInBackground() throws Exception {
     210                        configureDesktopgui(false);
     211                        return null;
     212                    }
     213                }.execute();
     214            }
     215        });
     216
     217        final JMenuItem restartItem;
     218        if (_context.hasWrapper()) {
     219            restartItem = new JMenuItem(_t("Restart I2P"));
     220            restartItem.addActionListener(new ActionListener() {
     221                @Override
     222                public void actionPerformed(ActionEvent arg0) {
     223                    new SwingWorker<Object, Object>() {
     224                        @Override
     225                        protected Object doInBackground() throws Exception {
     226                            RouterManager.restartGracefully(_context);
     227                            return null;
     228                        }
     229                    }.execute();
     230                }
     231            });
     232        } else {
     233            restartItem = null;
     234        }
     235
     236        final JMenuItem stopItem = new JMenuItem(_t("Stop I2P"));
     237        stopItem.addActionListener(new ActionListener() {
     238            @Override
     239            public void actionPerformed(ActionEvent arg0) {
     240                new SwingWorker<Object, Object>() {
     241                    @Override
     242                    protected Object doInBackground() throws Exception {
     243                        RouterManager.shutDownGracefully(_context);
     244                        return null;
     245                    }
     246                }.execute();
     247            }
     248        });
     249
     250        final JMenuItem restartItem2;
     251        if (_context.hasWrapper()) {
     252            restartItem2 = new JMenuItem(_t("Restart I2P Immediately"));
     253            restartItem2.addActionListener(new ActionListener() {
     254                @Override
     255                public void actionPerformed(ActionEvent arg0) {
     256                    new SwingWorker<Object, Object>() {
     257                        @Override
     258                        protected Object doInBackground() throws Exception {
     259                            RouterManager.restart(_context);
     260                            return null;
     261                        }
     262                    }.execute();
     263                }
     264            });
     265        } else {
     266            restartItem2 = null;
     267        }
     268
     269        final JMenuItem stopItem2 = new JMenuItem(_t("Stop I2P Immediately"));
     270        stopItem2.addActionListener(new ActionListener() {
     271            @Override
     272            public void actionPerformed(ActionEvent arg0) {
     273                new SwingWorker<Object, Object>() {
     274                    @Override
     275                    protected Object doInBackground() throws Exception {
     276                        RouterManager.shutDown(_context);
     277                        return null;
     278                    }
     279                }.execute();
     280            }
     281        });
     282
     283        final JMenuItem cancelItem = new JMenuItem(_t("Cancel I2P Shutdown"));
     284        cancelItem.addActionListener(new ActionListener() {
     285            @Override
     286            public void actionPerformed(ActionEvent arg0) {
     287                new SwingWorker<Object, Object>() {
     288                    @Override
     289                    protected Object doInBackground() throws Exception {
     290                        RouterManager.cancelShutdown(_context);
     291                        return null;
     292                    }
     293                }.execute();
     294            }
     295        });
     296
     297        popup.add(browserLauncher);
     298        popup.addSeparator();
     299        desktopguiConfigurationLauncher.add(configSubmenu);
     300        popup.add(desktopguiConfigurationLauncher);
     301        popup.addSeparator();
     302        if (_context.hasWrapper())
     303            popup.add(restartItem);
     304        popup.add(stopItem);
     305        if (_context.hasWrapper())
     306            popup.add(restartItem2);
     307        popup.add(stopItem2);
     308        popup.add(cancelItem);
     309       
     310        _jrestartItem = restartItem;
     311        _jstopItem = stopItem;
     312        _jcancelItem = cancelItem;
     313
     314        return popup;
     315    }
     316
    174317    /**
    175318     * Update the menu
     
    180323        if (_restartItem != null)
    181324            _restartItem.setEnabled(!x);
    182         _stopItem.setEnabled(!x);
    183         _cancelItem.setEnabled(x);
     325        if (_stopItem != null)
     326            _stopItem.setEnabled(!x);
     327        if (_cancelItem != null)
     328            _cancelItem.setEnabled(x);
     329        if (_jrestartItem != null)
     330            _jrestartItem.setEnabled(!x);
     331        if (_jstopItem != null)
     332            _jstopItem.setEnabled(!x);
     333        if (_jcancelItem != null)
     334            _jcancelItem.setEnabled(x);
    184335    }
    185336
     
    188339     */
    189340    private void configureDesktopgui(boolean enable) {
    190         String property = "desktopgui.enabled";
     341        String property = Main.PROP_ENABLE;
    191342        String value = Boolean.toString(enable);
    192343        try {
  • apps/desktopgui/src/net/i2p/desktopgui/Main.java

    r390b2d4 rdd50b14  
    1616import net.i2p.router.app.RouterApp;
    1717import net.i2p.util.Log;
     18import net.i2p.util.SystemVersion;
    1819import net.i2p.util.Translate;
    1920import net.i2p.util.I2PProperties.I2PPropertyCallback;
     
    3031    private ClientAppState _state = UNINITIALIZED;
    3132    private TrayManager _trayManager;
     33    public static final String PROP_ENABLE = "desktopgui.enabled";
     34    private static final String PROP_SWING = "desktopgui.swing";
    3235
    3336    /**
     
    6164    private synchronized void startUp() throws Exception {
    6265        final TrayManager trayManager;
     66        boolean useSwing = _appContext.getProperty(PROP_SWING, !SystemVersion.isWindows());
    6367        if (_context != null)
    64             trayManager = new InternalTrayManager(_context, this);
     68            trayManager = new InternalTrayManager(_context, this, useSwing);
    6569        else
    66             trayManager = new ExternalTrayManager(_appContext, this);
     70            trayManager = new ExternalTrayManager(_appContext, this, useSwing);
    6771        trayManager.startManager();
    6872        _trayManager = trayManager;
     
    7377        if (_context != null) {
    7478            _context.addPropertyCallback(new I2PPropertyCallback() {
    75 
    7679                @Override
    7780                public void propertyChanged(String arg0, String arg1) {
     
    8083                    }
    8184                }
    82                
    8385            });
    8486        }
  • apps/desktopgui/src/net/i2p/desktopgui/TrayManager.java

    r390b2d4 rdd50b14  
    22
    33import java.awt.AWTException;
     4import java.awt.Dimension;
     5import java.awt.Font;
    46import java.awt.Image;
    57import java.awt.PopupMenu;
     
    79import java.awt.Toolkit;
    810import java.awt.TrayIcon;
     11import java.awt.event.FocusEvent;
     12import java.awt.event.FocusListener;
    913import java.awt.event.MouseEvent;
    1014import java.awt.event.MouseListener;
    1115import java.net.URL;
     16
     17import javax.swing.JFrame;
     18import javax.swing.JPopupMenu;
     19import javax.swing.event.MenuKeyEvent;
     20import javax.swing.event.MenuKeyListener;
     21import javax.swing.event.PopupMenuEvent;
     22import javax.swing.event.PopupMenuListener;
    1223
    1324import net.i2p.I2PAppContext;
     
    2233    protected final I2PAppContext _appContext;
    2334    protected final Main _main;
     35    protected final boolean _useSwing;
    2436    ///The tray area, or null if unsupported
    2537    protected SystemTray tray;
     
    3042     * Instantiate tray manager.
    3143     */
    32     protected TrayManager(I2PAppContext ctx, Main main) {
     44    protected TrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
    3345        _appContext = ctx;
    3446        _main = main;
     47        _useSwing = useSwing;
    3548    }
    3649   
     
    3851     * Add the tray icon to the system tray and start everything up.
    3952     */
    40     public synchronized void startManager()  throws AWTException {
    41         if(SystemTray.isSupported()) {
    42             // TODO figure out how to get menu to pop up on left-click
    43             // left-click does nothing by default
    44             // MouseListener, MouseEvent, ...
    45             tray = SystemTray.getSystemTray();
    46             // Windows typically has tooltips; Linux (at least Ubuntu) doesn't
    47             String tooltip = SystemVersion.isWindows() ? _t("I2P: Right-click for menu") : null;
    48             trayIcon = new TrayIcon(getTrayImage(), tooltip, getMainMenu());
    49             trayIcon.setImageAutoSize(true); //Resize image to fit the system tray
    50             tray.add(trayIcon);
    51             // 16x16 on Windows, 24x24 on Linux, but that will probably vary
    52             //System.out.println("Tray icon size is " + trayIcon.getSize());
    53             trayIcon.addMouseListener(new MouseListener() {
    54                 public void mouseClicked(MouseEvent m)  { updateMenu(); }
    55                 public void mouseEntered(MouseEvent m)  { updateMenu(); }
    56                 public void mouseExited(MouseEvent m)   { updateMenu(); }
    57                 public void mousePressed(MouseEvent m)  { updateMenu(); }
    58                 public void mouseReleased(MouseEvent m) { updateMenu(); }
    59             });
    60         } else {
     53    public synchronized void startManager() throws AWTException {
     54        if (!SystemTray.isSupported())
    6155            throw new AWTException("SystemTray not supported");
    62         }
     56        tray = SystemTray.getSystemTray();
     57        // Windows typically has tooltips; Linux (at least Ubuntu) doesn't
     58        String tooltip = SystemVersion.isWindows() ? _t("I2P: Right-click for menu") : null;
     59        TrayIcon ti;
     60        if (_useSwing)
     61            ti = getSwingTrayIcon(tooltip);
     62        else
     63            ti = getAWTTrayIcon(tooltip);
     64        ti.setImageAutoSize(true); //Resize image to fit the system tray
     65        tray.add(ti);
     66        trayIcon = ti;
     67    }
     68
     69    private TrayIcon getAWTTrayIcon(String tooltip) throws AWTException {
     70        PopupMenu menu = getMainMenu();
     71        if (!SystemVersion.isWindows())
     72            menu.setFont(new Font("Arial", Font.BOLD, 14));
     73        TrayIcon ti = new TrayIcon(getTrayImage(), tooltip, menu);
     74        ti.addMouseListener(new MouseListener() {
     75            public void mouseClicked(MouseEvent m)  {}
     76            public void mouseEntered(MouseEvent m)  {}
     77            public void mouseExited(MouseEvent m)   {}
     78            public void mousePressed(MouseEvent m)  { updateMenu(); }
     79            public void mouseReleased(MouseEvent m) { updateMenu(); }
     80        });
     81        return ti;
     82    }
     83
     84    private TrayIcon getSwingTrayIcon(String tooltip) throws AWTException {
     85        // A JPopupMenu by itself is hard to get rid of,
     86        // so we hang it off a zero-size, undecorated JFrame.
     87        // http://stackoverflow.com/questions/1498789/jpopupmenu-behavior
     88        // http://stackoverflow.com/questions/2581314/how-do-you-hide-a-swing-popup-when-you-click-somewhere-else
     89        final JFrame frame = new JFrame();
     90        // http://stackoverflow.com/questions/2011601/jframe-without-frame-border-maximum-button-minimum-button-and-frame-icon
     91        frame.setUndecorated(true);
     92        frame.setMinimumSize(new Dimension(0, 0));
     93        frame.setSize(0, 0);
     94        final JPopupMenu menu = getSwingMainMenu();
     95        menu.setFocusable(true);
     96        frame.add(menu);
     97        TrayIcon ti = new TrayIcon(getTrayImage(), tooltip, null);
     98        ti.addMouseListener(new MouseListener() {
     99            public void mouseClicked(MouseEvent e)  {}
     100            public void mouseEntered(MouseEvent e)  {}
     101            public void mouseExited(MouseEvent e)   {}
     102            public void mousePressed(MouseEvent e)  { handle(e); }
     103            public void mouseReleased(MouseEvent e) { handle(e); }
     104            private void handle(MouseEvent e) {
     105                // http://stackoverflow.com/questions/17258250/changing-the-laf-of-a-popupmenu-for-a-trayicon-in-java
     106                // menu visible check is failsafe, for when menu gets cancelled
     107                if (!frame.isVisible() || !menu.isVisible()) {
     108                    frame.setLocation(e.getX(), e.getY());
     109                    frame.setVisible(true);
     110                    menu.show(frame, 0, 0);
     111                }
     112                updateMenu();
     113            }
     114        });
     115        menu.addPopupMenuListener(new PopupMenuListener() {
     116            public void popupMenuCanceled(PopupMenuEvent e)            { frame.setVisible(false); }
     117            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
     118            public void popupMenuWillBecomeVisible(PopupMenuEvent e)   {}
     119        });
     120        // this is to make it go away when we click elsewhere
     121        // doesn't do anything
     122        menu.addFocusListener(new FocusListener() {
     123            public void focusGained(FocusEvent e) {}
     124            public void focusLost(FocusEvent e)   { frame.setVisible(false); }
     125        });
     126        // this is to make it go away when we hit escape
     127        // doesn't do anything
     128        menu.addMenuKeyListener(new MenuKeyListener() {
     129            public void menuKeyPressed(MenuKeyEvent e)  {}
     130            public void menuKeyReleased(MenuKeyEvent e) {}
     131            public void menuKeyTyped(MenuKeyEvent e)    {
     132                if (e.getKeyChar() == (char) 0x1b)
     133                    frame.setVisible(false);
     134            }
     135        });
     136        return ti;
    63137    }
    64138
     
    77151   
    78152    public synchronized void languageChanged() {
    79         if (trayIcon != null)
    80             trayIcon.setPopupMenu(getMainMenu());
     153        if (trayIcon != null) {
     154            if (!_useSwing)
     155                trayIcon.setPopupMenu(getMainMenu());
     156            // else TODO
     157        }
    81158    }
    82159   
     
    86163     */
    87164    protected abstract PopupMenu getMainMenu();
     165   
     166    /**
     167     * Build a popup menu, adding callbacks to the different items.
     168     * @return popup menu
     169     * @since 0.9.26
     170     */
     171    protected abstract JPopupMenu getSwingMainMenu();
    88172   
    89173    /**
Note: See TracChangeset for help on using the changeset viewer.