forked from dorkbox/SystemTray
Refactored out Menu object (from the SystemTray object). Converted
SystemTray into a crossPlatform proxy. WIP submenus
This commit is contained in:
parent
de8d1d8b07
commit
bf3cb8de11
@ -25,6 +25,7 @@ import java.util.Iterator;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
|
|
||||||
@ -34,8 +35,40 @@ import dorkbox.systemTray.util.ImageUtils;
|
|||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
public
|
public
|
||||||
class Menu {
|
class Menu {
|
||||||
|
public static final AtomicInteger MENU_ID_COUNTER = new AtomicInteger();
|
||||||
|
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
protected final java.util.List<MenuEntry> menuEntries = new ArrayList<MenuEntry>();
|
protected final java.util.List<MenuEntry> menuEntries = new ArrayList<MenuEntry>();
|
||||||
|
|
||||||
|
private final SystemTray systemTray;
|
||||||
|
private final Menu parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param systemTray the system tray (which is the object that sits in the system tray)
|
||||||
|
* @param parent the parent of this menu, null if the parent is the system tray
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
Menu(final SystemTray systemTray, final Menu parent) {
|
||||||
|
this.systemTray = systemTray;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the parent menu (of this menu) or null if we are the root menu
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
Menu getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the system tray that this menu is ultimately attached to
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
SystemTray getSystemTray() {
|
||||||
|
return systemTray;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a spacer to the dropdown menu. When menu entries are removed, any menu spacer that ends up at the top/bottom of the drop-down
|
* Adds a spacer to the dropdown menu. When menu entries are removed, any menu spacer that ends up at the top/bottom of the drop-down
|
||||||
* menu, will also be removed. For example:
|
* menu, will also be removed. For example:
|
||||||
@ -71,7 +104,7 @@ class Menu {
|
|||||||
*
|
*
|
||||||
* @param menuText the menu entry text to use to find the menu entry. The first result found is returned
|
* @param menuText the menu entry text to use to find the menu entry. The first result found is returned
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
MenuEntry getMenuEntry(final String menuText) {
|
MenuEntry getMenuEntry(final String menuText) {
|
||||||
if (menuText == null || menuText.isEmpty()) {
|
if (menuText == null || menuText.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@ -95,7 +128,7 @@ class Menu {
|
|||||||
/**
|
/**
|
||||||
* Gets the first menu entry, ignoring status and spacers
|
* Gets the first menu entry, ignoring status and spacers
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
MenuEntry getFirstMenuEntry() {
|
MenuEntry getFirstMenuEntry() {
|
||||||
return getMenuEntry(0);
|
return getMenuEntry(0);
|
||||||
}
|
}
|
||||||
@ -103,7 +136,7 @@ class Menu {
|
|||||||
/**
|
/**
|
||||||
* Gets the last menu entry, ignoring status and spacers
|
* Gets the last menu entry, ignoring status and spacers
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
MenuEntry getLastMenuEntry() {
|
MenuEntry getLastMenuEntry() {
|
||||||
// Must be wrapped in a synchronized block for object visibility
|
// Must be wrapped in a synchronized block for object visibility
|
||||||
synchronized (menuEntries) {
|
synchronized (menuEntries) {
|
||||||
@ -127,7 +160,7 @@ class Menu {
|
|||||||
*
|
*
|
||||||
* @param menuIndex the menu entry index to use to retrieve the menu entry.
|
* @param menuIndex the menu entry index to use to retrieve the menu entry.
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
MenuEntry getMenuEntry(final int menuIndex) {
|
MenuEntry getMenuEntry(final int menuIndex) {
|
||||||
if (menuIndex < 0) {
|
if (menuIndex < 0) {
|
||||||
return null;
|
return null;
|
||||||
@ -162,7 +195,7 @@ class Menu {
|
|||||||
* @param menuText string of the text you want to appear
|
* @param menuText string of the text you want to appear
|
||||||
* @param callback callback that will be executed when this menu entry is clicked
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void addMenuEntry(String menuText, SystemTrayMenuAction callback) {
|
void addMenuEntry(String menuText, SystemTrayMenuAction callback) {
|
||||||
addMenuEntry(menuText, (String) null, callback);
|
addMenuEntry(menuText, (String) null, callback);
|
||||||
}
|
}
|
||||||
@ -174,7 +207,7 @@ class Menu {
|
|||||||
* @param imagePath the image (full path required) to use. If null, no image will be used
|
* @param imagePath the image (full path required) to use. If null, no image will be used
|
||||||
* @param callback callback that will be executed when this menu entry is clicked
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void addMenuEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
void addMenuEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
||||||
if (imagePath == null) {
|
if (imagePath == null) {
|
||||||
addMenuEntry_(menuText, null, callback);
|
addMenuEntry_(menuText, null, callback);
|
||||||
@ -191,7 +224,7 @@ class Menu {
|
|||||||
* @param imageUrl the URL of the image to use. If null, no image will be used
|
* @param imageUrl the URL of the image to use. If null, no image will be used
|
||||||
* @param callback callback that will be executed when this menu entry is clicked
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void addMenuEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
void addMenuEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
||||||
if (imageUrl == null) {
|
if (imageUrl == null) {
|
||||||
addMenuEntry_(menuText, null, callback);
|
addMenuEntry_(menuText, null, callback);
|
||||||
@ -209,7 +242,7 @@ class Menu {
|
|||||||
* @param imageStream the InputStream of the image to use. If null, no image will be used
|
* @param imageStream the InputStream of the image to use. If null, no image will be used
|
||||||
* @param callback callback that will be executed when this menu entry is clicked
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void addMenuEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
void addMenuEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
if (imageStream == null) {
|
if (imageStream == null) {
|
||||||
addMenuEntry_(menuText, null, callback);
|
addMenuEntry_(menuText, null, callback);
|
||||||
@ -226,7 +259,7 @@ class Menu {
|
|||||||
* @param imageStream the InputStream of the image to use. If null, no image will be used
|
* @param imageStream the InputStream of the image to use. If null, no image will be used
|
||||||
* @param callback callback that will be executed when this menu entry is clicked
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void addMenuEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
void addMenuEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
if (imageStream == null) {
|
if (imageStream == null) {
|
||||||
addMenuEntry_(menuText, null, callback);
|
addMenuEntry_(menuText, null, callback);
|
||||||
@ -241,7 +274,7 @@ class Menu {
|
|||||||
*
|
*
|
||||||
* @param menuEntry This is the menu entry to remove
|
* @param menuEntry This is the menu entry to remove
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void removeMenuEntry(final MenuEntry menuEntry) {
|
void removeMenuEntry(final MenuEntry menuEntry) {
|
||||||
if (menuEntry == null) {
|
if (menuEntry == null) {
|
||||||
throw new NullPointerException("No menu entry exists for menuEntry");
|
throw new NullPointerException("No menu entry exists for menuEntry");
|
||||||
@ -311,7 +344,7 @@ class Menu {
|
|||||||
*
|
*
|
||||||
* @param menuText This is the label for the menu entry to remove
|
* @param menuText This is the label for the menu entry to remove
|
||||||
*/
|
*/
|
||||||
public final
|
public
|
||||||
void removeMenuEntry(final String menuText) {
|
void removeMenuEntry(final String menuText) {
|
||||||
// have to wait for the value
|
// have to wait for the value
|
||||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
@ -25,6 +25,12 @@ import java.net.URL;
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
interface MenuEntry {
|
interface MenuEntry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the menu that contains this menu entry
|
||||||
|
*/
|
||||||
|
Menu getParent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the text label that the menu entry has assigned
|
* @return the text label that the menu entry has assigned
|
||||||
*/
|
*/
|
||||||
|
@ -49,7 +49,7 @@ import dorkbox.util.process.ShellProcessBuilder;
|
|||||||
* Factory and base-class for system tray implementations.
|
* Factory and base-class for system tray implementations.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unused", "Duplicates", "DanglingJavadoc", "WeakerAccess"})
|
@SuppressWarnings({"unused", "Duplicates", "DanglingJavadoc", "WeakerAccess"})
|
||||||
public abstract
|
public
|
||||||
class SystemTray extends Menu {
|
class SystemTray extends Menu {
|
||||||
public static final Logger logger = LoggerFactory.getLogger(SystemTray.class);
|
public static final Logger logger = LoggerFactory.getLogger(SystemTray.class);
|
||||||
|
|
||||||
@ -121,6 +121,7 @@ class SystemTray extends Menu {
|
|||||||
|
|
||||||
|
|
||||||
private static volatile SystemTray systemTray = null;
|
private static volatile SystemTray systemTray = null;
|
||||||
|
private static volatile Menu systemTrayMenu = null;
|
||||||
|
|
||||||
public final static boolean isJavaFxLoaded;
|
public final static boolean isJavaFxLoaded;
|
||||||
public final static boolean isSwtLoaded;
|
public final static boolean isSwtLoaded;
|
||||||
@ -157,13 +158,15 @@ class SystemTray extends Menu {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
systemTray = new SystemTray();
|
||||||
|
|
||||||
// no tray in a headless environment
|
// no tray in a headless environment
|
||||||
if (GraphicsEnvironment.isHeadless()) {
|
if (GraphicsEnvironment.isHeadless()) {
|
||||||
logger.error("Cannot use the SystemTray in a headless environment");
|
logger.error("Cannot use the SystemTray in a headless environment");
|
||||||
throw new HeadlessException();
|
throw new HeadlessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<? extends SystemTray> trayType = null;
|
Class<? extends Menu> trayType = null;
|
||||||
|
|
||||||
boolean isKDE = false;
|
boolean isKDE = false;
|
||||||
|
|
||||||
@ -577,10 +580,10 @@ class SystemTray extends Menu {
|
|||||||
if (trayType == null) {
|
if (trayType == null) {
|
||||||
// unsupported tray
|
// unsupported tray
|
||||||
logger.error("Unable to discover what tray implementation to use!");
|
logger.error("Unable to discover what tray implementation to use!");
|
||||||
systemTray = null;
|
systemTrayMenu = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SystemTray systemTray_ = null;
|
Menu menu_ = null;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* appIndicator/gtk require strings (which is the path)
|
* appIndicator/gtk require strings (which is the path)
|
||||||
@ -612,14 +615,14 @@ class SystemTray extends Menu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
systemTray_ = (SystemTray) trayType.getConstructors()[0].newInstance();
|
menu_ = (Menu) trayType.getConstructors()[0].newInstance(systemTray);
|
||||||
|
|
||||||
logger.info("Successfully Loaded: {}", trayType.getSimpleName());
|
logger.info("Successfully Loaded: {}", trayType.getSimpleName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'", e);
|
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
systemTray = systemTray_;
|
systemTrayMenu = menu_;
|
||||||
|
|
||||||
|
|
||||||
// These install a shutdown hook in JavaFX/SWT, so that when the main window is closed -- the system tray is ALSO closed.
|
// These install a shutdown hook in JavaFX/SWT, so that when the main window is closed -- the system tray is ALSO closed.
|
||||||
@ -671,36 +674,82 @@ class SystemTray extends Menu {
|
|||||||
* be granted in order to get the {@code SystemTray} instance. Otherwise this will return null.
|
* be granted in order to get the {@code SystemTray} instance. Otherwise this will return null.
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
SystemTray getSystemTray() {
|
SystemTray get() {
|
||||||
init();
|
init();
|
||||||
return systemTray;
|
return systemTray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected
|
private
|
||||||
SystemTray() {
|
SystemTray() {
|
||||||
|
super(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public abstract
|
public
|
||||||
void shutdown();
|
void shutdown() {
|
||||||
|
final Menu menu = systemTrayMenu;
|
||||||
|
|
||||||
|
if (menu instanceof AppIndicatorTray) {
|
||||||
|
((AppIndicatorTray) menu).shutdown();
|
||||||
|
}
|
||||||
|
else if (menu instanceof GtkSystemTray) {
|
||||||
|
((GtkSystemTray) menu).shutdown();
|
||||||
|
} else {
|
||||||
|
// swing
|
||||||
|
((SwingSystemTray) menu).shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the 'status' string assigned to the system tray
|
* Gets the 'status' string assigned to the system tray
|
||||||
*/
|
*/
|
||||||
public abstract
|
public
|
||||||
String getStatus();
|
String getStatus() {
|
||||||
|
final Menu menu = systemTrayMenu;
|
||||||
|
if (menu instanceof AppIndicatorTray) {
|
||||||
|
return ((AppIndicatorTray) menu).getStatus();
|
||||||
|
}
|
||||||
|
else if (menu instanceof GtkSystemTray) {
|
||||||
|
return ((GtkSystemTray) menu).getStatus();
|
||||||
|
} else {
|
||||||
|
// swing
|
||||||
|
return ((SwingSystemTray) menu).getStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a 'status' string at the first position in the popup menu. This 'status' string appears as a disabled menu entry.
|
* Sets a 'status' string at the first position in the popup menu. This 'status' string appears as a disabled menu entry.
|
||||||
*
|
*
|
||||||
* @param statusText the text you want displayed, null if you want to remove the 'status' string
|
* @param statusText the text you want displayed, null if you want to remove the 'status' string
|
||||||
*/
|
*/
|
||||||
public abstract
|
public
|
||||||
void setStatus(String statusText);
|
void setStatus(String statusText) {
|
||||||
|
final Menu menu = systemTrayMenu;
|
||||||
|
if (menu instanceof AppIndicatorTray) {
|
||||||
|
((AppIndicatorTray) menu).setStatus(statusText);
|
||||||
|
}
|
||||||
|
else if (menu instanceof GtkSystemTray) {
|
||||||
|
((GtkSystemTray) menu).setStatus(statusText);
|
||||||
|
} else {
|
||||||
|
// swing
|
||||||
|
((SwingSystemTray) menu).setStatus(statusText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract
|
protected
|
||||||
void setIcon_(File iconPath);
|
void setIcon_(File iconPath) {
|
||||||
|
final Menu menu = systemTrayMenu;
|
||||||
|
if (menu instanceof AppIndicatorTray) {
|
||||||
|
((AppIndicatorTray) menu).setIcon_(iconPath);
|
||||||
|
}
|
||||||
|
else if (menu instanceof GtkSystemTray) {
|
||||||
|
((GtkSystemTray) menu).setIcon_(iconPath);
|
||||||
|
} else {
|
||||||
|
// swing
|
||||||
|
((SwingSystemTray) menu).setIcon_(iconPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the tray icon used.
|
* Changes the tray icon used.
|
||||||
@ -754,5 +803,147 @@ class SystemTray extends Menu {
|
|||||||
void setIcon(InputStream imageStream) {
|
void setIcon(InputStream imageStream) {
|
||||||
setIcon_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imageStream));
|
setIcon_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imageStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the parent menu (of this menu) or null if we are the root menu
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
Menu getParent() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a spacer to the dropdown menu. When menu entries are removed, any menu spacer that ends up at the top/bottom of the drop-down
|
||||||
|
* menu, will also be removed. For example:
|
||||||
|
*
|
||||||
|
* Original Entry3 deleted Result
|
||||||
|
*
|
||||||
|
* <Status> <Status> <Status>
|
||||||
|
* Entry1 Entry1 Entry1
|
||||||
|
* Entry2 -> Entry2 -> Entry2
|
||||||
|
* <Spacer> (deleted)
|
||||||
|
* Entry3 (deleted)
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
void addMenuSpacer() {
|
||||||
|
systemTrayMenu.addMenuSpacer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the menu entry for a specified text
|
||||||
|
*
|
||||||
|
* @param menuText the menu entry text to use to find the menu entry. The first result found is returned
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
MenuEntry getMenuEntry(final String menuText) {
|
||||||
|
return systemTrayMenu.getMenuEntry(menuText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first menu entry, ignoring status and spacers
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
MenuEntry getFirstMenuEntry() {
|
||||||
|
return systemTrayMenu.getFirstMenuEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last menu entry, ignoring status and spacers
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
MenuEntry getLastMenuEntry() {
|
||||||
|
return systemTrayMenu.getLastMenuEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the menu entry for a specified index (zero-index), ignoring status and spacers
|
||||||
|
*
|
||||||
|
* @param menuIndex the menu entry index to use to retrieve the menu entry.
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
MenuEntry getMenuEntry(final int menuIndex) {
|
||||||
|
return systemTrayMenu.getMenuEntry(menuIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a menu entry to the tray icon with text (no image)
|
||||||
|
*
|
||||||
|
* @param menuText string of the text you want to appear
|
||||||
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
void addMenuEntry(String menuText, SystemTrayMenuAction callback) {
|
||||||
|
addMenuEntry(menuText, (String) null, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a menu entry to the tray icon with text + image
|
||||||
|
*
|
||||||
|
* @param menuText string of the text you want to appear
|
||||||
|
* @param imagePath the image (full path required) to use. If null, no image will be used
|
||||||
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
void addMenuEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
||||||
|
systemTrayMenu.addMenuEntry(menuText, imagePath, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a menu entry to the tray icon with text + image
|
||||||
|
*
|
||||||
|
* @param menuText string of the text you want to appear
|
||||||
|
* @param imageUrl the URL of the image to use. If null, no image will be used
|
||||||
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
void addMenuEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
||||||
|
systemTrayMenu.addMenuEntry(menuText, imageUrl, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a menu entry to the tray icon with text + image
|
||||||
|
*
|
||||||
|
* @param menuText string of the text you want to appear
|
||||||
|
* @param cacheName @param cacheName the name to use for lookup in the cache for the imageStream
|
||||||
|
* @param imageStream the InputStream of the image to use. If null, no image will be used
|
||||||
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
void addMenuEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
|
systemTrayMenu.addMenuEntry(menuText, cacheName, imageStream, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a menu entry to the tray icon with text + image
|
||||||
|
*
|
||||||
|
* @param menuText string of the text you want to appear
|
||||||
|
* @param imageStream the InputStream of the image to use. If null, no image will be used
|
||||||
|
* @param callback callback that will be executed when this menu entry is clicked
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
void addMenuEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
|
systemTrayMenu.addMenuEntry(menuText, imageStream, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes a menu entry from the dropdown menu.
|
||||||
|
*
|
||||||
|
* @param menuEntry This is the menu entry to remove
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
void removeMenuEntry(final MenuEntry menuEntry) {
|
||||||
|
systemTrayMenu.removeMenuEntry(menuEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes a menu entry (via the text label) from the dropdown menu.
|
||||||
|
*
|
||||||
|
* @param menuText This is the label for the menu entry to remove
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
void removeMenuEntry(final String menuText) {
|
||||||
|
systemTrayMenu.removeMenuEntry(menuText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,10 +19,11 @@ public
|
|||||||
interface SystemTrayMenuAction {
|
interface SystemTrayMenuAction {
|
||||||
/**
|
/**
|
||||||
* This method will ALWAYS be called in the correct context, either in the swing EDT (if it's swing based), or in a separate thread
|
* This method will ALWAYS be called in the correct context, either in the swing EDT (if it's swing based), or in a separate thread
|
||||||
* (GTK/AppIndicator based).
|
* (GtkStatusIcon/AppIndicator based).
|
||||||
*
|
*
|
||||||
* @param systemTray this is the parent, system tray object
|
* @param systemTray this is the parent, system tray object
|
||||||
|
* @param parentMenu this is the parent menu of this menu entry
|
||||||
* @param menuEntry this is the menu entry that was clicked
|
* @param menuEntry this is the menu entry that was clicked
|
||||||
*/
|
*/
|
||||||
void onClick(SystemTray systemTray, final MenuEntry menuEntry);
|
void onClick(SystemTray systemTray, Menu parentMenu, final MenuEntry menuEntry);
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,9 @@ class AppIndicatorTray extends GtkTypeSystemTray {
|
|||||||
private AtomicBoolean shuttingDown = new AtomicBoolean();
|
private AtomicBoolean shuttingDown = new AtomicBoolean();
|
||||||
|
|
||||||
public
|
public
|
||||||
AppIndicatorTray() {
|
AppIndicatorTray(final SystemTray systemTray) {
|
||||||
super();
|
super(systemTray);
|
||||||
|
|
||||||
if (SystemTray.FORCE_TRAY_TYPE == SystemTray.TYPE_GTK_STATUSICON) {
|
if (SystemTray.FORCE_TRAY_TYPE == SystemTray.TYPE_GTK_STATUSICON) {
|
||||||
// if we force GTK type system tray, don't attempt to load AppIndicator libs
|
// if we force GTK type system tray, don't attempt to load AppIndicator libs
|
||||||
throw new IllegalArgumentException("Unable to start AppIndicator if 'SystemTray.FORCE_TRAY_TYPE' is set to GtkStatusIcon");
|
throw new IllegalArgumentException("Unable to start AppIndicator if 'SystemTray.FORCE_TRAY_TYPE' is set to GtkStatusIcon");
|
||||||
@ -103,8 +104,7 @@ class AppIndicatorTray extends GtkTypeSystemTray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public
|
||||||
protected
|
|
||||||
void setIcon_(final File iconFile) {
|
void setIcon_(final File iconFile) {
|
||||||
dispatch(new Runnable() {
|
dispatch(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
201
src/dorkbox/systemTray/linux/GtkMenu.java
Normal file
201
src/dorkbox/systemTray/linux/GtkMenu.java
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.systemTray.linux;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
|
import dorkbox.systemTray.MenuEntry;
|
||||||
|
import dorkbox.systemTray.SystemTray;
|
||||||
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
import dorkbox.systemTray.linux.jna.Gobject;
|
||||||
|
import dorkbox.systemTray.linux.jna.Gtk;
|
||||||
|
|
||||||
|
class GtkMenu extends Menu {
|
||||||
|
volatile Pointer _native;
|
||||||
|
|
||||||
|
// called on dispatch
|
||||||
|
GtkMenu(SystemTray systemTray, GtkMenu parentMenu) {
|
||||||
|
super(systemTray, parentMenu);
|
||||||
|
// this._native = Gtk.gtk_menu_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Necessary to guarantee all updates occur on the dispatch thread
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
Gtk.dispatch(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
void shutdown() {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
obliterateMenu();
|
||||||
|
|
||||||
|
Gtk.shutdownGui();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void addMenuSpacer() {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
deleteMenu();
|
||||||
|
|
||||||
|
GtkMenuEntry menuEntry = new GtkMenuEntrySpacer(GtkMenu.this);
|
||||||
|
menuEntries.add(menuEntry);
|
||||||
|
|
||||||
|
createMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
/**
|
||||||
|
* Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object.
|
||||||
|
*/
|
||||||
|
void deleteMenu() {
|
||||||
|
if (_native != null) {
|
||||||
|
// have to remove all other menu entries
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
for (int i = 0; i < menuEntries.size(); i++) {
|
||||||
|
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
|
||||||
|
|
||||||
|
Gobject.g_object_force_floating(menuEntry__._native);
|
||||||
|
Gtk.gtk_container_remove(_native, menuEntry__._native);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.gtk_widget_destroy(_native);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// makes a new one
|
||||||
|
_native = Gtk.gtk_menu_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
void createMenu() {
|
||||||
|
boolean hasImages = false;
|
||||||
|
|
||||||
|
// now add back other menu entries
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
for (int i = 0; i < menuEntries.size(); i++) {
|
||||||
|
MenuEntry menuEntry__ = menuEntries.get(i);
|
||||||
|
hasImages |= menuEntry__.hasImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < menuEntries.size(); i++) {
|
||||||
|
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
|
||||||
|
// the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images
|
||||||
|
menuEntry__.setSpacerImage(hasImages);
|
||||||
|
|
||||||
|
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
|
||||||
|
Gtk.gtk_menu_shell_append(this._native, menuEntry__._native);
|
||||||
|
Gobject.g_object_ref_sink(menuEntry__._native); // undoes "floating"
|
||||||
|
}
|
||||||
|
|
||||||
|
onMenuAdded(_native);
|
||||||
|
Gtk.gtk_widget_show_all(_native);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called inside the gdk_threads block
|
||||||
|
*/
|
||||||
|
void onMenuAdded(final Pointer menu) {
|
||||||
|
// only needed for AppIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completely obliterates the menu, no possible way to reconstruct it.
|
||||||
|
*/
|
||||||
|
private
|
||||||
|
void obliterateMenu() {
|
||||||
|
if (_native != null) {
|
||||||
|
// have to remove all other menu entries
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
for (int i = 0; i < menuEntries.size(); i++) {
|
||||||
|
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
|
||||||
|
menuEntry__.removePrivate();
|
||||||
|
}
|
||||||
|
menuEntries.clear();
|
||||||
|
|
||||||
|
Gtk.gtk_widget_destroy(_native);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will add a new menu entry, or update one if it already exists
|
||||||
|
*/
|
||||||
|
protected
|
||||||
|
void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
||||||
|
// some implementations of appindicator, do NOT like having a menu added, which has no menu items yet.
|
||||||
|
// see: https://bugs.launchpad.net/glipper/+bug/1203888
|
||||||
|
|
||||||
|
if (menuText == null) {
|
||||||
|
throw new NullPointerException("Menu text cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
MenuEntry menuEntry = getMenuEntry(menuText);
|
||||||
|
if (menuEntry == null) {
|
||||||
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
deleteMenu();
|
||||||
|
|
||||||
|
menuEntry = new GtkMenuEntryItem(GtkMenu.this, menuText, imagePath, callback);
|
||||||
|
menuEntries.add(menuEntry);
|
||||||
|
|
||||||
|
if (menuText.equals("AAAAAAAA")) {
|
||||||
|
// GtkMenu subMenu = new GtkMenu();
|
||||||
|
// subMenu.addMenuEntry("asdasdasd", null, null, null);
|
||||||
|
// Gtk.gtk_menu_item_set_submenu(((GtkMenuEntryItem) menuEntry).nativeMenuItem, subMenu.nativeMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
createMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -21,16 +21,17 @@ import java.net.URL;
|
|||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.linux.jna.Gtk;
|
import dorkbox.systemTray.linux.jna.Gtk;
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
|
|
||||||
abstract
|
abstract
|
||||||
class GtkMenuEntry implements MenuEntry {
|
class GtkMenuEntry implements MenuEntry {
|
||||||
private final int id = GtkTypeSystemTray.MENU_ID_COUNTER.getAndIncrement();
|
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
final Pointer menuItem;
|
private final GtkMenu parentMenu;
|
||||||
final GtkTypeSystemTray systemTray;
|
final Pointer _native;
|
||||||
|
|
||||||
// this have to be volatile, because they can be changed from any thread
|
// this have to be volatile, because they can be changed from any thread
|
||||||
private volatile String text;
|
private volatile String text;
|
||||||
@ -39,9 +40,14 @@ class GtkMenuEntry implements MenuEntry {
|
|||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuEntry(Pointer menuItem, final GtkTypeSystemTray systemTray) {
|
GtkMenuEntry(final GtkMenu parentMenu, final Pointer menuItem) {
|
||||||
this.systemTray = systemTray;
|
this.parentMenu = parentMenu;
|
||||||
this.menuItem = menuItem;
|
this._native = menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu getParent() {
|
||||||
|
return parentMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,16 +148,16 @@ class GtkMenuEntry implements MenuEntry {
|
|||||||
*/
|
*/
|
||||||
public final
|
public final
|
||||||
void remove() {
|
void remove() {
|
||||||
Gtk.gtk_container_remove(systemTray.getMenu(), menuItem);
|
Gtk.gtk_container_remove(parentMenu._native, _native);
|
||||||
Gtk.gtk_menu_shell_deactivate(systemTray.getMenu(), menuItem);
|
Gtk.gtk_menu_shell_deactivate(parentMenu._native, _native);
|
||||||
|
|
||||||
removePrivate();
|
removePrivate();
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(menuItem);
|
Gtk.gtk_widget_destroy(_native);
|
||||||
|
|
||||||
// have to rebuild the menu now...
|
// have to rebuild the menu now...
|
||||||
systemTray.deleteMenu();
|
parentMenu.deleteMenu();
|
||||||
systemTray.createMenu();
|
parentMenu.createMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
// called when this item is removed. Necessary to cleanup/remove itself
|
// called when this item is removed. Necessary to cleanup/remove itself
|
||||||
|
@ -28,6 +28,7 @@ import dorkbox.systemTray.util.ImageUtils;
|
|||||||
|
|
||||||
class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
||||||
private static File transparentIcon = null;
|
private static File transparentIcon = null;
|
||||||
|
|
||||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||||
private final NativeLong nativeLong;
|
private final NativeLong nativeLong;
|
||||||
|
|
||||||
@ -42,8 +43,8 @@ class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
|||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuEntryItem(final String label, final File image, final SystemTrayMenuAction callback, final GtkTypeSystemTray systemTray) {
|
GtkMenuEntryItem(final GtkMenu parentMenu, final String label, final File image, final SystemTrayMenuAction callback) {
|
||||||
super(Gtk.gtk_image_menu_item_new_with_label(""), systemTray);
|
super(parentMenu, Gtk.gtk_image_menu_item_new_with_label(""));
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
setText(label);
|
setText(label);
|
||||||
|
|
||||||
@ -54,9 +55,11 @@ class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
|||||||
setImage_(image);
|
setImage_(image);
|
||||||
|
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
nativeLong = Gobject.g_signal_connect_object(menuItem, "activate", this, null, 0);
|
Gtk.gtk_widget_set_sensitive(_native, Gtk.TRUE);
|
||||||
|
nativeLong = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Gtk.gtk_widget_set_sensitive(_native, Gtk.FALSE);
|
||||||
nativeLong = null;
|
nativeLong = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +76,7 @@ class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
|||||||
int callback(final Pointer instance, final Pointer data) {
|
int callback(final Pointer instance, final Pointer data) {
|
||||||
final SystemTrayMenuAction cb = this.callback;
|
final SystemTrayMenuAction cb = this.callback;
|
||||||
if (cb != null) {
|
if (cb != null) {
|
||||||
Gtk.proxyClick(cb, systemTray, GtkMenuEntryItem.this);
|
Gtk.proxyClick(getParent(), GtkMenuEntryItem.this, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Gtk.TRUE;
|
return Gtk.TRUE;
|
||||||
@ -100,26 +103,26 @@ class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
|||||||
if (image != null) {
|
if (image != null) {
|
||||||
Gtk.gtk_widget_destroy(image);
|
Gtk.gtk_widget_destroy(image);
|
||||||
image = null;
|
image = null;
|
||||||
Gtk.gtk_widget_show_all(menuItem);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (everyoneElseHasImages) {
|
if (everyoneElseHasImages) {
|
||||||
image = Gtk.gtk_image_new_from_file(transparentIcon.getAbsolutePath());
|
image = Gtk.gtk_image_new_from_file(transparentIcon.getAbsolutePath());
|
||||||
Gtk.gtk_image_menu_item_set_image(menuItem, image);
|
Gtk.gtk_image_menu_item_set_image(_native, image);
|
||||||
|
|
||||||
// must always re-set always-show after setting the image
|
// must always re-set always-show after setting the image
|
||||||
Gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE);
|
Gtk.gtk_image_menu_item_set_always_show_image(_native, Gtk.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.gtk_widget_show_all(menuItem);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* must always be called in the GTK thread
|
* must always be called in the GTK thread
|
||||||
*/
|
*/
|
||||||
void renderText(final String text) {
|
void renderText(final String text) {
|
||||||
Gtk.gtk_menu_item_set_label(menuItem, text);
|
Gtk.gtk_menu_item_set_label(_native, text);
|
||||||
Gtk.gtk_widget_show_all(menuItem);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
|
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
|
||||||
@ -135,18 +138,18 @@ class GtkMenuEntryItem extends GtkMenuEntry implements GCallback {
|
|||||||
if (image != null) {
|
if (image != null) {
|
||||||
Gtk.gtk_widget_destroy(image);
|
Gtk.gtk_widget_destroy(image);
|
||||||
image = null;
|
image = null;
|
||||||
Gtk.gtk_widget_show_all(menuItem);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageFile != null) {
|
if (imageFile != null) {
|
||||||
image = Gtk.gtk_image_new_from_file(imageFile.getAbsolutePath());
|
image = Gtk.gtk_image_new_from_file(imageFile.getAbsolutePath());
|
||||||
Gtk.gtk_image_menu_item_set_image(menuItem, image);
|
Gtk.gtk_image_menu_item_set_image(_native, image);
|
||||||
|
|
||||||
// must always re-set always-show after setting the image
|
// must always re-set always-show after setting the image
|
||||||
Gtk.gtk_image_menu_item_set_always_show_image(menuItem, Gtk.TRUE);
|
Gtk.gtk_image_menu_item_set_always_show_image(_native, Gtk.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.gtk_widget_show_all(menuItem);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ class GtkMenuEntrySpacer extends GtkMenuEntry implements MenuSpacer {
|
|||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuEntrySpacer(final GtkTypeSystemTray parent) {
|
GtkMenuEntrySpacer(final GtkMenu parent) {
|
||||||
super(Gtk.gtk_separator_menu_item_new(), parent);
|
super(parent, Gtk.gtk_separator_menu_item_new());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,8 +29,8 @@ class GtkMenuEntryStatus extends GtkMenuEntryItem {
|
|||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuEntryStatus(final String label, final GtkTypeSystemTray parent) {
|
GtkMenuEntryStatus(final GtkMenu parentMenu, final String label) {
|
||||||
super(label, null, null, parent);
|
super(parentMenu, label, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in the GTK thread
|
// called in the GTK thread
|
||||||
@ -39,13 +39,13 @@ class GtkMenuEntryStatus extends GtkMenuEntryItem {
|
|||||||
// evil hacks abound...
|
// evil hacks abound...
|
||||||
// https://developer.gnome.org/pango/stable/PangoMarkupFormat.html
|
// https://developer.gnome.org/pango/stable/PangoMarkupFormat.html
|
||||||
|
|
||||||
Pointer label = Gtk.gtk_bin_get_child(menuItem);
|
Pointer label = Gtk.gtk_bin_get_child(_native);
|
||||||
Gtk.gtk_label_set_use_markup(label, Gtk.TRUE);
|
Gtk.gtk_label_set_use_markup(label, Gtk.TRUE);
|
||||||
Pointer markup = Gobject.g_markup_printf_escaped("<b>%s</b>", text);
|
Pointer markup = Gobject.g_markup_printf_escaped("<b>%s</b>", text);
|
||||||
Gtk.gtk_label_set_markup(label, markup);
|
Gtk.gtk_label_set_markup(label, markup);
|
||||||
Gobject.g_free(markup);
|
Gobject.g_free(markup);
|
||||||
|
|
||||||
Gtk.gtk_widget_set_sensitive(menuItem, Gtk.FALSE);
|
Gtk.gtk_widget_set_sensitive(_native, Gtk.FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,8 +51,8 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
private volatile boolean isActive = false;
|
private volatile boolean isActive = false;
|
||||||
|
|
||||||
public
|
public
|
||||||
GtkSystemTray() {
|
GtkSystemTray(final SystemTray systemTray) {
|
||||||
super();
|
super(systemTray);
|
||||||
if (SystemTray.FORCE_TRAY_TYPE == SystemTray.TYPE_APP_INDICATOR) {
|
if (SystemTray.FORCE_TRAY_TYPE == SystemTray.TYPE_APP_INDICATOR) {
|
||||||
// if we force GTK type system tray, don't attempt to load AppIndicator libs
|
// if we force GTK type system tray, don't attempt to load AppIndicator libs
|
||||||
throw new IllegalArgumentException("Unable to start GtkStatusIcon if 'SystemTray.FORCE_TRAY_TYPE' is set to AppIndicator");
|
throw new IllegalArgumentException("Unable to start GtkStatusIcon if 'SystemTray.FORCE_TRAY_TYPE' is set to AppIndicator");
|
||||||
@ -74,7 +74,7 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
void callback(Pointer notUsed, final GdkEventButton event) {
|
void callback(Pointer notUsed, final GdkEventButton event) {
|
||||||
// BUTTON_PRESS only (any mouse click)
|
// BUTTON_PRESS only (any mouse click)
|
||||||
if (event.type == 4) {
|
if (event.type == 4) {
|
||||||
Gtk.gtk_menu_popup(getMenu(), null, null, Gtk.gtk_status_icon_position_menu, trayIcon, 0, event.time);
|
Gtk.gtk_menu_popup(_native, null, null, Gtk.gtk_status_icon_position_menu, trayIcon, 0, event.time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -140,8 +140,7 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public
|
||||||
protected
|
|
||||||
void setIcon_(final File iconFile) {
|
void setIcon_(final File iconFile) {
|
||||||
dispatch(new Runnable() {
|
dispatch(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,48 +16,20 @@
|
|||||||
|
|
||||||
package dorkbox.systemTray.linux;
|
package dorkbox.systemTray.linux;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
|
||||||
import dorkbox.systemTray.linux.jna.Gobject;
|
|
||||||
import dorkbox.systemTray.linux.jna.Gtk;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derived from
|
* Derived from
|
||||||
* Lantern: https://github.com/getlantern/lantern/ Apache 2.0 License Copyright 2010 Brave New Software Project, Inc.
|
* Lantern: https://github.com/getlantern/lantern/ Apache 2.0 License Copyright 2010 Brave New Software Project, Inc.
|
||||||
*/
|
*/
|
||||||
public abstract
|
abstract
|
||||||
class GtkTypeSystemTray extends SystemTray {
|
class GtkTypeSystemTray extends GtkMenu {
|
||||||
static final AtomicInteger MENU_ID_COUNTER = new AtomicInteger();
|
|
||||||
|
|
||||||
private volatile Pointer menu;
|
GtkTypeSystemTray(final SystemTray systemTray) {
|
||||||
|
super(systemTray, null);
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void dispatch(final Runnable runnable) {
|
|
||||||
Gtk.dispatch(runnable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void shutdown() {
|
|
||||||
Gtk.dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
obliterateMenu();
|
|
||||||
|
|
||||||
Gtk.shutdownGui();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
public
|
||||||
String getStatus() {
|
String getStatus() {
|
||||||
synchronized (menuEntries) {
|
synchronized (menuEntries) {
|
||||||
@ -70,10 +42,9 @@ class GtkTypeSystemTray extends SystemTray {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
public
|
||||||
void setStatus(final String statusText) {
|
void setStatus(final String statusText) {
|
||||||
Gtk.dispatch(new Runnable() {
|
dispatch(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
@ -96,13 +67,13 @@ class GtkTypeSystemTray extends SystemTray {
|
|||||||
deleteMenu();
|
deleteMenu();
|
||||||
|
|
||||||
if (menuEntry == null) {
|
if (menuEntry == null) {
|
||||||
menuEntry = new GtkMenuEntryStatus(statusText, GtkTypeSystemTray.this);
|
menuEntry = new GtkMenuEntryStatus( GtkTypeSystemTray.this, statusText);
|
||||||
// status is ALWAYS at 0 index...
|
// status is ALWAYS at 0 index...
|
||||||
menuEntries.add(0, menuEntry);
|
menuEntries.add(0, menuEntry);
|
||||||
} else if (menuEntry instanceof GtkMenuEntryStatus) {
|
} else if (menuEntry instanceof GtkMenuEntryStatus) {
|
||||||
// change the text?
|
// change the text?
|
||||||
if (statusText != null) {
|
if (statusText != null) {
|
||||||
menuEntry = new GtkMenuEntryStatus(statusText, GtkTypeSystemTray.this);
|
menuEntry = new GtkMenuEntryStatus( GtkTypeSystemTray.this, statusText);
|
||||||
menuEntries.add(0, menuEntry);
|
menuEntries.add(0, menuEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,140 +83,4 @@ class GtkTypeSystemTray extends SystemTray {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void addMenuSpacer() {
|
|
||||||
Gtk.dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
deleteMenu();
|
|
||||||
|
|
||||||
GtkMenuEntry menuEntry = new GtkMenuEntrySpacer(GtkTypeSystemTray.this);
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
|
|
||||||
createMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
/**
|
|
||||||
* Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object.
|
|
||||||
*/
|
|
||||||
void deleteMenu() {
|
|
||||||
if (menu != null) {
|
|
||||||
// have to remove all other menu entries
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
for (int i = 0; i < menuEntries.size(); i++) {
|
|
||||||
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
|
|
||||||
|
|
||||||
Gobject.g_object_force_floating(menuEntry__.menuItem);
|
|
||||||
Gtk.gtk_container_remove(menu, menuEntry__.menuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes a new one
|
|
||||||
menu = Gtk.gtk_menu_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
void createMenu() {
|
|
||||||
boolean hasImages = false;
|
|
||||||
|
|
||||||
// now add back other menu entries
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
for (int i = 0; i < menuEntries.size(); i++) {
|
|
||||||
MenuEntry menuEntry__ = menuEntries.get(i);
|
|
||||||
hasImages |= menuEntry__.hasImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < menuEntries.size(); i++) {
|
|
||||||
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
|
|
||||||
// the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images
|
|
||||||
menuEntry__.setSpacerImage(hasImages);
|
|
||||||
|
|
||||||
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
|
|
||||||
Gtk.gtk_menu_shell_append(this.menu, menuEntry__.menuItem);
|
|
||||||
Gobject.g_object_ref_sink(menuEntry__.menuItem); // undoes "floating"
|
|
||||||
}
|
|
||||||
|
|
||||||
onMenuAdded(menu);
|
|
||||||
Gtk.gtk_widget_show_all(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called inside the gdk_threads block
|
|
||||||
*/
|
|
||||||
void onMenuAdded(final Pointer menu) {
|
|
||||||
// only needed for AppIndicator
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Completely obliterates the menu, no possible way to reconstruct it.
|
|
||||||
*/
|
|
||||||
private
|
|
||||||
void obliterateMenu() {
|
|
||||||
if (menu != null) {
|
|
||||||
// have to remove all other menu entries
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
for (int i = 0; i < menuEntries.size(); i++) {
|
|
||||||
GtkMenuEntry menuEntry__ = (GtkMenuEntry) menuEntries.get(i);
|
|
||||||
menuEntry__.removePrivate();
|
|
||||||
}
|
|
||||||
menuEntries.clear();
|
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected
|
|
||||||
Pointer getMenu() {
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected
|
|
||||||
void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
|
||||||
// some implementations of appindicator, do NOT like having a menu added, which has no menu items yet.
|
|
||||||
// see: https://bugs.launchpad.net/glipper/+bug/1203888
|
|
||||||
|
|
||||||
if (menuText == null) {
|
|
||||||
throw new NullPointerException("Menu text cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = getMenuEntry(menuText);
|
|
||||||
if (menuEntry == null) {
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
deleteMenu();
|
|
||||||
|
|
||||||
menuEntry = new GtkMenuEntryItem(menuText, imagePath, callback, GtkTypeSystemTray.this);
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
|
|
||||||
createMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,10 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import com.sun.jna.Function;
|
import com.sun.jna.Function;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
import dorkbox.systemTray.linux.GtkTypeSystemTray;
|
|
||||||
import dorkbox.systemTray.util.JavaFX;
|
import dorkbox.systemTray.util.JavaFX;
|
||||||
import dorkbox.systemTray.util.Swt;
|
import dorkbox.systemTray.util.Swt;
|
||||||
|
|
||||||
@ -383,11 +383,11 @@ class Gtk {
|
|||||||
* @param callback will never be null.
|
* @param callback will never be null.
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
void proxyClick(final SystemTrayMenuAction callback, final GtkTypeSystemTray parent, final MenuEntry menuEntry) {
|
void proxyClick(final Menu parentMenu, final MenuEntry menuEntry, final SystemTrayMenuAction callback) {
|
||||||
Gtk.isDispatch = true;
|
Gtk.isDispatch = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
callback.onClick(parent, menuEntry);
|
callback.onClick(parentMenu.getSystemTray(), parentMenu, menuEntry);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
SystemTray.logger.error("Error calling menu entry {} click event.", menuEntry.getText(), throwable);
|
SystemTray.logger.error("Error calling menu entry {} click event.", menuEntry.getText(), throwable);
|
||||||
}
|
}
|
||||||
@ -419,11 +419,10 @@ class Gtk {
|
|||||||
private static native void gtk_main_quit();
|
private static native void gtk_main_quit();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static native Pointer gtk_menu_new();
|
public static native Pointer gtk_menu_new();
|
||||||
|
public static native Pointer gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu);
|
||||||
|
|
||||||
|
|
||||||
public static native Pointer gtk_menu_item_new_with_label(String label);
|
|
||||||
|
|
||||||
public static native Pointer gtk_separator_menu_item_new();
|
public static native Pointer gtk_separator_menu_item_new();
|
||||||
|
|
||||||
|
32
src/dorkbox/systemTray/swing/AdjustedJMenuItem.java
Normal file
32
src/dorkbox/systemTray/swing/AdjustedJMenuItem.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
|
import java.awt.Insets;
|
||||||
|
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
|
class AdjustedJMenuItem extends JMenuItem {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Insets getMargin() {
|
||||||
|
Insets margin = super.getMargin();
|
||||||
|
if (margin != null) {
|
||||||
|
margin.set(2, -2, 2, 4);
|
||||||
|
}
|
||||||
|
return margin;
|
||||||
|
}
|
||||||
|
}
|
105
src/dorkbox/systemTray/swing/SwingMenu.java
Normal file
105
src/dorkbox/systemTray/swing/SwingMenu.java
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
|
import dorkbox.systemTray.MenuEntry;
|
||||||
|
import dorkbox.systemTray.SystemTray;
|
||||||
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
|
public
|
||||||
|
class SwingMenu extends Menu {
|
||||||
|
|
||||||
|
volatile SwingSystemTrayMenuPopup _native;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param systemTray
|
||||||
|
* the system tray (which is the object that sits in the system tray)
|
||||||
|
* @param parent
|
||||||
|
* the parent of this menu, null if the parent is the system tray
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
SwingMenu(final SystemTray systemTray, final Menu parent) {
|
||||||
|
super(systemTray, parent);
|
||||||
|
|
||||||
|
SwingUtil.invokeAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
_native = new SwingSystemTrayMenuPopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
// this will properly check if we are running on the EDT
|
||||||
|
SwingUtil.invokeLater(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void addMenuSpacer() {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
MenuEntry menuEntry = new SwingMenuEntrySpacer(SwingMenu.this);
|
||||||
|
menuEntries.add(menuEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will add a new menu entry, or update one if it already exists
|
||||||
|
*/
|
||||||
|
protected
|
||||||
|
void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
||||||
|
if (menuText == null) {
|
||||||
|
throw new NullPointerException("Menu text cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
MenuEntry menuEntry = getMenuEntry(menuText);
|
||||||
|
|
||||||
|
if (menuEntry != null) {
|
||||||
|
throw new IllegalArgumentException("Menu entry already exists for given label '" + menuText + "'");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// must always be called on the EDT
|
||||||
|
menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback);
|
||||||
|
menuEntry.setText(menuText);
|
||||||
|
menuEntry.setImage(imagePath);
|
||||||
|
|
||||||
|
menuEntries.add(menuEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dorkbox.systemTray.swing;
|
package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -22,26 +21,33 @@ import java.net.URL;
|
|||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
abstract
|
abstract
|
||||||
class SwingMenuEntry implements MenuEntry {
|
class SwingMenuEntry implements MenuEntry {
|
||||||
private final int id = SwingSystemTray.MENU_ID_COUNTER.getAndIncrement();
|
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
final SwingSystemTray systemTray;
|
private final SwingMenu parentMenu;
|
||||||
final JComponent menuItem;
|
final JComponent _native;
|
||||||
|
|
||||||
// this have to be volatile, because they can be changed from any thread
|
// this have to be volatile, because they can be changed from any thread
|
||||||
private volatile String text;
|
private volatile String text;
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntry(JComponent menuItem, final SwingSystemTray systemTray) {
|
SwingMenuEntry(final SwingMenu parentMenu, final JComponent menuItem) {
|
||||||
this.menuItem = menuItem;
|
this.parentMenu = parentMenu;
|
||||||
this.systemTray = systemTray;
|
this._native = menuItem;
|
||||||
|
|
||||||
systemTray.getMenu().add(menuItem);
|
parentMenu._native.add(menuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
Menu getParent() {
|
||||||
|
return parentMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,7 +143,7 @@ class SwingMenuEntry implements MenuEntry {
|
|||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
removePrivate();
|
removePrivate();
|
||||||
systemTray.getMenu().remove(menuItem);
|
parentMenu._native.remove(_native);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dorkbox.systemTray.swing;
|
package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
import java.awt.Insets;
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -27,33 +25,21 @@ import javax.swing.JMenuItem;
|
|||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
public
|
|
||||||
class SwingMenuEntryItem extends SwingMenuEntry {
|
class SwingMenuEntryItem extends SwingMenuEntry {
|
||||||
private static
|
|
||||||
class AdjustedJMenuItem extends JMenuItem {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Insets getMargin() {
|
|
||||||
Insets margin = super.getMargin();
|
|
||||||
if (margin != null) {
|
|
||||||
margin.set(2, -2, 2, 4);
|
|
||||||
}
|
|
||||||
return margin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final ActionListener swingCallback;
|
private final ActionListener swingCallback;
|
||||||
|
|
||||||
private volatile boolean hasLegitIcon = false;
|
private volatile boolean hasLegitIcon = false;
|
||||||
private volatile SystemTrayMenuAction callback;
|
private volatile SystemTrayMenuAction callback;
|
||||||
|
|
||||||
public
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntryItem(final SystemTrayMenuAction callback, final SwingSystemTray systemTray) {
|
SwingMenuEntryItem(final SwingMenu parentMenu, final SystemTrayMenuAction callback) {
|
||||||
super(new AdjustedJMenuItem(), systemTray);
|
super(parentMenu, new AdjustedJMenuItem());
|
||||||
|
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
|
|
||||||
|
if (callback != null) {
|
||||||
|
_native.setEnabled(true);
|
||||||
swingCallback = new ActionListener() {
|
swingCallback = new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
@ -63,7 +49,11 @@ class SwingMenuEntryItem extends SwingMenuEntry {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
((JMenuItem) menuItem).addActionListener(swingCallback);
|
((JMenuItem) _native).addActionListener(swingCallback);
|
||||||
|
} else {
|
||||||
|
_native.setEnabled(false);
|
||||||
|
swingCallback = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,7 +65,7 @@ class SwingMenuEntryItem extends SwingMenuEntry {
|
|||||||
private
|
private
|
||||||
void handle() {
|
void handle() {
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onClick(systemTray, this);
|
callback.onClick(getParent().getSystemTray(), getParent(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,13 +77,13 @@ class SwingMenuEntryItem extends SwingMenuEntry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void removePrivate() {
|
void removePrivate() {
|
||||||
((JMenuItem) menuItem).removeActionListener(swingCallback);
|
((JMenuItem) _native).removeActionListener(swingCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// always called in the EDT
|
// always called in the EDT
|
||||||
@Override
|
@Override
|
||||||
void renderText(final String text) {
|
void renderText(final String text) {
|
||||||
((JMenuItem) menuItem).setText(text);
|
((JMenuItem) _native).setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,10 +96,10 @@ class SwingMenuEntryItem extends SwingMenuEntry {
|
|||||||
void run() {
|
void run() {
|
||||||
if (imageFile != null) {
|
if (imageFile != null) {
|
||||||
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
|
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
|
||||||
((JMenuItem) menuItem).setIcon(origIcon);
|
((JMenuItem) _native).setIcon(origIcon);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
((JMenuItem) menuItem).setIcon(null);
|
((JMenuItem) _native).setIcon(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -25,8 +25,8 @@ import dorkbox.systemTray.SystemTrayMenuAction;
|
|||||||
class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer {
|
class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer {
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntrySpacer(final SwingSystemTray systemTray) {
|
SwingMenuEntrySpacer(final SwingMenu parentMenu) {
|
||||||
super(new JSeparator(JSeparator.HORIZONTAL), systemTray);
|
super(parentMenu, new JSeparator(JSeparator.HORIZONTAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in the EDT thread
|
// called in the EDT thread
|
||||||
@ -51,6 +51,5 @@ class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setCallback(final SystemTrayMenuAction callback) {
|
void setCallback(final SystemTrayMenuAction callback) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,20 +26,20 @@ import dorkbox.systemTray.SystemTrayMenuAction;
|
|||||||
class SwingMenuEntryStatus extends SwingMenuEntry implements MenuStatus {
|
class SwingMenuEntryStatus extends SwingMenuEntry implements MenuStatus {
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntryStatus(final String label, final SwingSystemTray systemTray) {
|
SwingMenuEntryStatus(final SwingMenu parentMenu, final String label) {
|
||||||
super(new JMenuItem(), systemTray);
|
super(parentMenu, new JMenuItem());
|
||||||
setText(label);
|
setText(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in the EDT thread
|
// called in the EDT thread
|
||||||
@Override
|
@Override
|
||||||
void renderText(final String text) {
|
void renderText(final String text) {
|
||||||
((JMenuItem) menuItem).setText(text);
|
((JMenuItem) _native).setText(text);
|
||||||
Font font = menuItem.getFont();
|
Font font = _native.getFont();
|
||||||
Font font1 = font.deriveFont(Font.BOLD);
|
Font font1 = font.deriveFont(Font.BOLD);
|
||||||
menuItem.setFont(font1);
|
_native.setFont(font1);
|
||||||
|
|
||||||
menuItem.setEnabled(false);
|
_native.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,12 +25,10 @@ import java.awt.TrayIcon;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
import dorkbox.util.ScreenUtil;
|
import dorkbox.util.ScreenUtil;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
@ -45,11 +43,7 @@ import dorkbox.util.SwingUtil;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
|
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
|
||||||
public
|
public
|
||||||
class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
class SwingSystemTray extends SwingMenu {
|
||||||
static final AtomicInteger MENU_ID_COUNTER = new AtomicInteger();
|
|
||||||
|
|
||||||
volatile SwingSystemTrayMenuPopup menu;
|
|
||||||
|
|
||||||
volatile SystemTray tray;
|
volatile SystemTray tray;
|
||||||
volatile TrayIcon trayIcon;
|
volatile TrayIcon trayIcon;
|
||||||
|
|
||||||
@ -59,8 +53,8 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
* Creates a new system tray handler class.
|
* Creates a new system tray handler class.
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
SwingSystemTray() {
|
SwingSystemTray(final dorkbox.systemTray.SystemTray systemTray) {
|
||||||
super();
|
super(systemTray, null);
|
||||||
|
|
||||||
ImageUtils.determineIconSize(dorkbox.systemTray.SystemTray.TYPE_SWING);
|
ImageUtils.determineIconSize(dorkbox.systemTray.SystemTray.TYPE_SWING);
|
||||||
|
|
||||||
@ -69,15 +63,10 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
SwingSystemTray.this.tray = SystemTray.getSystemTray();
|
SwingSystemTray.this.tray = SystemTray.getSystemTray();
|
||||||
menu = new SwingSystemTrayMenuPopup();
|
|
||||||
if (SwingSystemTray.this.tray == null) {
|
|
||||||
logger.error("The system tray is not available");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
public
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
SwingUtil.invokeAndWait(new Runnable() {
|
SwingUtil.invokeAndWait(new Runnable() {
|
||||||
@ -96,12 +85,6 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected
|
|
||||||
SwingSystemTrayMenuPopup getMenu() {
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
public
|
||||||
String getStatus() {
|
String getStatus() {
|
||||||
synchronized (menuEntries) {
|
synchronized (menuEntries) {
|
||||||
@ -114,13 +97,6 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected
|
|
||||||
void dispatch(final Runnable runnable) {
|
|
||||||
// this will properly check if we are running on the EDT
|
|
||||||
SwingUtil.invokeLater(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
public
|
||||||
void setStatus(final String statusText) {
|
void setStatus(final String statusText) {
|
||||||
dispatch(new Runnable() {
|
dispatch(new Runnable() {
|
||||||
@ -148,7 +124,7 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// create a new one
|
// create a new one
|
||||||
menuEntry = new SwingMenuEntryStatus(statusText, SwingSystemTray.this);
|
menuEntry = new SwingMenuEntryStatus(SwingSystemTray.this, statusText);
|
||||||
// status is ALWAYS at 0 index...
|
// status is ALWAYS at 0 index...
|
||||||
menuEntries.add(0, menuEntry);
|
menuEntries.add(0, menuEntry);
|
||||||
}
|
}
|
||||||
@ -157,27 +133,7 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
public
|
||||||
void addMenuSpacer() {
|
|
||||||
dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = new SwingMenuEntrySpacer(SwingSystemTray.this);
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void setIcon_(final File iconFile) {
|
void setIcon_(final File iconFile) {
|
||||||
dispatch(new Runnable() {
|
dispatch(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -201,7 +157,7 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void mousePressed(MouseEvent e) {
|
void mousePressed(MouseEvent e) {
|
||||||
Dimension size = menu.getPreferredSize();
|
Dimension size = _native.getPreferredSize();
|
||||||
|
|
||||||
Point point = e.getPoint();
|
Point point = e.getPoint();
|
||||||
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
||||||
@ -229,18 +185,18 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
|
|
||||||
// voodoo to get this to popup to have the correct parent
|
// voodoo to get this to popup to have the correct parent
|
||||||
// from: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6285881
|
// from: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6285881
|
||||||
menu.setInvoker(menu);
|
_native.setInvoker(_native);
|
||||||
menu.setLocation(x, y);
|
_native.setLocation(x, y);
|
||||||
menu.setVisible(true);
|
_native.setVisible(true);
|
||||||
menu.setFocusable(true);
|
_native.setFocusable(true);
|
||||||
menu.requestFocusInWindow();
|
_native.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SwingSystemTray.this.tray.add(trayIcon);
|
SwingSystemTray.this.tray.add(trayIcon);
|
||||||
} catch (AWTException e) {
|
} catch (AWTException e) {
|
||||||
logger.error("TrayIcon could not be added.", e);
|
dorkbox.systemTray.SystemTray.logger.error("TrayIcon could not be added.", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trayIcon.setImage(trayImage);
|
trayIcon.setImage(trayImage);
|
||||||
@ -248,36 +204,4 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Will add a new menu entry, or update one if it already exists
|
|
||||||
*/
|
|
||||||
protected
|
|
||||||
void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
|
||||||
if (menuText == null) {
|
|
||||||
throw new NullPointerException("Menu text cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = getMenuEntry(menuText);
|
|
||||||
|
|
||||||
if (menuEntry != null) {
|
|
||||||
throw new IllegalArgumentException("Menu entry already exists for given label '" + menuText + "'");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// must always be called on the EDT
|
|
||||||
menuEntry = new SwingMenuEntryItem(callback, SwingSystemTray.this);
|
|
||||||
menuEntry.setText(menuText);
|
|
||||||
menuEntry.setImage(imagePath);
|
|
||||||
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -217,6 +217,9 @@ class ImageUtils {
|
|||||||
return newFile;
|
return newFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure the directory exists
|
||||||
|
newFile.getParentFile().mkdirs();
|
||||||
|
|
||||||
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D g2d = image.createGraphics();
|
Graphics2D g2d = image.createGraphics();
|
||||||
g2d.setColor(new Color(0,0,0,0));
|
g2d.setColor(new Color(0,0,0,0));
|
||||||
|
@ -18,6 +18,7 @@ package dorkbox;
|
|||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
@ -44,7 +45,7 @@ class TestTray {
|
|||||||
|
|
||||||
public
|
public
|
||||||
TestTray() {
|
TestTray() {
|
||||||
this.systemTray = SystemTray.getSystemTray();
|
this.systemTray = SystemTray.get();
|
||||||
if (systemTray == null) {
|
if (systemTray == null) {
|
||||||
throw new RuntimeException("Unable to load SystemTray!");
|
throw new RuntimeException("Unable to load SystemTray!");
|
||||||
}
|
}
|
||||||
@ -55,7 +56,7 @@ class TestTray {
|
|||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.setStatus("Some Mail!");
|
systemTray.setStatus("Some Mail!");
|
||||||
systemTray.setIcon(GREEN_MAIL);
|
systemTray.setIcon(GREEN_MAIL);
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ class TestTray {
|
|||||||
callbackGray = new SystemTrayMenuAction() {
|
callbackGray = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.setStatus(null);
|
systemTray.setStatus(null);
|
||||||
systemTray.setIcon(BLACK_MAIL);
|
systemTray.setIcon(BLACK_MAIL);
|
||||||
|
|
||||||
@ -82,11 +83,12 @@ class TestTray {
|
|||||||
|
|
||||||
this.systemTray.addMenuEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
this.systemTray.addMenuEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
||||||
this.systemTray.addMenuSpacer();
|
this.systemTray.addMenuSpacer();
|
||||||
|
this.systemTray.addMenuEntry("AAAAAAAA", LT_GRAY_MAIL, null);
|
||||||
|
|
||||||
systemTray.addMenuEntry("Quit", new SystemTrayMenuAction() {
|
systemTray.addMenuEntry("Quit", new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.shutdown();
|
systemTray.shutdown();
|
||||||
//System.exit(0); not necessary if all non-daemon threads have stopped.
|
//System.exit(0); not necessary if all non-daemon threads have stopped.
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package dorkbox;
|
|||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
@ -77,7 +78,7 @@ class TestTrayJavaFX extends Application {
|
|||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
|
|
||||||
|
|
||||||
this.systemTray = SystemTray.getSystemTray();
|
this.systemTray = SystemTray.get();
|
||||||
if (systemTray == null) {
|
if (systemTray == null) {
|
||||||
throw new RuntimeException("Unable to load SystemTray!");
|
throw new RuntimeException("Unable to load SystemTray!");
|
||||||
}
|
}
|
||||||
@ -89,7 +90,7 @@ class TestTrayJavaFX extends Application {
|
|||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.setIcon(GREEN_MAIL);
|
systemTray.setIcon(GREEN_MAIL);
|
||||||
systemTray.setStatus("Some Mail!");
|
systemTray.setStatus("Some Mail!");
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ class TestTrayJavaFX extends Application {
|
|||||||
callbackGray = new SystemTrayMenuAction() {
|
callbackGray = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.setStatus(null);
|
systemTray.setStatus(null);
|
||||||
systemTray.setIcon(BLACK_MAIL);
|
systemTray.setIcon(BLACK_MAIL);
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ class TestTrayJavaFX extends Application {
|
|||||||
systemTray.addMenuEntry("Quit", new SystemTrayMenuAction() {
|
systemTray.addMenuEntry("Quit", new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.shutdown();
|
systemTray.shutdown();
|
||||||
Platform.exit(); // necessary to close javaFx
|
Platform.exit(); // necessary to close javaFx
|
||||||
//System.exit(0); not necessary if all non-daemon threads have stopped.
|
//System.exit(0); not necessary if all non-daemon threads have stopped.
|
||||||
|
@ -23,6 +23,7 @@ import org.eclipse.swt.widgets.Display;
|
|||||||
import org.eclipse.swt.widgets.Shell;
|
import org.eclipse.swt.widgets.Shell;
|
||||||
import org.eclipse.swt.widgets.Text;
|
import org.eclipse.swt.widgets.Text;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
@ -61,7 +62,7 @@ class TestTraySwt {
|
|||||||
helloWorldTest.pack();
|
helloWorldTest.pack();
|
||||||
|
|
||||||
|
|
||||||
this.systemTray = SystemTray.getSystemTray();
|
this.systemTray = SystemTray.get();
|
||||||
if (systemTray == null) {
|
if (systemTray == null) {
|
||||||
throw new RuntimeException("Unable to load SystemTray!");
|
throw new RuntimeException("Unable to load SystemTray!");
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ class TestTraySwt {
|
|||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final dorkbox.systemTray.Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.setStatus("Some Mail!");
|
systemTray.setStatus("Some Mail!");
|
||||||
systemTray.setIcon(GREEN_MAIL);
|
systemTray.setIcon(GREEN_MAIL);
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ class TestTraySwt {
|
|||||||
callbackGray = new SystemTrayMenuAction() {
|
callbackGray = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.setStatus(null);
|
systemTray.setStatus(null);
|
||||||
systemTray.setIcon(BLACK_MAIL);
|
systemTray.setIcon(BLACK_MAIL);
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ class TestTraySwt {
|
|||||||
systemTray.addMenuEntry("Quit", new SystemTrayMenuAction() {
|
systemTray.addMenuEntry("Quit", new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parentMenu, final MenuEntry menuEntry) {
|
||||||
systemTray.shutdown();
|
systemTray.shutdown();
|
||||||
|
|
||||||
display.asyncExec(new Runnable() {
|
display.asyncExec(new Runnable() {
|
||||||
|
Loading…
Reference in New Issue
Block a user