Skip to content
This repository has been archived by the owner on Dec 10, 2018. It is now read-only.

Commit

Permalink
Merge pull request #125 from tresf/1.9
Browse files Browse the repository at this point in the history
Add classic menu support for Apple
  • Loading branch information
tresf committed Dec 26, 2015
2 parents 2fef8bb + c412a27 commit a56d16e
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 28 deletions.
4 changes: 0 additions & 4 deletions src/org/jdesktop/swinghelper/tray/JXTrayIcon.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ public void popupMenuCanceled(PopupMenuEvent e) {
public JXTrayIcon(Image image) {
super(image);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
showJPopupMenu(e);
}

public void mouseReleased(MouseEvent e) {
showJPopupMenu(e);
}
Expand Down
17 changes: 13 additions & 4 deletions src/qz/common/TrayManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import qz.deploy.WindowsDeploy;
import qz.ui.*;
import qz.deploy.LinuxCertificate;
import qz.ui.tray.ClassicTrayIcon;
import qz.ui.tray.ModernTrayIcon;
import qz.utils.FileUtilities;
import qz.utils.MacUtilities;
import qz.utils.SystemUtilities;
Expand Down Expand Up @@ -106,7 +108,14 @@ public TrayManager() {

if (SystemTray.isSupported()) {
// Accommodate some OS-specific tray bugs
tray = SystemUtilities.isLinux() ? new ModernTrayIcon() : new JXTrayIcon(new ImageIcon(new byte[1]).getImage());
Image blank = new ImageIcon(new byte[1]).getImage();
if (SystemUtilities.isWindows()) {
tray = new JXTrayIcon(blank);
} else if (SystemUtilities.isMac()) {
tray = new ClassicTrayIcon(blank);
} else {
tray = new ModernTrayIcon(blank);
}

// Iterates over all images denoted by IconCache.getTypes() and caches them
iconCache = new IconCache(tray.getSize());
Expand Down Expand Up @@ -347,15 +356,15 @@ public void actionPerformed(ActionEvent e) {
if (confirmDialog.prompt("Exit " + name + "?")) { exit(0); }
}
};

public void exit(int returnCode) {
for (Handler h : trayLogger.getHandlers()) {
trayLogger.removeHandler(h);
}
prefs.save();
System.exit(returnCode);
}

/**
* Process toggle/checkbox events as they relate to creating shortcuts
*
Expand Down Expand Up @@ -642,4 +651,4 @@ public void singleInstanceCheck(Integer[] ports, Integer ourPortIndex) {
}
}
}
}
}
108 changes: 108 additions & 0 deletions src/qz/ui/tray/AWTMenuWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @author Tres Finocchiaro
*
* Copyright (C) 2015 Tres Finocchiaro, QZ Industries
*
* IMPORTANT: This software is dual-licensed
*
* LGPL 2.1 This is free software. This software and source code are released
* under the "LGPL 2.1 License". A copy of this license should be distributed
* with this software. http://www.gnu.org/licenses/lgpl-2.1.html
*
* QZ INDUSTRIES SOURCE CODE LICENSE This software and source code *may* instead
* be distributed under the "QZ Industries Source Code License", available by
* request ONLY. If source code for this project is to be made proprietary for
* an individual and/or a commercial entity, written permission via a copy of
* the "QZ Industries Source Code License" must be obtained first. If you've
* obtained a copy of the proprietary license, the terms and conditions of the
* license apply only to the licensee identified in the agreement. Only THEN may
* the LGPL 2.1 license be voided.
*
*/

package qz.ui.tray;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

/**
* Wraps a Swing JMenuItem or JSeparator into an AWT item including text, shortcuts and action listeners.
*
* @author Tres Finocchiaro
*/
public class AWTMenuWrapper {
private MenuItem item;

private AWTMenuWrapper(JCheckBoxMenuItem item) {
this.item = new CheckboxMenuItem(item.getText());
wrapState(item);
wrapShortcut(item);
wrapItemListeners(item);
}

private AWTMenuWrapper(JMenuItem item) {
this.item = new MenuItem(item.getText());
wrapShortcut(item);
wrapActionListeners(item);;
}

private AWTMenuWrapper(JMenu menu) {
this.item = new Menu(menu.getText());
wrapShortcut(menu);
}

private AWTMenuWrapper(JSeparator ignore) {
this.item = new MenuItem("-");
}

private MenuItem getMenuItem() {
return item;
}

private void wrapShortcut(JMenuItem item) {
this.item.setShortcut(new MenuShortcut(item.getMnemonic(), false));
}

private void wrapActionListeners(JMenuItem item) {
for (ActionListener l : item.getActionListeners()) {
this.item.addActionListener(l);
}
}

// Special case for CheckboxMenuItem
private void wrapItemListeners(final JMenuItem item) {
for (final ActionListener l : item.getActionListeners()) {
((CheckboxMenuItem)this.item).addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
((JCheckBoxMenuItem)item).setState(((CheckboxMenuItem)AWTMenuWrapper.this.item).getState());
l.actionPerformed(new ActionEvent(item, e.getID(), item.getActionCommand()));
}
});
}
}

private void wrapState(JMenuItem item) {
if (this.item instanceof CheckboxMenuItem && item instanceof JCheckBoxMenuItem) {
((CheckboxMenuItem)this.item).setState(((JCheckBoxMenuItem)item).getState());
}
}

public static MenuItem wrap(Component c) {
if (c instanceof JCheckBoxMenuItem) {
return new AWTMenuWrapper((JCheckBoxMenuItem)c).getMenuItem();
} else if (c instanceof JMenu) {
return new AWTMenuWrapper((JMenu)c).getMenuItem();
} else if (c instanceof JSeparator) {
return new AWTMenuWrapper((JSeparator)c).getMenuItem();
} else if (c instanceof JMenuItem) {
return new AWTMenuWrapper((JMenuItem)c).getMenuItem();
} else {
return new MenuItem("Error");
}
}
}
67 changes: 67 additions & 0 deletions src/qz/ui/tray/ClassicTrayIcon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @author Tres Finocchiaro
*
* Copyright (C) 2013 Tres Finocchiaro, QZ Industries
*
* IMPORTANT: This software is dual-licensed
*
* LGPL 2.1 This is free software. This software and source code are released
* under the "LGPL 2.1 License". A copy of this license should be distributed
* with this software. http://www.gnu.org/licenses/lgpl-2.1.html
*
* QZ INDUSTRIES SOURCE CODE LICENSE This software and source code *may* instead
* be distributed under the "QZ Industries Source Code License", available by
* request ONLY. If source code for this project is to be made proprietary for
* an individual and/or a commercial entity, written permission via a copy of
* the "QZ Industries Source Code License" must be obtained first. If you've
* obtained a copy of the proprietary license, the terms and conditions of the
* license apply only to the licensee identified in the agreement. Only THEN may
* the LGPL 2.1 license be voided.
*
*/

package qz.ui.tray;

import org.jdesktop.swinghelper.tray.JXTrayIcon;
import qz.utils.MacUtilities;
import qz.utils.ShellUtilities;
import qz.utils.SystemUtilities;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.peer.TrayIconPeer;
import java.lang.reflect.Field;

/**
* Wraps a Swing JPopupMenu into an AWT PopupMenu
*
* @author Tres Finocchiaro
*/
public class ClassicTrayIcon extends JXTrayIcon {
public ClassicTrayIcon(Image image) {
super(image);
}

@Override
public void setJPopupMenu(JPopupMenu source) {
final PopupMenu popup = new PopupMenu();
setPopupMenu(popup);
wrapAll(popup, source.getComponents());
}

/**
* Convert an array of Swing menu components to its AWT equivalent
* @param menu PopupMenu to receive new components
* @param components Array of components to recurse over
*/
private static void wrapAll(Menu menu, Component[] components) {
for (Component c : components) {
MenuItem item = AWTMenuWrapper.wrap(c);
menu.add(item);
if (item instanceof Menu) {
wrapAll((Menu)item, ((JMenu)c).getMenuComponents());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@
*
*/

package qz.ui;
package qz.ui.tray;

import org.jdesktop.swinghelper.tray.JXTrayIcon;
import qz.utils.SystemUtilities;

import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
Expand All @@ -43,25 +42,10 @@ public class ModernTrayIcon extends JXTrayIcon {
private JPopupMenu popup;
private int x = 0, y = 0;

public ModernTrayIcon() {
super(new ImageIcon(new byte[1]).getImage());
}

public ModernTrayIcon(Image image) {
super(image);
}

public ModernTrayIcon(Image image, String tooltip) {
super(image);
setToolTip(tooltip);
}

public ModernTrayIcon(Image image, String tooltip, JPopupMenu popup) {
super(image);
setToolTip(tooltip);
setJPopupMenu(popup);
}

@Override
public void setJPopupMenu(final JPopupMenu popup) {
this.popup = popup;
Expand Down Expand Up @@ -100,6 +84,13 @@ public void windowActivated(WindowEvent we) {
}

@Override
public void setImage(Image image) {
super.setImage(image);
if (invisibleFrame != null) {
invisibleFrame.setIconImage(image);
}
}

public JPopupMenu getJPopupMenu() {
return popup;
}
Expand Down
7 changes: 5 additions & 2 deletions src/qz/utils/MacUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
import com.apple.OSXAdapter;
import qz.common.TrayManager;

import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.awt.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Utility class for MacOS specific functions.
Expand Down Expand Up @@ -58,7 +62,7 @@ public static void registerAboutDialog(Dialog aboutDialog) {

/**
* Adds a listener to register the Apple "Quit" to call trayManager.exit(0)
* @param aboutDialog
* @param trayManager
*/
public static void registerQuitHandler(TrayManager trayManager) {
MacUtilities.trayManager = trayManager;
Expand All @@ -69,5 +73,4 @@ public static void registerQuitHandler(TrayManager trayManager) {
e.printStackTrace();
}
}

}
3 changes: 2 additions & 1 deletion src/qz/ws/PrintWebSocketServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
Expand All @@ -56,7 +57,7 @@ public void run() {
});
runServer();
}catch(Exception e){
log.severe("Could not start tray manager");
log.log(Level.SEVERE, "Could not start tray manager", e);
}
}

Expand Down

0 comments on commit a56d16e

Please sign in to comment.