forked from dorkbox/SystemTray
Clean API
This commit is contained in:
parent
68a9e4edd7
commit
53a9110605
@ -24,7 +24,7 @@ import java.net.URL;
|
|||||||
* This represents a common menu-entry, that is cross platform in nature
|
* This represents a common menu-entry, that is cross platform in nature
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
interface MenuEntry {
|
interface Entry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the menu that contains this menu entry
|
* @return the menu that contains this menu entry
|
@ -15,54 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.systemTray;
|
package dorkbox.systemTray;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a cross-platform menu that is displayed by the tray-icon or as a sub-menu
|
* Represents a cross-platform menu that is displayed by the tray-icon or as a sub-menu
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
|
||||||
public abstract
|
|
||||||
class Menu implements MenuEntry {
|
|
||||||
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>();
|
|
||||||
|
|
||||||
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
|
public
|
||||||
Menu(final SystemTray systemTray, final Menu parent) {
|
interface Menu extends Entry {
|
||||||
this.systemTray = systemTray;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the parent menu (of this menu) or null if we are the root menu
|
* @return the parent menu (of this menu) or null if we are the root menu
|
||||||
*/
|
*/
|
||||||
public
|
Menu getParent();
|
||||||
Menu getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the system tray that this menu is ultimately attached to
|
* @return the system tray that this menu is ultimately attached to
|
||||||
*/
|
*/
|
||||||
public
|
SystemTray getSystemTray();
|
||||||
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
|
||||||
@ -76,127 +46,37 @@ class Menu implements MenuEntry {
|
|||||||
* <Spacer> (deleted)
|
* <Spacer> (deleted)
|
||||||
* Entry3 (deleted)
|
* Entry3 (deleted)
|
||||||
*/
|
*/
|
||||||
public abstract
|
|
||||||
void addSeparator();
|
void addSeparator();
|
||||||
|
|
||||||
/*
|
|
||||||
* Will add a new menu entry, or update one if it already exists
|
|
||||||
*/
|
|
||||||
protected abstract
|
|
||||||
MenuEntry addEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Will add a new sub-menu entry, or update one if it already exists
|
|
||||||
*/
|
|
||||||
protected abstract
|
|
||||||
Menu addMenu_(final String menuText, final File imagePath);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Necessary to guarantee all updates occur on the dispatch thread
|
|
||||||
*/
|
|
||||||
protected abstract
|
|
||||||
void dispatch(Runnable runnable);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Necessary to guarantee all updates occur on the dispatch thread
|
|
||||||
*/
|
|
||||||
protected abstract
|
|
||||||
void dispatchAndWait(Runnable runnable);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables, or disables the sub-menu entry.
|
* This removes al menu entries from this menu
|
||||||
*/
|
*/
|
||||||
public abstract
|
void clear();
|
||||||
void setEnabled(final boolean enabled);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the menu entry for a specified text
|
* 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
|
* @param menuText the menu entry text to use to find the menu entry. The first result found is returned
|
||||||
*/
|
*/
|
||||||
public
|
Entry get(final String menuText);
|
||||||
MenuEntry get(final String menuText) {
|
|
||||||
if (menuText == null || menuText.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be wrapped in a synchronized block for object visibility
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
for (MenuEntry entry : menuEntries) {
|
|
||||||
String text = entry.getText();
|
|
||||||
|
|
||||||
// text can be null
|
|
||||||
if (menuText.equals(text)) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the first menu entry or sub-menu, ignoring status and spacers
|
* Gets the first menu entry or sub-menu, ignoring status and spacers
|
||||||
*/
|
*/
|
||||||
public
|
Entry getFirst();
|
||||||
MenuEntry getFirst() {
|
|
||||||
return get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the last menu entry or sub-menu, ignoring status and spacers
|
* Gets the last menu entry or sub-menu, ignoring status and spacers
|
||||||
*/
|
*/
|
||||||
public
|
Entry getLast();
|
||||||
MenuEntry getLast() {
|
|
||||||
// Must be wrapped in a synchronized block for object visibility
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
if (!menuEntries.isEmpty()) {
|
|
||||||
MenuEntry menuEntry = null;
|
|
||||||
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
|
||||||
menuEntry = menuEntries.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(menuEntry instanceof MenuSpacer || menuEntry instanceof MenuStatus)) {
|
|
||||||
return menuEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the menu entry or sub-menu for a specified index (zero-index), ignoring status and spacers
|
* Gets the menu entry or sub-menu for a specified index (zero-index), ignoring status and spacers
|
||||||
*
|
*
|
||||||
* @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
|
Entry get(final int menuIndex);
|
||||||
MenuEntry get(final int menuIndex) {
|
|
||||||
if (menuIndex < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be wrapped in a synchronized block for object visibility
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
if (!menuEntries.isEmpty()) {
|
|
||||||
int count = 0;
|
|
||||||
for (MenuEntry menuEntry : menuEntries) {
|
|
||||||
if (menuEntry instanceof MenuSpacer || menuEntry instanceof MenuStatus) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == menuIndex) {
|
|
||||||
return menuEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -206,10 +86,7 @@ class Menu implements MenuEntry {
|
|||||||
* @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
|
Entry addEntry(String menuText, SystemTrayMenuAction callback);
|
||||||
MenuEntry addEntry(String menuText, SystemTrayMenuAction callback) {
|
|
||||||
return addEntry(menuText, (String) null, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a menu entry with text + image
|
* Adds a menu entry with text + image
|
||||||
@ -218,15 +95,7 @@ class Menu implements MenuEntry {
|
|||||||
* @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
|
Entry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback);
|
||||||
MenuEntry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
|
||||||
if (imagePath == null) {
|
|
||||||
return addEntry_(menuText, null, callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath), callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a menu entry with text + image
|
* Adds a menu entry with text + image
|
||||||
@ -235,15 +104,7 @@ class Menu implements MenuEntry {
|
|||||||
* @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
|
Entry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback);
|
||||||
MenuEntry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
|
||||||
if (imageUrl == null) {
|
|
||||||
return addEntry_(menuText, null, callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl), callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a menu entry with text + image
|
* Adds a menu entry with text + image
|
||||||
@ -253,15 +114,7 @@ class Menu implements MenuEntry {
|
|||||||
* @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
|
Entry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback);
|
||||||
MenuEntry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
|
||||||
if (imageStream == null) {
|
|
||||||
return addEntry_(menuText, null, callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream), callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a menu entry with text + image
|
* Adds a menu entry with text + image
|
||||||
@ -270,15 +123,7 @@ class Menu implements MenuEntry {
|
|||||||
* @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
|
Entry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback);
|
||||||
MenuEntry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
|
||||||
if (imageStream == null) {
|
|
||||||
return addEntry_(menuText, null, callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream), callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -288,10 +133,7 @@ class Menu implements MenuEntry {
|
|||||||
*
|
*
|
||||||
* @param menuText string of the text you want to appear
|
* @param menuText string of the text you want to appear
|
||||||
*/
|
*/
|
||||||
public
|
Menu addMenu(String menuText);
|
||||||
Menu addMenu(String menuText) {
|
|
||||||
return addMenu(menuText, (String) null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a sub-menu entry with text + image
|
* Adds a sub-menu entry with text + image
|
||||||
@ -299,15 +141,7 @@ class Menu implements MenuEntry {
|
|||||||
* @param menuText string of the text you want to appear
|
* @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 imagePath the image (full path required) to use. If null, no image will be used
|
||||||
*/
|
*/
|
||||||
public
|
Menu addMenu(String menuText, String imagePath);
|
||||||
Menu addMenu(String menuText, String imagePath) {
|
|
||||||
if (imagePath == null) {
|
|
||||||
return addMenu_(menuText, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a sub-menu entry with text + image
|
* Adds a sub-menu entry with text + image
|
||||||
@ -315,15 +149,7 @@ class Menu implements MenuEntry {
|
|||||||
* @param menuText string of the text you want to appear
|
* @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 imageUrl the URL of the image to use. If null, no image will be used
|
||||||
*/
|
*/
|
||||||
public
|
Menu addMenu(String menuText, URL imageUrl);
|
||||||
Menu addMenu(String menuText, URL imageUrl) {
|
|
||||||
if (imageUrl == null) {
|
|
||||||
return addMenu_(menuText, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a sub-menu entry with text + image
|
* Adds a sub-menu entry with text + image
|
||||||
@ -332,15 +158,7 @@ class Menu implements MenuEntry {
|
|||||||
* @param cacheName @param cacheName the name to use for lookup in the cache for the imageStream
|
* @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 imageStream the InputStream of the image to use. If null, no image will be used
|
||||||
*/
|
*/
|
||||||
public
|
Menu addMenu(String menuText, String cacheName, InputStream imageStream);
|
||||||
Menu addMenu(String menuText, String cacheName, InputStream imageStream) {
|
|
||||||
if (imageStream == null) {
|
|
||||||
return addMenu_(menuText, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a sub-menu entry with text + image
|
* Adds a sub-menu entry with text + image
|
||||||
@ -348,120 +166,27 @@ class Menu implements MenuEntry {
|
|||||||
* @param menuText string of the text you want to appear
|
* @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 imageStream the InputStream of the image to use. If null, no image will be used
|
||||||
*/
|
*/
|
||||||
public
|
Menu addMenu(String menuText, InputStream imageStream);
|
||||||
Menu addMenu(String menuText, InputStream imageStream) {
|
|
||||||
if (imageStream == null) {
|
|
||||||
return addMenu_(menuText, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This removes a menu entry from the dropdown menu.
|
* This removes a menu entry from the dropdown menu.
|
||||||
*
|
*
|
||||||
* @param menuEntry This is the menu entry to remove
|
* @param entry This is the menu entry to remove
|
||||||
*/
|
*/
|
||||||
public
|
void remove(final Entry entry);
|
||||||
void remove(final MenuEntry menuEntry) {
|
|
||||||
if (menuEntry == null) {
|
|
||||||
throw new NullPointerException("No menu entry exists for menuEntry");
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
remove__(menuEntry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This removes a sub-menu entry from the dropdown menu.
|
* This removes a sub-menu entry from the dropdown menu.
|
||||||
*
|
*
|
||||||
* @param menu This is the menu entry to remove
|
* @param menu This is the menu entry to remove
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("Duplicates")
|
void remove(final Menu menu);
|
||||||
public
|
|
||||||
void remove(final Menu menu) {
|
|
||||||
if (menu == null) {
|
|
||||||
throw new NullPointerException("No sub-menu entry exists for menu");
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
remove__(menu);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected
|
|
||||||
void remove__(final Object menuEntry) {
|
|
||||||
try {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
// null is passed in when a sub-menu is removing itself from us (because they have already called "remove" and have also
|
|
||||||
// removed themselves from the menuEntries)
|
|
||||||
if (menuEntry != null) {
|
|
||||||
for (Iterator<MenuEntry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
|
|
||||||
final MenuEntry entry = iterator.next();
|
|
||||||
if (entry == menuEntry) {
|
|
||||||
iterator.remove();
|
|
||||||
entry.remove();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
|
|
||||||
if (!menuEntries.isEmpty()) {
|
|
||||||
if (menuEntries.get(0) instanceof MenuSpacer) {
|
|
||||||
remove(menuEntries.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
|
|
||||||
if (!menuEntries.isEmpty()) {
|
|
||||||
if (menuEntries.get(menuEntries.size()-1) instanceof MenuSpacer) {
|
|
||||||
remove(menuEntries.get(menuEntries.size() - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
SystemTray.logger.error("Error removing entry from menu.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This removes a menu entry or sub-menu (via the text label) from the dropdown menu.
|
* This removes a menu entry or sub-menu (via the text label) from the dropdown menu.
|
||||||
*
|
*
|
||||||
* @param menuText This is the label for the menu entry or sub-menu to remove
|
* @param menuText This is the label for the menu entry or sub-menu to remove
|
||||||
*/
|
*/
|
||||||
public
|
void remove(final String menuText);
|
||||||
void remove(final String menuText) {
|
|
||||||
dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = get(menuText);
|
|
||||||
|
|
||||||
if (menuEntry != null) {
|
|
||||||
remove(menuEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This removes the sub-menu entry from the dropdown menu.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
void remove() {
|
|
||||||
getParent().remove(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,5 @@ package dorkbox.systemTray;
|
|||||||
* This represents a common menu-spacer entry, that is cross platform in nature
|
* This represents a common menu-spacer entry, that is cross platform in nature
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
interface MenuSpacer {
|
interface Separator {
|
||||||
|
|
||||||
}
|
}
|
@ -20,6 +20,5 @@ package dorkbox.systemTray;
|
|||||||
* This represents a common menu-status entry, that is cross platform in nature
|
* This represents a common menu-status entry, that is cross platform in nature
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
interface MenuStatus {
|
interface Status {
|
||||||
|
|
||||||
}
|
}
|
@ -24,6 +24,7 @@ import java.io.FileReader;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -34,7 +35,6 @@ import dorkbox.systemTray.linux.jna.Gtk;
|
|||||||
import dorkbox.systemTray.swing._AppIndicatorTray;
|
import dorkbox.systemTray.swing._AppIndicatorTray;
|
||||||
import dorkbox.systemTray.swing._GtkStatusIconTray;
|
import dorkbox.systemTray.swing._GtkStatusIconTray;
|
||||||
import dorkbox.systemTray.swing._SwingTray;
|
import dorkbox.systemTray.swing._SwingTray;
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
|
||||||
import dorkbox.systemTray.util.JavaFX;
|
import dorkbox.systemTray.util.JavaFX;
|
||||||
import dorkbox.systemTray.util.Swt;
|
import dorkbox.systemTray.util.Swt;
|
||||||
import dorkbox.systemTray.util.WindowsSystemTraySwing;
|
import dorkbox.systemTray.util.WindowsSystemTraySwing;
|
||||||
@ -51,7 +51,7 @@ import dorkbox.util.process.ShellProcessBuilder;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unused", "Duplicates", "DanglingJavadoc", "WeakerAccess"})
|
@SuppressWarnings({"unused", "Duplicates", "DanglingJavadoc", "WeakerAccess"})
|
||||||
public
|
public
|
||||||
class SystemTray extends Menu {
|
class SystemTray implements Menu {
|
||||||
public static final Logger logger = LoggerFactory.getLogger(SystemTray.class);
|
public static final Logger logger = LoggerFactory.getLogger(SystemTray.class);
|
||||||
|
|
||||||
public static final int TYPE_AUTO_DETECT = 0;
|
public static final int TYPE_AUTO_DETECT = 0;
|
||||||
@ -559,7 +559,7 @@ class SystemTray extends Menu {
|
|||||||
systemTrayMenu = null;
|
systemTrayMenu = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final Menu[] menuReference = new Menu[1];
|
final AtomicReference<Menu> reference = new AtomicReference<Menu>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* appIndicator/gtk require strings (which is the path)
|
* appIndicator/gtk require strings (which is the path)
|
||||||
@ -602,7 +602,7 @@ class SystemTray extends Menu {
|
|||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
try {
|
try {
|
||||||
menuReference[0] = (Menu) finalTrayType.getConstructors()[0].newInstance(systemTray);
|
reference.set((Menu) finalTrayType.getConstructors()[0].newInstance(systemTray));
|
||||||
logger.info("Successfully Loaded: {}", finalTrayType.getSimpleName());
|
logger.info("Successfully Loaded: {}", finalTrayType.getSimpleName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable to create tray type: '" + finalTrayType.getSimpleName() + "'", e);
|
logger.error("Unable to create tray type: '" + finalTrayType.getSimpleName() + "'", e);
|
||||||
@ -613,7 +613,7 @@ class SystemTray extends Menu {
|
|||||||
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'", e);
|
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
systemTrayMenu = menuReference[0];
|
systemTrayMenu = reference.get();
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
||||||
@ -670,13 +670,6 @@ class SystemTray extends Menu {
|
|||||||
return systemTray;
|
return systemTray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private
|
|
||||||
SystemTray() {
|
|
||||||
super(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
public
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
final Menu menu = systemTrayMenu;
|
final Menu menu = systemTrayMenu;
|
||||||
@ -717,6 +710,7 @@ class SystemTray extends Menu {
|
|||||||
public
|
public
|
||||||
void setStatus(String statusText) {
|
void setStatus(String statusText) {
|
||||||
final Menu menu = systemTrayMenu;
|
final Menu menu = systemTrayMenu;
|
||||||
|
|
||||||
if (menu instanceof _AppIndicatorTray) {
|
if (menu instanceof _AppIndicatorTray) {
|
||||||
((_AppIndicatorTray) menu).setStatus(statusText);
|
((_AppIndicatorTray) menu).setStatus(statusText);
|
||||||
}
|
}
|
||||||
@ -728,20 +722,6 @@ class SystemTray extends Menu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected
|
|
||||||
void setImage_(File iconPath) {
|
|
||||||
final Menu menu = systemTrayMenu;
|
|
||||||
if (menu instanceof _AppIndicatorTray) {
|
|
||||||
((_AppIndicatorTray) menu).setImage_(iconPath);
|
|
||||||
}
|
|
||||||
else if (menu instanceof _GtkStatusIconTray) {
|
|
||||||
((_GtkStatusIconTray) menu).setImage_(iconPath);
|
|
||||||
} else {
|
|
||||||
// swing (windows/mac)
|
|
||||||
((_SwingTray) menu).setImage_(iconPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the parent menu (of this menu) or null if we are the root menu
|
* @return the parent menu (of this menu) or null if we are the root menu
|
||||||
*/
|
*/
|
||||||
@ -750,6 +730,12 @@ class SystemTray extends Menu {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
SystemTray getSystemTray() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the attached menu to this system tray
|
* @return the attached menu to this system tray
|
||||||
*/
|
*/
|
||||||
@ -776,107 +762,113 @@ class SystemTray extends Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NO OP.
|
/**
|
||||||
@Override
|
* Does nothing. You cannot set the text for the system tray
|
||||||
protected
|
*/
|
||||||
MenuEntry addEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NO OP.
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
Menu addMenu_(final String menuText, final File imagePath) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NO OP.
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setEnabled(final boolean enabled) {
|
void setEnabled(final boolean enabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing. You cannot get the text for the system tray
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
String getText() {
|
String getText() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// NO OP.
|
/**
|
||||||
|
* Does nothing. You cannot set the text for the system tray
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setText(final String newText) {
|
void setText(final String newText) {
|
||||||
|
// NO OP.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the tray image used.
|
* Specifies the new image to set for a menu entry, NULL to delete the image
|
||||||
*
|
*
|
||||||
* Because the cross-platform, underlying system uses a file path to load images for the system tray,
|
* @param imageFile the file of the image to use or null
|
||||||
* this will directly use the contents of the specified file.
|
|
||||||
*
|
|
||||||
* @param imageFile the path of the image to use
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setImage(final File imageFile) {
|
void setImage(final File imageFile) {
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imageFile));
|
if (imageFile == null) {
|
||||||
|
throw new NullPointerException("imageFile cannot be null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
systemTrayMenu.setImage(imageFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the tray image used.
|
* Specifies the new image to set for a menu entry, NULL to delete the image
|
||||||
*
|
*
|
||||||
* Because the cross-platform, underlying system uses a file path to load images for the system tray,
|
* @param imagePath the full path of the image to use or null
|
||||||
* this will directly use the contents of the specified file.
|
|
||||||
*
|
|
||||||
* @param imagePath the path of the image to use
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setImage(final String imagePath) {
|
void setImage(final String imagePath) {
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imagePath));
|
if (imagePath == null) {
|
||||||
|
throw new NullPointerException("imagePath cannot be null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
systemTrayMenu.setImage(imagePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the tray image used.
|
* Specifies the new image to set for a menu entry, NULL to delete the image
|
||||||
*
|
*
|
||||||
* Because the cross-platform, underlying system uses a file path to load images for the system tray, this will copy the contents of
|
* @param imageUrl the URL of the image to use or null
|
||||||
* the URL to a temporary location on disk, based on the path specified by the URL.
|
|
||||||
*
|
|
||||||
* @param imageUrl the URL of the image to use
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setImage(URL imageUrl) {
|
void setImage(final URL imageUrl) {
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imageUrl));
|
if (imageUrl == null) {
|
||||||
|
throw new NullPointerException("imageUrl cannot be null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
systemTrayMenu.setImage(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the tray image used.
|
* Specifies the new image to set for a menu entry, NULL to delete the image
|
||||||
*
|
|
||||||
* Because the cross-platform, underlying system uses a file path to load images for the system tray, this will copy the contents of
|
|
||||||
* the imageStream to a temporary location on disk, based on the `cacheName` specified.
|
|
||||||
*
|
*
|
||||||
* @param cacheName the name to use for lookup in the cache for the imageStream
|
* @param cacheName the name to use for lookup in the cache for the imageStream
|
||||||
* @param imageStream the InputStream of the image to use
|
* @param imageStream the InputStream of the image to use
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setImage(String cacheName, InputStream imageStream) {
|
void setImage(final String cacheName, final InputStream imageStream) {
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, cacheName, imageStream));
|
if (cacheName == null) {
|
||||||
|
setImage(imageStream);
|
||||||
|
} else {
|
||||||
|
if (imageStream == null) {
|
||||||
|
throw new NullPointerException("imageStream cannot be null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
systemTrayMenu.setImage(cacheName, imageStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the tray image used.
|
* Specifies the new image to set for a menu entry, NULL to delete the image
|
||||||
*
|
*
|
||||||
* Because the cross-platform, underlying system uses a file path to load images for the system tray, this will copy the contents of
|
* This method **DOES NOT CACHE** the result, so multiple lookups for the same inputStream result in new files every time. This is
|
||||||
* the imageStream to a temporary location on disk.
|
* also NOT RECOMMENDED, but is provided for simplicity.
|
||||||
*
|
*
|
||||||
* @param imageStream the InputStream of the image to use
|
* @param imageStream the InputStream of the image to use
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setImage(final InputStream imageStream) {
|
void setImage(final InputStream imageStream) {
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imageStream));
|
if (imageStream == null) {
|
||||||
|
throw new NullPointerException("imageStream cannot be null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
systemTrayMenu.setImage(imageStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -888,33 +880,22 @@ class SystemTray extends Menu {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NO OP.
|
/**
|
||||||
|
* Does nothing. The system tray cannot have a callback when opened
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setCallback(final SystemTrayMenuAction callback) {
|
void setCallback(final SystemTrayMenuAction callback) {
|
||||||
|
// NO OP.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NO OP.
|
/**
|
||||||
|
* Does nothing. The system tray cannot be opened via a shortcut key
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setShortcut(final char key) {
|
void setShortcut(final char key) {
|
||||||
}
|
|
||||||
|
|
||||||
// NO OP.
|
// NO OP.
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void dispatch(final Runnable runnable) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// NO OP.
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void dispatchAndWait(final Runnable runnable) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// NO OP.
|
|
||||||
protected
|
|
||||||
void removePrivate() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -925,14 +906,13 @@ class SystemTray extends Menu {
|
|||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the menu entry for a specified text
|
* 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
|
* @param menuText the menu entry text to use to find the menu entry. The first result found is returned
|
||||||
*/
|
*/
|
||||||
public final
|
public final
|
||||||
MenuEntry get(final String menuText) {
|
Entry get(final String menuText) {
|
||||||
return systemTrayMenu.get(menuText);
|
return systemTrayMenu.get(menuText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -940,7 +920,7 @@ class SystemTray extends Menu {
|
|||||||
* Gets the first menu entry, ignoring status and spacers
|
* Gets the first menu entry, ignoring status and spacers
|
||||||
*/
|
*/
|
||||||
public final
|
public final
|
||||||
MenuEntry getFirst() {
|
Entry getFirst() {
|
||||||
return systemTrayMenu.getFirst();
|
return systemTrayMenu.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,7 +928,7 @@ class SystemTray extends Menu {
|
|||||||
* Gets the last menu entry, ignoring status and spacers
|
* Gets the last menu entry, ignoring status and spacers
|
||||||
*/
|
*/
|
||||||
public final
|
public final
|
||||||
MenuEntry getLast() {
|
Entry getLast() {
|
||||||
return systemTrayMenu.getLast();
|
return systemTrayMenu.getLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,7 +938,7 @@ class SystemTray extends 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 final
|
||||||
MenuEntry get(final int menuIndex) {
|
Entry get(final int menuIndex) {
|
||||||
return systemTrayMenu.get(menuIndex);
|
return systemTrayMenu.get(menuIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,7 +953,7 @@ class SystemTray extends Menu {
|
|||||||
* @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 final
|
||||||
MenuEntry addEntry(String menuText, SystemTrayMenuAction callback) {
|
Entry addEntry(String menuText, SystemTrayMenuAction callback) {
|
||||||
return addEntry(menuText, (String) null, callback);
|
return addEntry(menuText, (String) null, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -985,7 +965,7 @@ class SystemTray extends Menu {
|
|||||||
* @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 final
|
||||||
MenuEntry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
Entry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
||||||
return systemTrayMenu.addEntry(menuText, imagePath, callback);
|
return systemTrayMenu.addEntry(menuText, imagePath, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -997,7 +977,7 @@ class SystemTray extends Menu {
|
|||||||
* @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 final
|
||||||
MenuEntry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
Entry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
||||||
return systemTrayMenu.addEntry(menuText, imageUrl, callback);
|
return systemTrayMenu.addEntry(menuText, imageUrl, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,7 +990,7 @@ class SystemTray extends Menu {
|
|||||||
* @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
|
public
|
||||||
MenuEntry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
Entry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
return systemTrayMenu.addEntry(menuText, cacheName, imageStream, callback);
|
return systemTrayMenu.addEntry(menuText, cacheName, imageStream, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1022,7 +1002,7 @@ class SystemTray extends Menu {
|
|||||||
* @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 final
|
||||||
MenuEntry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
Entry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
return systemTrayMenu.addEntry(menuText, imageStream, callback);
|
return systemTrayMenu.addEntry(menuText, imageStream, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1091,11 +1071,31 @@ class SystemTray extends Menu {
|
|||||||
/**
|
/**
|
||||||
* This removes a menu entry from the dropdown menu.
|
* This removes a menu entry from the dropdown menu.
|
||||||
*
|
*
|
||||||
* @param menuEntry This is the menu entry to remove
|
* @param entry This is the menu entry to remove
|
||||||
*/
|
*/
|
||||||
public final
|
public final
|
||||||
void remove(final MenuEntry menuEntry) {
|
void remove(final Entry entry) {
|
||||||
systemTrayMenu.remove(menuEntry);
|
systemTrayMenu.remove(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes a sub-menu entry from the dropdown menu.
|
||||||
|
*
|
||||||
|
* @param menu This is the menu entry to remove
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void remove(final Menu menu) {
|
||||||
|
systemTrayMenu.remove(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes al menu entries from this menu
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void clear() {
|
||||||
|
systemTrayMenu.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,12 +18,11 @@ package dorkbox.systemTray;
|
|||||||
public
|
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 swing EDT
|
||||||
* (GtkStatusIcon/AppIndicator based).
|
|
||||||
*
|
*
|
||||||
* @param systemTray this is the parent, system tray object
|
* @param systemTray this is the parent, system tray object
|
||||||
* @param parent this is the parent menu of this menu entry
|
* @param parent this is the parent menu of this menu entry
|
||||||
* @param entry this is the menu entry that was clicked
|
* @param entry this is the menu entry that was clicked
|
||||||
*/
|
*/
|
||||||
void onClick(SystemTray systemTray, Menu parent, final MenuEntry entry);
|
void onClick(SystemTray systemTray, Menu parent, final Entry entry);
|
||||||
}
|
}
|
||||||
|
@ -23,24 +23,24 @@ import java.net.URL;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Entry;
|
||||||
import dorkbox.systemTray.Menu;
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
abstract
|
abstract
|
||||||
class Entry implements MenuEntry {
|
class EntryImpl implements Entry {
|
||||||
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
|
private final int id = MenuImpl.MENU_ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
private final SwingMenu parent;
|
private final MenuImpl parent;
|
||||||
final JComponent _native;
|
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.
|
||||||
Entry(final SwingMenu parent, final JComponent menuItem) {
|
EntryImpl(final MenuImpl parent, final JComponent menuItem) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this._native = menuItem;
|
this._native = menuItem;
|
||||||
|
|
||||||
@ -54,11 +54,14 @@ class Entry implements MenuEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* must always be called in the GTK thread
|
* must always be called in the EDT thread
|
||||||
*/
|
*/
|
||||||
abstract
|
abstract
|
||||||
void renderText(final String text);
|
void renderText(final String text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not always called on the EDT thread
|
||||||
|
*/
|
||||||
abstract
|
abstract
|
||||||
void setImage_(final File imageFile);
|
void setImage_(final File imageFile);
|
||||||
|
|
||||||
@ -204,7 +207,7 @@ class Entry implements MenuEntry {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry other = (Entry) obj;
|
EntryImpl other = (EntryImpl) obj;
|
||||||
return this.id == other.id;
|
return this.id == other.id;
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ import javax.swing.JMenuItem;
|
|||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
class EntryItem extends Entry {
|
class EntryItem extends EntryImpl {
|
||||||
|
|
||||||
private final ActionListener swingCallback;
|
private final ActionListener swingCallback;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ class EntryItem extends Entry {
|
|||||||
private volatile SystemTrayMenuAction callback;
|
private volatile SystemTrayMenuAction callback;
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
EntryItem(final SwingMenu parent, final SystemTrayMenuAction callback) {
|
EntryItem(final MenuImpl parent, final SystemTrayMenuAction callback) {
|
||||||
super(parent, new AdjustedJMenuItem());
|
super(parent, new AdjustedJMenuItem());
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
|
@ -19,13 +19,12 @@ import java.io.File;
|
|||||||
|
|
||||||
import javax.swing.JSeparator;
|
import javax.swing.JSeparator;
|
||||||
|
|
||||||
import dorkbox.systemTray.MenuSpacer;
|
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
|
||||||
class EntrySeparator extends Entry implements MenuSpacer {
|
class EntrySeparator extends EntryImpl implements dorkbox.systemTray.Separator {
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
EntrySeparator(final SwingMenu parent) {
|
EntrySeparator(final MenuImpl parent) {
|
||||||
super(parent, new JSeparator(JSeparator.HORIZONTAL));
|
super(parent, new JSeparator(JSeparator.HORIZONTAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,17 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.systemTray.swing;
|
package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
|
import java.awt.Font;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import dorkbox.systemTray.MenuStatus;
|
import dorkbox.systemTray.Status;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
|
||||||
class EntryStatus extends Entry implements MenuStatus {
|
class EntryStatus extends EntryImpl implements Status {
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
EntryStatus(final SwingMenu parent, final String label) {
|
EntryStatus(final MenuImpl parent, final String label) {
|
||||||
super(parent, new JMenuItem());
|
super(parent, new JMenuItem());
|
||||||
setText(label);
|
setText(label);
|
||||||
}
|
}
|
||||||
@ -33,11 +34,12 @@ class EntryStatus extends Entry implements MenuStatus {
|
|||||||
// called in the EDT thread
|
// called in the EDT thread
|
||||||
@Override
|
@Override
|
||||||
void renderText(final String text) {
|
void renderText(final String text) {
|
||||||
// AppIndicator strips out markup text, so we disable bold in order to have all of the menus MOSTLY look the same
|
|
||||||
// https://mail.gnome.org/archives/commits-list/2016-March/msg05444.html
|
|
||||||
|
|
||||||
((JMenuItem) _native).setText(text);
|
((JMenuItem) _native).setText(text);
|
||||||
|
Font font = _native.getFont();
|
||||||
|
Font font1 = font.deriveFont(Font.BOLD);
|
||||||
|
_native.setFont(font1);
|
||||||
|
|
||||||
|
// this makes sure it can't be selected
|
||||||
_native.setEnabled(false);
|
_native.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
720
src/dorkbox/systemTray/swing/MenuImpl.java
Normal file
720
src/dorkbox/systemTray/swing/MenuImpl.java
Normal file
@ -0,0 +1,720 @@
|
|||||||
|
/*
|
||||||
|
* 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 static dorkbox.systemTray.swing.EntryImpl.getVkKey;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Entry;
|
||||||
|
import dorkbox.systemTray.Menu;
|
||||||
|
import dorkbox.systemTray.Status;
|
||||||
|
import dorkbox.systemTray.SystemTray;
|
||||||
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
|
// this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both
|
||||||
|
class MenuImpl implements Menu {
|
||||||
|
public static final AtomicInteger MENU_ID_COUNTER = new AtomicInteger();
|
||||||
|
private final int id = MenuImpl.MENU_ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
|
protected final java.util.List<Entry> menuEntries = new ArrayList<Entry>();
|
||||||
|
|
||||||
|
private final SystemTray systemTray;
|
||||||
|
private final Menu parent;
|
||||||
|
|
||||||
|
// sub-menu = AdjustedJMenu
|
||||||
|
// systemtray = TrayPopup
|
||||||
|
volatile JComponent _native;
|
||||||
|
|
||||||
|
// this have to be volatile, because they can be changed from any thread
|
||||||
|
private volatile String text;
|
||||||
|
private volatile boolean hasLegitIcon = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in the EDT
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* @param _native the native element that represents this menu
|
||||||
|
*/
|
||||||
|
MenuImpl(final SystemTray systemTray, final Menu parent, final JComponent _native) {
|
||||||
|
this.systemTray = systemTray;
|
||||||
|
this.parent = parent;
|
||||||
|
this._native = _native;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected
|
||||||
|
void dispatch(final Runnable runnable) {
|
||||||
|
// this will properly check if we are running on the EDT
|
||||||
|
SwingUtil.invokeLater(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected
|
||||||
|
void dispatchAndWait(final Runnable runnable) {
|
||||||
|
// this will properly check if we are running on the EDT
|
||||||
|
try {
|
||||||
|
SwingUtil.invokeAndWait(runnable);
|
||||||
|
} catch (Exception e) {
|
||||||
|
SystemTray.logger.error("Error processing event on the dispatch thread.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// always called in the EDT
|
||||||
|
private
|
||||||
|
void renderText(final String text) {
|
||||||
|
((JMenuItem) _native).setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will add a new menu entry, or update one if it already exists
|
||||||
|
* NOT ALWAYS CALLED ON EDT
|
||||||
|
*/
|
||||||
|
private
|
||||||
|
Entry addEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
||||||
|
if (menuText == null) {
|
||||||
|
throw new NullPointerException("Menu text cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final AtomicReference<Entry> value = new AtomicReference<Entry>();
|
||||||
|
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
Entry entry = get(menuText);
|
||||||
|
|
||||||
|
if (entry == null) {
|
||||||
|
// must always be called on the EDT
|
||||||
|
entry = new EntryItem(MenuImpl.this, callback);
|
||||||
|
entry.setText(menuText);
|
||||||
|
entry.setImage(imagePath);
|
||||||
|
|
||||||
|
menuEntries.add(entry);
|
||||||
|
} else if (entry instanceof EntryItem) {
|
||||||
|
entry.setText(menuText);
|
||||||
|
entry.setImage(imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
value.set(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return value.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will add a new sub-menu entry, or update one if it already exists
|
||||||
|
* NOT ALWAYS CALLED ON EDT
|
||||||
|
*/
|
||||||
|
private
|
||||||
|
Menu addMenu_(final String menuText, final File imagePath) {
|
||||||
|
if (menuText == null) {
|
||||||
|
throw new NullPointerException("Menu text cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final AtomicReference<Menu> value = new AtomicReference<Menu>();
|
||||||
|
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
Entry entry = get(menuText);
|
||||||
|
|
||||||
|
if (entry == null) {
|
||||||
|
// must always be called on the EDT
|
||||||
|
entry = new MenuImpl(getSystemTray(), MenuImpl.this, new AdjustedJMenu());
|
||||||
|
_native.add(((MenuImpl) entry)._native);
|
||||||
|
|
||||||
|
entry.setText(menuText);
|
||||||
|
entry.setImage(imagePath);
|
||||||
|
value.set((Menu) entry);
|
||||||
|
|
||||||
|
} else if (entry instanceof MenuImpl) {
|
||||||
|
entry.setText(menuText);
|
||||||
|
entry.setImage(imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
menuEntries.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return value.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// public here so that Swing/Gtk/AppIndicator can override this
|
||||||
|
public
|
||||||
|
void setImage_(final File imageFile) {
|
||||||
|
hasLegitIcon = imageFile != null;
|
||||||
|
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
if (imageFile != null) {
|
||||||
|
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
|
||||||
|
((JMenuItem) _native).setIcon(origIcon);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
((JMenuItem) _native).setIcon(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
SystemTray getSystemTray() {
|
||||||
|
return systemTray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
boolean hasImage() {
|
||||||
|
return hasLegitIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables, or disables the sub-menu entry.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setEnabled(final boolean enabled) {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
_native.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOT ALWAYS CALLED ON EDT
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void addSeparator() {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
Entry entry = new EntrySeparator(MenuImpl.this);
|
||||||
|
menuEntries.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry get(final String menuText) {
|
||||||
|
if (menuText == null || menuText.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be wrapped in a synchronized block for object visibility
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
for (Entry entry : menuEntries) {
|
||||||
|
String text = entry.getText();
|
||||||
|
|
||||||
|
// text can be null
|
||||||
|
if (menuText.equals(text)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry getFirst() {
|
||||||
|
return get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry getLast() {
|
||||||
|
// Must be wrapped in a synchronized block for object visibility
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
if (!menuEntries.isEmpty()) {
|
||||||
|
Entry entry = null;
|
||||||
|
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
||||||
|
entry = menuEntries.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(entry instanceof dorkbox.systemTray.Separator || entry instanceof Status)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry get(final int menuIndex) {
|
||||||
|
if (menuIndex < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be wrapped in a synchronized block for object visibility
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
if (!menuEntries.isEmpty()) {
|
||||||
|
int count = 0;
|
||||||
|
for (Entry entry : menuEntries) {
|
||||||
|
if (entry instanceof dorkbox.systemTray.Separator || entry instanceof Status) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == menuIndex) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry addEntry(String menuText, SystemTrayMenuAction callback) {
|
||||||
|
return addEntry(menuText, (String) null, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback) {
|
||||||
|
if (imagePath == null) {
|
||||||
|
return addEntry_(menuText, null, callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath), callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) {
|
||||||
|
if (imageUrl == null) {
|
||||||
|
return addEntry_(menuText, null, callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl), callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
return addEntry_(menuText, null, callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream), callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Entry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
return addEntry_(menuText, null, callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream), callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu addMenu(String menuText) {
|
||||||
|
return addMenu(menuText, (String) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu addMenu(String menuText, String imagePath) {
|
||||||
|
if (imagePath == null) {
|
||||||
|
return addMenu_(menuText, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu addMenu(String menuText, URL imageUrl) {
|
||||||
|
if (imageUrl == null) {
|
||||||
|
return addMenu_(menuText, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu addMenu(String menuText, String cacheName, InputStream imageStream) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
return addMenu_(menuText, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
Menu addMenu(String menuText, InputStream imageStream) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
return addMenu_(menuText, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return addMenu_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final File imageFile) {
|
||||||
|
if (imageFile == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final String imagePath) {
|
||||||
|
if (imagePath == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final URL imageUrl) {
|
||||||
|
if (imageUrl == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final String cacheName, final InputStream imageStream) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final InputStream imageStream) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setText(final String newText) {
|
||||||
|
text = newText;
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
renderText(newText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setCallback(final SystemTrayMenuAction callback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setShortcut(final char key) {
|
||||||
|
if (_native instanceof JMenuItem) {
|
||||||
|
// yikes...
|
||||||
|
final int vKey = getVkKey(key);
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
((JMenuItem) _native).setMnemonic(vKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes a menu entry from the dropdown menu.
|
||||||
|
*
|
||||||
|
* @param entry This is the menu entry to remove
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
void remove(final Entry entry) {
|
||||||
|
if (entry == null) {
|
||||||
|
throw new NullPointerException("No menu entry exists for entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
remove__(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes a sub-menu entry from the dropdown menu.
|
||||||
|
*
|
||||||
|
* @param menu This is the menu entry to remove
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void remove(final Menu menu) {
|
||||||
|
final Menu parent = getParent();
|
||||||
|
if (parent == null) {
|
||||||
|
// we are the system tray menu, so we just remove from ourselves
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
remove__(menu);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
final Menu _this = this;
|
||||||
|
// we are a submenu
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
((MenuImpl) parent).remove__(_this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT ALWAYS CALLED ON EDT
|
||||||
|
private
|
||||||
|
void remove__(final Object menuEntry) {
|
||||||
|
try {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
// null is passed in when a sub-menu is removing itself from us (because they have already called "remove" and have also
|
||||||
|
// removed themselves from the menuEntries)
|
||||||
|
if (menuEntry != null) {
|
||||||
|
for (Iterator<Entry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
|
||||||
|
final Entry entry = iterator.next();
|
||||||
|
if (entry == menuEntry) {
|
||||||
|
iterator.remove();
|
||||||
|
entry.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
|
||||||
|
if (!menuEntries.isEmpty()) {
|
||||||
|
if (menuEntries.get(0) instanceof dorkbox.systemTray.Separator) {
|
||||||
|
remove(menuEntries.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
|
||||||
|
if (!menuEntries.isEmpty()) {
|
||||||
|
if (menuEntries.get(menuEntries.size()-1) instanceof dorkbox.systemTray.Separator) {
|
||||||
|
remove(menuEntries.get(menuEntries.size() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
SystemTray.logger.error("Error removing entry from menu.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes a menu entry or sub-menu (via the text label) from the dropdown menu.
|
||||||
|
*
|
||||||
|
* @param menuText This is the label for the menu entry or sub-menu to remove
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
void remove(final String menuText) {
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
Entry entry = get(menuText);
|
||||||
|
|
||||||
|
if (entry != null) {
|
||||||
|
remove(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void remove() {
|
||||||
|
dispatchAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
_native.setVisible(false);
|
||||||
|
if (_native instanceof TrayPopup) {
|
||||||
|
((TrayPopup) _native).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuImpl parent = (MenuImpl) getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
parent._native.remove(_native);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void clear() {
|
||||||
|
dispatch(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
synchronized (menuEntries) {
|
||||||
|
// have to make copy because we are deleting all of them, and sub-menus remove themselves from parents
|
||||||
|
ArrayList<Entry> menuEntriesCopy = new ArrayList<Entry>(MenuImpl.this.menuEntries);
|
||||||
|
for (Entry entry : menuEntriesCopy) {
|
||||||
|
entry.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,331 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 static dorkbox.systemTray.swing.Entry.getVkKey;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JMenuItem;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.Menu;
|
|
||||||
import dorkbox.systemTray.MenuEntry;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
|
||||||
import dorkbox.util.SwingUtil;
|
|
||||||
|
|
||||||
// this is a weird composite class, because it must be a Menu, but ALSO a MenuEntry -- so it has both
|
|
||||||
class SwingMenu extends Menu {
|
|
||||||
|
|
||||||
// sub-menu = AdjustedJMenu
|
|
||||||
// systemtray = TrayPopup
|
|
||||||
volatile JComponent _native;
|
|
||||||
|
|
||||||
// this have to be volatile, because they can be changed from any thread
|
|
||||||
private volatile String text;
|
|
||||||
private volatile boolean hasLegitIcon = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called in the EDT
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
SwingMenu(final SystemTray systemTray, final Menu parent, final JComponent _native) {
|
|
||||||
super(systemTray, parent);
|
|
||||||
this._native = _native;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected
|
|
||||||
void dispatch(final Runnable runnable) {
|
|
||||||
// this will properly check if we are running on the EDT
|
|
||||||
SwingUtil.invokeLater(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected
|
|
||||||
void dispatchAndWait(final Runnable runnable) {
|
|
||||||
// this will properly check if we are running on the EDT
|
|
||||||
try {
|
|
||||||
SwingUtil.invokeAndWait(runnable);
|
|
||||||
} catch (Exception e) {
|
|
||||||
SystemTray.logger.error("Error processing event on the dispatch thread.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void addSeparator() {
|
|
||||||
dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = new EntrySeparator(SwingMenu.this);
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables, or disables the sub-menu entry.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setEnabled(final boolean enabled) {
|
|
||||||
_native.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will add a new menu entry, or update one if it already exists
|
|
||||||
*/
|
|
||||||
protected
|
|
||||||
MenuEntry addEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) {
|
|
||||||
if (menuText == null) {
|
|
||||||
throw new NullPointerException("Menu text cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
final AtomicReference<MenuEntry> value = new AtomicReference<MenuEntry>();
|
|
||||||
|
|
||||||
dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = get(menuText);
|
|
||||||
|
|
||||||
if (menuEntry == null) {
|
|
||||||
// must always be called on the EDT
|
|
||||||
menuEntry = new EntryItem(SwingMenu.this, callback);
|
|
||||||
menuEntry.setText(menuText);
|
|
||||||
menuEntry.setImage(imagePath);
|
|
||||||
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
} else if (menuEntry instanceof EntryItem) {
|
|
||||||
menuEntry.setText(menuText);
|
|
||||||
menuEntry.setImage(imagePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
value.set(menuEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return value.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will add a new sub-menu entry, or update one if it already exists
|
|
||||||
*/
|
|
||||||
protected
|
|
||||||
Menu addMenu_(final String menuText, final File imagePath) {
|
|
||||||
if (menuText == null) {
|
|
||||||
throw new NullPointerException("Menu text cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
final AtomicReference<Menu> value = new AtomicReference<Menu>();
|
|
||||||
|
|
||||||
dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
MenuEntry menuEntry = get(menuText);
|
|
||||||
|
|
||||||
if (menuEntry == null) {
|
|
||||||
// must always be called on the EDT
|
|
||||||
menuEntry = new SwingMenu(getSystemTray(), SwingMenu.this, new AdjustedJMenu());
|
|
||||||
_native.add(((SwingMenu) menuEntry)._native);
|
|
||||||
|
|
||||||
menuEntry.setText(menuText);
|
|
||||||
menuEntry.setImage(imagePath);
|
|
||||||
value.set((Menu)menuEntry);
|
|
||||||
|
|
||||||
} else if (menuEntry instanceof SwingMenu) {
|
|
||||||
menuEntry.setText(menuText);
|
|
||||||
menuEntry.setImage(imagePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
menuEntries.add(menuEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return value.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// always called in the EDT
|
|
||||||
private
|
|
||||||
void renderText(final String text) {
|
|
||||||
((JMenuItem) _native).setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
|
||||||
private
|
|
||||||
void setImage_(final File imageFile) {
|
|
||||||
hasLegitIcon = imageFile != null;
|
|
||||||
|
|
||||||
SwingUtil.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
if (imageFile != null) {
|
|
||||||
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
|
|
||||||
((JMenuItem) _native).setIcon(origIcon);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
((JMenuItem) _native).setIcon(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
String getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setText(final String newText) {
|
|
||||||
text = newText;
|
|
||||||
SwingUtil.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
renderText(newText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setImage(final File imageFile) {
|
|
||||||
if (imageFile == null) {
|
|
||||||
setImage_(null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageFile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final
|
|
||||||
void setImage(final String imagePath) {
|
|
||||||
if (imagePath == null) {
|
|
||||||
setImage_(null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final
|
|
||||||
void setImage(final URL imageUrl) {
|
|
||||||
if (imageUrl == null) {
|
|
||||||
setImage_(null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final
|
|
||||||
void setImage(final String cacheName, final InputStream imageStream) {
|
|
||||||
if (imageStream == null) {
|
|
||||||
setImage_(null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final
|
|
||||||
void setImage(final InputStream imageStream) {
|
|
||||||
if (imageStream == null) {
|
|
||||||
setImage_(null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
boolean hasImage() {
|
|
||||||
return hasLegitIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setCallback(final SystemTrayMenuAction callback) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setShortcut(final char key) {
|
|
||||||
if (_native instanceof JMenuItem) {
|
|
||||||
// yikes...
|
|
||||||
final int vKey = getVkKey(key);
|
|
||||||
dispatch(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
((JMenuItem) _native).setMnemonic(vKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final
|
|
||||||
void remove() {
|
|
||||||
dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
_native.setVisible(false);
|
|
||||||
if (_native instanceof TrayPopup) {
|
|
||||||
((TrayPopup) _native).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
SwingMenu parent = (SwingMenu) getParent();
|
|
||||||
if (parent != null) {
|
|
||||||
parent._native.remove(_native);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -66,7 +66,6 @@ class TrayPopup extends JPopupMenu {
|
|||||||
hiddenDialog.setAlwaysOnTop(true);
|
hiddenDialog.setAlwaysOnTop(true);
|
||||||
|
|
||||||
// on Linux, the following two entries will **MOST OF THE TIME** prevent the hidden dialog from showing in the task-bar
|
// on Linux, the following two entries will **MOST OF THE TIME** prevent the hidden dialog from showing in the task-bar
|
||||||
// on MacOS, you need "special permission" to not have a hidden dialog show on the dock.
|
|
||||||
hiddenDialog.getContentPane().setLayout(null);
|
hiddenDialog.getContentPane().setLayout(null);
|
||||||
|
|
||||||
// this is java 1.7, so we have to use reflection. It's not critical for this to be set, but it helps hide the title in the taskbar
|
// this is java 1.7, so we have to use reflection. It's not critical for this to be set, but it helps hide the title in the taskbar
|
||||||
@ -157,15 +156,14 @@ class TrayPopup extends JPopupMenu {
|
|||||||
hiddenDialog.dispatchEvent(new WindowEvent(hiddenDialog, WindowEvent.WINDOW_CLOSING));
|
hiddenDialog.dispatchEvent(new WindowEvent(hiddenDialog, WindowEvent.WINDOW_CLOSING));
|
||||||
}
|
}
|
||||||
|
|
||||||
void doShow(final Point point, final int offset) {
|
void doShow(final Point point, int offset) {
|
||||||
|
|
||||||
Dimension size = getPreferredSize();
|
Dimension size = getPreferredSize();
|
||||||
|
|
||||||
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
||||||
|
|
||||||
int x = point.x;
|
int x = point.x;
|
||||||
int y = point.y;
|
int y = point.y;
|
||||||
|
|
||||||
|
|
||||||
if (y < bounds.y) {
|
if (y < bounds.y) {
|
||||||
y = bounds.y;
|
y = bounds.y;
|
||||||
}
|
}
|
||||||
@ -175,22 +173,19 @@ class TrayPopup extends JPopupMenu {
|
|||||||
y -= size.height; // snap to edge of mouse
|
y -= size.height; // snap to edge of mouse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (x < bounds.x) {
|
if (x < bounds.x) {
|
||||||
x = bounds.x;
|
x = bounds.x;
|
||||||
|
|
||||||
x -= offset; // display over the stupid appindicator menu (which has to show, this is a major hack!)
|
|
||||||
}
|
}
|
||||||
else if (x + size.width > bounds.x + bounds.width) {
|
else if (x + size.width > bounds.x + bounds.width) {
|
||||||
// our menu cannot have the left-edge snap to the mouse so we make the right-edge snap to the mouse
|
// our menu cannot have the left-edge snap to the mouse so we make the right-edge snap to the mouse
|
||||||
x -= size.width; // snap right edge of menu to mouse
|
x -= size.width; // snap right edge of menu to mouse
|
||||||
|
|
||||||
x += offset; // display over the stupid appindicator menu (which has to show, this is a major hack!)
|
offset = -offset; // flip offset
|
||||||
} else {
|
|
||||||
x -= offset; // display over the stupid appindicator menu (which has to show, this is a major hack!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.println("SHOWING POPUP @" + x + "," + y);
|
// display over the stupid AppIndicator menu (which has to show, then we remove. THIS IS A HACK!)
|
||||||
|
x -= offset;
|
||||||
|
|
||||||
// critical to get the keyboard listeners working for the popup menu
|
// critical to get the keyboard listeners working for the popup menu
|
||||||
setInvoker(hiddenDialog.getContentPane());
|
setInvoker(hiddenDialog.getContentPane());
|
||||||
@ -202,6 +197,4 @@ class TrayPopup extends JPopupMenu {
|
|||||||
setVisible(true);
|
setVisible(true);
|
||||||
requestFocusInWindow();
|
requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,17 +80,26 @@ import dorkbox.util.SwingUtil;
|
|||||||
* http://bazaar.launchpad.net/~ubuntu-desktop/ido/gtk3/files
|
* http://bazaar.launchpad.net/~ubuntu-desktop/ido/gtk3/files
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class _AppIndicatorTray extends GenericTray {
|
class _AppIndicatorTray extends _Tray {
|
||||||
private AppIndicatorInstanceStruct appIndicator;
|
private volatile AppIndicatorInstanceStruct appIndicator;
|
||||||
private boolean isActive = false;
|
private boolean isActive = false;
|
||||||
|
private final Runnable popupRunnable;
|
||||||
|
|
||||||
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
|
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
|
||||||
private AtomicBoolean shuttingDown = new AtomicBoolean();
|
private AtomicBoolean shuttingDown = new AtomicBoolean();
|
||||||
|
|
||||||
private volatile NativeLong nativeLong;
|
// necessary to prevent GC on these objects
|
||||||
private volatile GEventCallback gtkCallback;
|
private NativeLong nativeLong;
|
||||||
|
private GEventCallback gtkCallback;
|
||||||
|
|
||||||
|
|
||||||
|
// necessary to provide a menu (which we draw over) so we get the "on open" event when the menu is opened via clicking
|
||||||
private Pointer dummyMenu;
|
private Pointer dummyMenu;
|
||||||
private final Runnable popupRunnable;
|
|
||||||
|
|
||||||
|
// appindicators DO NOT support anything other than PLAIN gtk-menus (which we hack to support swing menus)
|
||||||
|
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
||||||
|
// trayIcon.setToolTip("app name");
|
||||||
|
|
||||||
public
|
public
|
||||||
_AppIndicatorTray(final SystemTray systemTray) {
|
_AppIndicatorTray(final SystemTray systemTray) {
|
||||||
@ -98,7 +107,7 @@ class _AppIndicatorTray extends GenericTray {
|
|||||||
|
|
||||||
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 Tray if 'SystemTray.FORCE_TRAY_TYPE' is set to GtkStatusIcon");
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayPopup popupMenu = (TrayPopup) _native;
|
TrayPopup popupMenu = (TrayPopup) _native;
|
||||||
@ -108,6 +117,11 @@ class _AppIndicatorTray extends GenericTray {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
|
if (appIndicator == null) {
|
||||||
|
// if we are shutting down, don't hook the menu again
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Such ugly hacks to get AppIndicator support properly working. This is so horrible I am ashamed.
|
// Such ugly hacks to get AppIndicator support properly working. This is so horrible I am ashamed.
|
||||||
Gtk.dispatch(new Runnable() {
|
Gtk.dispatch(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -132,10 +146,6 @@ class _AppIndicatorTray extends GenericTray {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// appindicators DO NOT support anything other than PLAIN gtk-menus
|
|
||||||
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
|
||||||
// trayIcon.setToolTip(_SwingTray.this.appName);
|
|
||||||
|
|
||||||
Gtk.startGui();
|
Gtk.startGui();
|
||||||
|
|
||||||
Gtk.dispatch(new Runnable() {
|
Gtk.dispatch(new Runnable() {
|
||||||
@ -167,7 +177,7 @@ class _AppIndicatorTray extends GenericTray {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void callback(Pointer notUsed, final GdkEventButton event) {
|
void callback(Pointer notUsed, final GdkEventButton event) {
|
||||||
Gtk.gtk_widget_destroy(dummyMenu);
|
Gtk.gtk_widget_destroy(dummyMenu); // destroy the menu, so it will disappear (and we then have focus on our swing menu)
|
||||||
SwingUtil.invokeLater(popupRunnable);
|
SwingUtil.invokeLater(popupRunnable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -175,7 +185,8 @@ class _AppIndicatorTray extends GenericTray {
|
|||||||
nativeLong = Gobject.g_signal_connect_object(rootMenuItem.getValue(), "about-to-show", gtkCallback, null, 0);
|
nativeLong = Gobject.g_signal_connect_object(rootMenuItem.getValue(), "about-to-show", gtkCallback, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAppIndicatorMenu() {
|
private
|
||||||
|
void createAppIndicatorMenu() {
|
||||||
dummyMenu = Gtk.gtk_menu_new();
|
dummyMenu = Gtk.gtk_menu_new();
|
||||||
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic("");
|
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic("");
|
||||||
Gtk.gtk_menu_shell_append(dummyMenu, item);
|
Gtk.gtk_menu_shell_append(dummyMenu, item);
|
||||||
@ -223,7 +234,7 @@ class _AppIndicatorTray extends GenericTray {
|
|||||||
|
|
||||||
// kindof lame, but necessary for KDE
|
// kindof lame, but necessary for KDE
|
||||||
if (Gtk.isKDE) {
|
if (Gtk.isKDE) {
|
||||||
AppIndicator.app_indicator_set_label(appIndicator, "SystemTray", null);
|
AppIndicator.app_indicator_set_label(appIndicator, "SystemTray", "SystemTray");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we have to setup a way for us to catch the "activation" click on this menu. Must be after the menu is set
|
// now we have to setup a way for us to catch the "activation" click on this menu. Must be after the menu is set
|
||||||
|
@ -40,7 +40,7 @@ import dorkbox.systemTray.linux.jna.Gtk;
|
|||||||
* swing menu popup INSTEAD of GTK menu popups. The "golden standard" is our swing menu popup, since we have 100% control over it.
|
* swing menu popup INSTEAD of GTK menu popups. The "golden standard" is our swing menu popup, since we have 100% control over it.
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class _GtkStatusIconTray extends GenericTray {
|
class _GtkStatusIconTray extends _Tray {
|
||||||
private volatile Pointer trayIcon;
|
private volatile Pointer trayIcon;
|
||||||
|
|
||||||
// http://code.metager.de/source/xref/gnome/Platform/gtk%2B/gtk/deprecated/gtkstatusicon.c
|
// http://code.metager.de/source/xref/gnome/Platform/gtk%2B/gtk/deprecated/gtkstatusicon.c
|
||||||
@ -58,6 +58,7 @@ class _GtkStatusIconTray extends GenericTray {
|
|||||||
public
|
public
|
||||||
_GtkStatusIconTray(final SystemTray systemTray) {
|
_GtkStatusIconTray(final SystemTray systemTray) {
|
||||||
super(systemTray, null, new TrayPopup());
|
super(systemTray, null, new TrayPopup());
|
||||||
|
|
||||||
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");
|
||||||
@ -79,9 +80,9 @@ class _GtkStatusIconTray extends GenericTray {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// appindicators DO NOT support anything other than PLAIN gtk-menus
|
// appindicators DO NOT support anything other than PLAIN gtk-menus (which we hack to support swing menus)
|
||||||
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
||||||
// trayIcon.setToolTip(_SwingTray.this.appName);
|
// trayIcon.setToolTip("app name");
|
||||||
|
|
||||||
Gtk.startGui();
|
Gtk.startGui();
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import java.io.File;
|
|||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
|
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for handling all system tray interaction, via SWING.
|
* Class for handling all system tray interaction, via SWING.
|
||||||
@ -38,7 +38,7 @@ import dorkbox.systemTray.MenuEntry;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
|
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
|
||||||
public
|
public
|
||||||
class _SwingTray extends GenericTray {
|
class _SwingTray extends _Tray {
|
||||||
volatile SystemTray tray;
|
volatile SystemTray tray;
|
||||||
volatile TrayIcon trayIcon;
|
volatile TrayIcon trayIcon;
|
||||||
|
|
||||||
@ -59,8 +59,8 @@ class _SwingTray extends GenericTray {
|
|||||||
tray.remove(trayIcon);
|
tray.remove(trayIcon);
|
||||||
|
|
||||||
synchronized (menuEntries) {
|
synchronized (menuEntries) {
|
||||||
for (MenuEntry menuEntry : menuEntries) {
|
for (Entry entry : menuEntries) {
|
||||||
menuEntry.remove();
|
entry.remove();
|
||||||
}
|
}
|
||||||
menuEntries.clear();
|
menuEntries.clear();
|
||||||
}
|
}
|
||||||
@ -88,9 +88,9 @@ class _SwingTray extends GenericTray {
|
|||||||
popupMenu.pack();
|
popupMenu.pack();
|
||||||
popupMenu.setFocusable(true);
|
popupMenu.setFocusable(true);
|
||||||
|
|
||||||
// appindicators DO NOT support anything other than PLAIN gtk-menus
|
// appindicators DO NOT support anything other than PLAIN gtk-menus (which we hack to support swing menus)
|
||||||
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
||||||
// trayIcon.setToolTip(_SwingTray.this.appName);
|
// trayIcon.setToolTip("app name");
|
||||||
|
|
||||||
trayIcon.addMouseListener(new MouseAdapter() {
|
trayIcon.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,17 +17,17 @@ package dorkbox.systemTray.swing;
|
|||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Entry;
|
||||||
import dorkbox.systemTray.Menu;
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.util.ImageUtils;
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
|
|
||||||
public abstract
|
public abstract
|
||||||
class GenericTray extends SwingMenu {
|
class _Tray extends MenuImpl {
|
||||||
/**
|
/**
|
||||||
* Called in the EDT
|
* Called in the EDT
|
||||||
*/
|
*/
|
||||||
GenericTray(final SystemTray systemTray, final Menu parent, final JComponent _native) {
|
_Tray(final SystemTray systemTray, final Menu parent, final JComponent _native) {
|
||||||
super(systemTray, parent, _native);
|
super(systemTray, parent, _native);
|
||||||
|
|
||||||
ImageUtils.determineIconSize();
|
ImageUtils.determineIconSize();
|
||||||
@ -36,9 +36,9 @@ class GenericTray extends SwingMenu {
|
|||||||
public
|
public
|
||||||
String getStatus() {
|
String getStatus() {
|
||||||
synchronized (menuEntries) {
|
synchronized (menuEntries) {
|
||||||
MenuEntry menuEntry = menuEntries.get(0);
|
Entry entry = menuEntries.get(0);
|
||||||
if (menuEntry instanceof EntryStatus) {
|
if (entry instanceof EntryStatus) {
|
||||||
return menuEntry.getText();
|
return entry.getText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,16 +47,16 @@ class GenericTray extends SwingMenu {
|
|||||||
|
|
||||||
public
|
public
|
||||||
void setStatus(final String statusText) {
|
void setStatus(final String statusText) {
|
||||||
final SwingMenu _this = this;
|
final MenuImpl _this = this;
|
||||||
dispatchAndWait(new Runnable() {
|
dispatchAndWait(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
synchronized (menuEntries) {
|
synchronized (menuEntries) {
|
||||||
// status is ALWAYS at 0 index...
|
// status is ALWAYS at 0 index...
|
||||||
Entry menuEntry = null;
|
EntryImpl menuEntry = null;
|
||||||
if (!menuEntries.isEmpty()) {
|
if (!menuEntries.isEmpty()) {
|
||||||
menuEntry = (Entry) menuEntries.get(0);
|
menuEntry = (EntryImpl) menuEntries.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuEntry instanceof EntryStatus) {
|
if (menuEntry instanceof EntryStatus) {
|
@ -18,8 +18,8 @@ package dorkbox;
|
|||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Entry;
|
||||||
import dorkbox.systemTray.Menu;
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
|
||||||
@ -56,13 +56,13 @@ class TestTray {
|
|||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.setStatus("Some Mail!");
|
systemTray.setStatus("Some Mail!");
|
||||||
systemTray.setImage(GREEN_MAIL);
|
systemTray.setImage(GREEN_MAIL);
|
||||||
|
|
||||||
menuEntry.setCallback(callbackGray);
|
entry.setCallback(callbackGray);
|
||||||
menuEntry.setImage(BLACK_MAIL);
|
entry.setImage(BLACK_MAIL);
|
||||||
menuEntry.setText("Delete Mail");
|
entry.setText("Delete Mail");
|
||||||
// systemTray.remove(menuEntry);
|
// systemTray.remove(menuEntry);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -70,18 +70,18 @@ class TestTray {
|
|||||||
callbackGray = new SystemTrayMenuAction() {
|
callbackGray = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.setStatus(null);
|
systemTray.setStatus(null);
|
||||||
systemTray.setImage(BLACK_MAIL);
|
systemTray.setImage(BLACK_MAIL);
|
||||||
|
|
||||||
menuEntry.setCallback(null);
|
entry.setCallback(null);
|
||||||
// systemTray.setStatus("Mail Empty");
|
// systemTray.setStatus("Mail Empty");
|
||||||
systemTray.remove(menuEntry);
|
systemTray.remove(entry);
|
||||||
System.err.println("POW");
|
System.err.println("POW");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuEntry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
Entry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
||||||
// case does not matter
|
// case does not matter
|
||||||
menuEntry.setShortcut('G');
|
menuEntry.setShortcut('G');
|
||||||
|
|
||||||
@ -91,14 +91,14 @@ class TestTray {
|
|||||||
submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() {
|
submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry entry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
submenu.setEnabled(false);
|
submenu.setEnabled(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() {
|
submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry entry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
submenu.remove();
|
submenu.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -107,7 +107,7 @@ class TestTray {
|
|||||||
systemTray.addEntry("Quit", new SystemTrayMenuAction() {
|
systemTray.addEntry("Quit", new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
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,8 +18,8 @@ package dorkbox;
|
|||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
import dorkbox.systemTray.Entry;
|
||||||
import dorkbox.systemTray.Menu;
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
@ -90,13 +90,13 @@ class TestTrayJavaFX extends Application {
|
|||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.setImage(GREEN_MAIL);
|
systemTray.setImage(GREEN_MAIL);
|
||||||
systemTray.setStatus("Some Mail!");
|
systemTray.setStatus("Some Mail!");
|
||||||
|
|
||||||
menuEntry.setCallback(callbackGray);
|
entry.setCallback(callbackGray);
|
||||||
menuEntry.setImage(BLACK_MAIL);
|
entry.setImage(BLACK_MAIL);
|
||||||
menuEntry.setText("Delete Mail");
|
entry.setText("Delete Mail");
|
||||||
// systemTray.remove(menuEntry);
|
// systemTray.remove(menuEntry);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -104,18 +104,18 @@ class TestTrayJavaFX extends Application {
|
|||||||
callbackGray = new SystemTrayMenuAction() {
|
callbackGray = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.setStatus(null);
|
systemTray.setStatus(null);
|
||||||
systemTray.setImage(BLACK_MAIL);
|
systemTray.setImage(BLACK_MAIL);
|
||||||
|
|
||||||
menuEntry.setCallback(null);
|
entry.setCallback(null);
|
||||||
// systemTray.setStatus("Mail Empty");
|
// systemTray.setStatus("Mail Empty");
|
||||||
systemTray.remove(menuEntry);
|
systemTray.remove(entry);
|
||||||
System.err.println("POW");
|
System.err.println("POW");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuEntry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
Entry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
||||||
// case does not matter
|
// case does not matter
|
||||||
menuEntry.setShortcut('G');
|
menuEntry.setShortcut('G');
|
||||||
|
|
||||||
@ -125,14 +125,14 @@ class TestTrayJavaFX extends Application {
|
|||||||
submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() {
|
submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry entry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
submenu.setEnabled(false);
|
submenu.setEnabled(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() {
|
submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry entry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
submenu.remove();
|
submenu.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -140,7 +140,7 @@ class TestTrayJavaFX extends Application {
|
|||||||
systemTray.addEntry("Quit", new SystemTrayMenuAction() {
|
systemTray.addEntry("Quit", new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
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,8 +23,8 @@ 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.Entry;
|
||||||
import dorkbox.systemTray.Menu;
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
|
||||||
@ -74,13 +74,13 @@ class TestTraySwt {
|
|||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.setStatus("Some Mail!");
|
systemTray.setStatus("Some Mail!");
|
||||||
systemTray.setImage(GREEN_MAIL);
|
systemTray.setImage(GREEN_MAIL);
|
||||||
|
|
||||||
menuEntry.setCallback(callbackGray);
|
entry.setCallback(callbackGray);
|
||||||
menuEntry.setImage(BLACK_MAIL);
|
entry.setImage(BLACK_MAIL);
|
||||||
menuEntry.setText("Delete Mail");
|
entry.setText("Delete Mail");
|
||||||
// systemTray.remove(menuEntry);
|
// systemTray.remove(menuEntry);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -88,18 +88,18 @@ class TestTraySwt {
|
|||||||
callbackGray = new SystemTrayMenuAction() {
|
callbackGray = new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.setStatus(null);
|
systemTray.setStatus(null);
|
||||||
systemTray.setImage(BLACK_MAIL);
|
systemTray.setImage(BLACK_MAIL);
|
||||||
|
|
||||||
menuEntry.setCallback(null);
|
entry.setCallback(null);
|
||||||
// systemTray.setStatus("Mail Empty");
|
// systemTray.setStatus("Mail Empty");
|
||||||
systemTray.remove(menuEntry);
|
systemTray.remove(entry);
|
||||||
System.err.println("POW");
|
System.err.println("POW");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuEntry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
Entry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
|
||||||
// case does not matter
|
// case does not matter
|
||||||
menuEntry.setShortcut('G');
|
menuEntry.setShortcut('G');
|
||||||
|
|
||||||
@ -109,14 +109,14 @@ class TestTraySwt {
|
|||||||
submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() {
|
submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry entry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
submenu.setEnabled(false);
|
submenu.setEnabled(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() {
|
submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry entry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
submenu.remove();
|
submenu.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -124,7 +124,7 @@ class TestTraySwt {
|
|||||||
systemTray.addEntry("Quit", new SystemTrayMenuAction() {
|
systemTray.addEntry("Quit", new SystemTrayMenuAction() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onClick(final SystemTray systemTray, final Menu parent, final MenuEntry menuEntry) {
|
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
|
||||||
systemTray.shutdown();
|
systemTray.shutdown();
|
||||||
|
|
||||||
display.asyncExec(new Runnable() {
|
display.asyncExec(new Runnable() {
|
||||||
|
Loading…
Reference in New Issue
Block a user