forked from dorkbox/SystemTray
WIP AppIndicators using swing menu
This commit is contained in:
parent
b936a4cd76
commit
fbf528d0ca
@ -34,8 +34,6 @@ class AppIndicator {
|
||||
public static boolean isVersion3 = false;
|
||||
private static boolean isLoaded = false;
|
||||
|
||||
private static final boolean VERBOSE_DEBUG = false;
|
||||
|
||||
/**
|
||||
* Loader for AppIndicator, because it is absolutely mindboggling how those whom maintain the standard, can't agree to what that
|
||||
* standard library naming convention or features/API set is. We just try until we find one that work, and are able to map the
|
||||
@ -69,11 +67,8 @@ class AppIndicator {
|
||||
isLoaded = true;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (VERBOSE_DEBUG) {
|
||||
logger.debug("Error loading library: {}", "appindicator1", e);
|
||||
}
|
||||
else if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading GTK2 explicit appindicator1");
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading GTK2 explicit appindicator1. {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,7 +99,7 @@ class AppIndicator {
|
||||
isLoaded = true;
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading library: '{}'", nameToCheck1, e);
|
||||
logger.debug("Error loading library: '{}'. \n{}", nameToCheck1, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,7 +136,7 @@ class AppIndicator {
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading library: '{}'", "appindicator" + i, e);
|
||||
logger.debug("Error loading library: '{}'. \n{}", "appindicator" + i, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +163,7 @@ class AppIndicator {
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading library: '{}'", "appindicator" + i, e);
|
||||
logger.debug("Error loading library: '{}'. \n{}", "appindicator" + i, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +191,7 @@ class AppIndicator {
|
||||
isLoaded = true;
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading library: '{}'", nameToCheck1, e);
|
||||
logger.debug("Error loading library: '{}'. \n{}", nameToCheck1, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,7 +203,7 @@ class AppIndicator {
|
||||
isLoaded = true;
|
||||
} catch (Throwable e) {
|
||||
if (SystemTray.DEBUG) {
|
||||
logger.debug("Error loading library: '{}'", nameToCheck2, e);
|
||||
logger.debug("Error loading library: '{}'. \n{}", nameToCheck2, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,4 +228,5 @@ class AppIndicator {
|
||||
public static native void app_indicator_set_status(AppIndicatorInstanceStruct self, int status);
|
||||
public static native void app_indicator_set_menu(AppIndicatorInstanceStruct self, Pointer menu);
|
||||
public static native void app_indicator_set_icon(AppIndicatorInstanceStruct self, String icon_name);
|
||||
public static native void app_indicator_set_label(AppIndicatorInstanceStruct self, String label, String notused);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package dorkbox.systemTray.linux.jna;
|
||||
import com.sun.jna.Callback;
|
||||
import com.sun.jna.NativeLong;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
|
||||
/**
|
||||
* bindings for libgobject-2.0
|
||||
@ -32,6 +33,8 @@ class Gobject {
|
||||
}
|
||||
|
||||
|
||||
public static native void g_object_get(Pointer object, String objectName, PointerByReference objectVal, Pointer nullValue);
|
||||
|
||||
public static native void g_free(Pointer object);
|
||||
public static native void g_object_unref(Pointer object);
|
||||
|
||||
|
@ -55,6 +55,7 @@ class Gtk {
|
||||
|
||||
// there is ONLY a single thread EVER setting this value!!
|
||||
private static volatile boolean isDispatch = false;
|
||||
public static boolean isKDE = false;
|
||||
|
||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk
|
||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
|
||||
|
291
src/dorkbox/systemTray/swing/AppIndicatorTray.java
Normal file
291
src/dorkbox/systemTray/swing/AppIndicatorTray.java
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.SystemTray.TIMEOUT;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import com.sun.jna.NativeLong;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
|
||||
import dorkbox.systemTray.SystemTray;
|
||||
import dorkbox.systemTray.linux.jna.AppIndicator;
|
||||
import dorkbox.systemTray.linux.jna.AppIndicatorInstanceStruct;
|
||||
import dorkbox.systemTray.linux.jna.GEventCallback;
|
||||
import dorkbox.systemTray.linux.jna.GdkEventButton;
|
||||
import dorkbox.systemTray.linux.jna.Gobject;
|
||||
import dorkbox.systemTray.linux.jna.Gtk;
|
||||
import dorkbox.systemTray.util.ImageUtils;
|
||||
import dorkbox.util.ScreenUtil;
|
||||
import dorkbox.util.SwingUtil;
|
||||
|
||||
/**
|
||||
* Class for handling all system tray interactions.
|
||||
* specialization for using app indicators in ubuntu unity
|
||||
*
|
||||
* Derived from
|
||||
* Lantern: https://github.com/getlantern/lantern/ Apache 2.0 License Copyright 2010 Brave New Software Project, Inc.
|
||||
*
|
||||
* AppIndicators DO NOT support anything other than plain gtk-menus, because of how they use dbus so no tooltips AND no custom widgets
|
||||
*
|
||||
*
|
||||
*
|
||||
* As a result of this decision by Canonical, we have to resort to hacks to get it to do what we want. BY NO MEANS IS THIS PERFECT.
|
||||
*
|
||||
*
|
||||
* We still cannot have tooltips, but we *CAN* have custom widgets in the menu (because it's our swing menu now...)
|
||||
*
|
||||
*
|
||||
* It would be too much work to re-implement AppIndicators, or even to use LD_PRELOAD + restart service to do what we want.
|
||||
*
|
||||
* As a result, we have some wicked little hacks which are rather effective (but have a small side-effect of very briefly
|
||||
* showing a blank menu)
|
||||
*
|
||||
* // What are AppIndicators?
|
||||
* http://unity.ubuntu.com/projects/appindicators/
|
||||
*
|
||||
*
|
||||
* // Entry-point into appindicators
|
||||
* http://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/services/panel-main.c
|
||||
*
|
||||
*
|
||||
* // The idiocy of appindicators
|
||||
* https://bugs.launchpad.net/screenlets/+bug/522152
|
||||
*
|
||||
* // Code of how the dbus menus work
|
||||
* http://bazaar.launchpad.net/~dbusmenu-team/libdbusmenu/trunk.16.10/view/head:/libdbusmenu-gtk/client.c
|
||||
* https://developer.ubuntu.com/api/devel/ubuntu-12.04/c/dbusmenugtk/index.html
|
||||
*
|
||||
* // more info about trying to put widgets into GTK menus
|
||||
* http://askubuntu.com/questions/16431/putting-an-arbitrary-gtk-widget-into-an-appindicator-indicator
|
||||
*
|
||||
* // possible idea on how to get GTK widgets into GTK menus
|
||||
* https://launchpad.net/ido
|
||||
* http://bazaar.launchpad.net/~canonical-dx-team/ido/trunk/view/head:/src/idoentrymenuitem.c
|
||||
* http://bazaar.launchpad.net/~ubuntu-desktop/ido/gtk3/files
|
||||
*/
|
||||
public
|
||||
class AppIndicatorTray extends SwingGenericTray {
|
||||
private AppIndicatorInstanceStruct appIndicator;
|
||||
private boolean isActive = false;
|
||||
|
||||
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
|
||||
private AtomicBoolean shuttingDown = new AtomicBoolean();
|
||||
|
||||
private volatile NativeLong nativeLong;
|
||||
private volatile GEventCallback gtkCallback;
|
||||
private Pointer dummyMenu;
|
||||
private final Runnable popupRunnable;
|
||||
|
||||
public
|
||||
AppIndicatorTray(final SystemTray systemTray) {
|
||||
super(systemTray,null, new SwingSystemTrayMenuPopup());
|
||||
|
||||
if (SystemTray.FORCE_TRAY_TYPE == SystemTray.TYPE_GTK_STATUSICON) {
|
||||
// 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");
|
||||
}
|
||||
|
||||
JPopupMenu popupMenu = (JPopupMenu) _native;
|
||||
popupMenu.pack();
|
||||
popupMenu.setFocusable(true);
|
||||
|
||||
popupRunnable = new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Dimension size = _native.getPreferredSize();
|
||||
|
||||
Point point = MouseInfo.getPointerInfo()
|
||||
.getLocation();
|
||||
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
||||
|
||||
int x = point.x;
|
||||
int y = point.y;
|
||||
|
||||
if (y < bounds.y) {
|
||||
y = bounds.y;
|
||||
}
|
||||
else if (y + size.height > bounds.y + bounds.height) {
|
||||
// our menu cannot have the top-edge snap to the mouse
|
||||
// so we make the bottom-edge snap to the mouse
|
||||
y -= size.height; // snap to edge of mouse
|
||||
}
|
||||
|
||||
if (x < bounds.x) {
|
||||
x = bounds.x;
|
||||
|
||||
x -= 32; // display over the stupid appindicator menu (which has to show, this is a major hack!)
|
||||
}
|
||||
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
|
||||
x -= size.width; // snap to edge of mouse
|
||||
|
||||
x += 32; // display over the stupid appindicator menu (which has to show, this is a major hack!)
|
||||
}
|
||||
|
||||
SwingSystemTrayMenuPopup popupMenu = (SwingSystemTrayMenuPopup) _native;
|
||||
popupMenu.doShow(x, y);
|
||||
|
||||
// Such ugly hacks to get AppIndicator support properly working. This is so horrible I am ashamed.
|
||||
Gtk.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
createAppIndicatorMenu();
|
||||
hookMenuOpen();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 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(SwingSystemTray.this.appName);
|
||||
|
||||
Gtk.startGui();
|
||||
|
||||
Gtk.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
// we initialize with a blank image
|
||||
File image = ImageUtils.getTransparentImage(ImageUtils.ENTRY_SIZE);
|
||||
String id = System.nanoTime() + "DBST";
|
||||
appIndicator = AppIndicator.app_indicator_new(id, image.getAbsolutePath(), AppIndicator.CATEGORY_APPLICATION_STATUS);
|
||||
|
||||
createAppIndicatorMenu();
|
||||
}
|
||||
});
|
||||
|
||||
Gtk.waitForStartup();
|
||||
}
|
||||
|
||||
private
|
||||
void hookMenuOpen() {
|
||||
// now we have to setup a way for us to catch the "activation" click on this menu. Must be after the menu is set
|
||||
PointerByReference menuServer = new PointerByReference();
|
||||
PointerByReference rootMenuItem = new PointerByReference();
|
||||
|
||||
Gobject.g_object_get(appIndicator.getPointer(), "dbus-menu-server", menuServer, null);
|
||||
Gobject.g_object_get(menuServer.getValue(), "root-node", rootMenuItem, null);
|
||||
|
||||
gtkCallback = new GEventCallback() {
|
||||
@Override
|
||||
public
|
||||
void callback(Pointer notUsed, final GdkEventButton event) {
|
||||
Gtk.gtk_widget_destroy(dummyMenu);
|
||||
SwingUtil.invokeLater(popupRunnable);
|
||||
}
|
||||
};
|
||||
|
||||
nativeLong = Gobject.g_signal_connect_object(rootMenuItem.getValue(), "about-to-show", gtkCallback, null, 0);
|
||||
}
|
||||
|
||||
private void createAppIndicatorMenu() {
|
||||
dummyMenu = Gtk.gtk_menu_new();
|
||||
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic("");
|
||||
Gtk.gtk_menu_shell_append(dummyMenu, item);
|
||||
Gtk.gtk_widget_show_all(item);
|
||||
|
||||
AppIndicator.app_indicator_set_menu(appIndicator, dummyMenu);
|
||||
}
|
||||
|
||||
public
|
||||
void shutdown() {
|
||||
if (!shuttingDown.getAndSet(true)) {
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
|
||||
Gtk.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
try {
|
||||
// STATUS_PASSIVE hides the indicator
|
||||
AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_PASSIVE);
|
||||
Pointer p = appIndicator.getPointer();
|
||||
Gobject.g_object_unref(p);
|
||||
|
||||
appIndicator = null;
|
||||
} finally {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// this is slightly different than how swing does it. We have a timeout here so that we can make sure that updates on the GUI
|
||||
// thread occur in REASONABLE time-frames, and alert the user if not.
|
||||
try {
|
||||
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
SystemTray.logger.error("Event dispatch queue took longer than " + TIMEOUT + " seconds to shutdown. Please adjust " +
|
||||
"`SystemTray.TIMEOUT` to a value which better suites your environment.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
SystemTray.logger.error("Error waiting for shutdown dispatch to complete.", new Exception());
|
||||
}
|
||||
|
||||
Gtk.shutdownGui();
|
||||
|
||||
// uses EDT
|
||||
super.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public
|
||||
void setImage_(final File imageFile) {
|
||||
dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
AppIndicator.app_indicator_set_icon(appIndicator, imageFile.getAbsolutePath());
|
||||
|
||||
if (!isActive) {
|
||||
isActive = true;
|
||||
|
||||
AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
|
||||
|
||||
// kindof lame, but necessary for KDE
|
||||
if (Gtk.isKDE) {
|
||||
AppIndicator.app_indicator_set_label(appIndicator, "SystemTray", null);
|
||||
}
|
||||
|
||||
// now we have to setup a way for us to catch the "activation" click on this menu. Must be after the menu is set
|
||||
hookMenuOpen();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
((SwingSystemTrayMenuPopup) _native).setTitleBarImage(imageFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -33,13 +33,11 @@ import javax.swing.JPopupMenu;
|
||||
import com.sun.jna.NativeLong;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import dorkbox.systemTray.MenuEntry;
|
||||
import dorkbox.systemTray.SystemTray;
|
||||
import dorkbox.systemTray.linux.jna.GEventCallback;
|
||||
import dorkbox.systemTray.linux.jna.GdkEventButton;
|
||||
import dorkbox.systemTray.linux.jna.Gobject;
|
||||
import dorkbox.systemTray.linux.jna.Gtk;
|
||||
import dorkbox.systemTray.util.ImageUtils;
|
||||
import dorkbox.util.ScreenUtil;
|
||||
|
||||
/**
|
||||
@ -49,7 +47,7 @@ import dorkbox.util.ScreenUtil;
|
||||
* swing menu popup INSTEAD of GTK menu popups. The "golden standard" is our swing menu popup, since we have 100% control over it.
|
||||
*/
|
||||
public
|
||||
class GtkStatusIconTray extends SwingMenu {
|
||||
class GtkStatusIconTray extends SwingGenericTray {
|
||||
private volatile Pointer trayIcon;
|
||||
|
||||
// http://code.metager.de/source/xref/gnome/Platform/gtk%2B/gtk/deprecated/gtkstatusicon.c
|
||||
@ -72,7 +70,6 @@ class GtkStatusIconTray extends SwingMenu {
|
||||
throw new IllegalArgumentException("Unable to start GtkStatusIcon if 'SystemTray.FORCE_TRAY_TYPE' is set to AppIndicator");
|
||||
}
|
||||
|
||||
|
||||
JPopupMenu popupMenu = (JPopupMenu) _native;
|
||||
popupMenu.pack();
|
||||
popupMenu.setFocusable(true);
|
||||
@ -117,7 +114,6 @@ class GtkStatusIconTray extends SwingMenu {
|
||||
// they ALSO do not support tooltips, so we cater to the lowest common denominator
|
||||
// trayIcon.setToolTip(SwingSystemTray.this.appName);
|
||||
|
||||
ImageUtils.determineIconSize();
|
||||
Gtk.startGui();
|
||||
|
||||
Gtk.dispatch(new Runnable() {
|
||||
@ -131,6 +127,7 @@ class GtkStatusIconTray extends SwingMenu {
|
||||
@Override
|
||||
public
|
||||
void callback(Pointer notUsed, final GdkEventButton event) {
|
||||
// show the swing menu on the EDT
|
||||
// BUTTON_PRESS only (any mouse click)
|
||||
if (event.type == 4) {
|
||||
// show the swing menu on the EDT
|
||||
@ -214,6 +211,7 @@ class GtkStatusIconTray extends SwingMenu {
|
||||
|
||||
Gtk.shutdownGui();
|
||||
|
||||
// uses EDT
|
||||
super.remove();
|
||||
}
|
||||
}
|
||||
@ -232,53 +230,12 @@ class GtkStatusIconTray extends SwingMenu {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public
|
||||
String getStatus() {
|
||||
synchronized (menuEntries) {
|
||||
MenuEntry menuEntry = menuEntries.get(0);
|
||||
if (menuEntry instanceof SwingEntryStatus) {
|
||||
return menuEntry.getText();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
public
|
||||
void setStatus(final String statusText) {
|
||||
dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
synchronized (menuEntries) {
|
||||
// status is ALWAYS at 0 index...
|
||||
SwingEntry menuEntry = null;
|
||||
if (!menuEntries.isEmpty()) {
|
||||
menuEntry = (SwingEntry) menuEntries.get(0);
|
||||
}
|
||||
|
||||
if (menuEntry instanceof SwingEntryStatus) {
|
||||
// set the text or delete...
|
||||
|
||||
if (statusText == null) {
|
||||
// delete
|
||||
remove(menuEntry);
|
||||
}
|
||||
else {
|
||||
// set text
|
||||
menuEntry.setText(statusText);
|
||||
}
|
||||
|
||||
} else {
|
||||
// create a new one
|
||||
menuEntry = new SwingEntryStatus(GtkStatusIconTray.this, statusText);
|
||||
// status is ALWAYS at 0 index...
|
||||
menuEntries.add(0, menuEntry);
|
||||
}
|
||||
}
|
||||
((SwingSystemTrayMenuPopup) _native).setTitleBarImage(iconFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
77
src/dorkbox/systemTray/swing/SwingGenericTray.java
Normal file
77
src/dorkbox/systemTray/swing/SwingGenericTray.java
Normal file
@ -0,0 +1,77 @@
|
||||
package dorkbox.systemTray.swing;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import dorkbox.systemTray.Menu;
|
||||
import dorkbox.systemTray.MenuEntry;
|
||||
import dorkbox.systemTray.SystemTray;
|
||||
import dorkbox.systemTray.util.ImageUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract
|
||||
class SwingGenericTray extends SwingMenu {
|
||||
/**
|
||||
* Called in the EDT
|
||||
*
|
||||
* @param systemTray
|
||||
* the system tray (which is the object that sits in the system tray)
|
||||
* @param parent
|
||||
* @param _native
|
||||
*/
|
||||
SwingGenericTray(final SystemTray systemTray, final Menu parent, final JComponent _native) {
|
||||
super(systemTray, parent, _native);
|
||||
|
||||
ImageUtils.determineIconSize();
|
||||
}
|
||||
|
||||
public
|
||||
String getStatus() {
|
||||
synchronized (menuEntries) {
|
||||
MenuEntry menuEntry = menuEntries.get(0);
|
||||
if (menuEntry instanceof SwingEntryStatus) {
|
||||
return menuEntry.getText();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public
|
||||
void setStatus(final String statusText) {
|
||||
final SwingMenu _this = this;
|
||||
dispatchAndWait(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
synchronized (menuEntries) {
|
||||
// status is ALWAYS at 0 index...
|
||||
SwingEntry menuEntry = null;
|
||||
if (!menuEntries.isEmpty()) {
|
||||
menuEntry = (SwingEntry) menuEntries.get(0);
|
||||
}
|
||||
|
||||
if (menuEntry instanceof SwingEntryStatus) {
|
||||
// set the text or delete...
|
||||
|
||||
if (statusText == null) {
|
||||
// delete
|
||||
remove(menuEntry);
|
||||
}
|
||||
else {
|
||||
// set text
|
||||
menuEntry.setText(statusText);
|
||||
}
|
||||
|
||||
} else {
|
||||
// create a new one
|
||||
menuEntry = new SwingEntryStatus(_this, statusText);
|
||||
// status is ALWAYS at 0 index...
|
||||
menuEntries.add(0, menuEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@ import javax.swing.ImageIcon;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import dorkbox.systemTray.MenuEntry;
|
||||
import dorkbox.systemTray.util.ImageUtils;
|
||||
import dorkbox.util.ScreenUtil;
|
||||
|
||||
/**
|
||||
@ -43,7 +42,7 @@ import dorkbox.util.ScreenUtil;
|
||||
*/
|
||||
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
|
||||
public
|
||||
class SwingSystemTray extends SwingMenu {
|
||||
class SwingSystemTray extends SwingGenericTray {
|
||||
volatile SystemTray tray;
|
||||
volatile TrayIcon trayIcon;
|
||||
|
||||
@ -52,8 +51,6 @@ class SwingSystemTray extends SwingMenu {
|
||||
SwingSystemTray(final dorkbox.systemTray.SystemTray systemTray) {
|
||||
super(systemTray, null, new SwingSystemTrayMenuPopup());
|
||||
|
||||
ImageUtils.determineIconSize();
|
||||
|
||||
SwingSystemTray.this.tray = SystemTray.getSystemTray();
|
||||
}
|
||||
|
||||
@ -77,55 +74,6 @@ class SwingSystemTray extends SwingMenu {
|
||||
});
|
||||
}
|
||||
|
||||
public
|
||||
String getStatus() {
|
||||
synchronized (menuEntries) {
|
||||
MenuEntry menuEntry = menuEntries.get(0);
|
||||
if (menuEntry instanceof SwingEntryStatus) {
|
||||
return menuEntry.getText();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
public
|
||||
void setStatus(final String statusText) {
|
||||
dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
synchronized (menuEntries) {
|
||||
// status is ALWAYS at 0 index...
|
||||
SwingEntry menuEntry = null;
|
||||
if (!menuEntries.isEmpty()) {
|
||||
menuEntry = (SwingEntry) menuEntries.get(0);
|
||||
}
|
||||
|
||||
if (menuEntry instanceof SwingEntryStatus) {
|
||||
// set the text or delete...
|
||||
|
||||
if (statusText == null) {
|
||||
// delete
|
||||
remove(menuEntry);
|
||||
}
|
||||
else {
|
||||
// set text
|
||||
menuEntry.setText(statusText);
|
||||
}
|
||||
|
||||
} else {
|
||||
// create a new one
|
||||
menuEntry = new SwingEntryStatus(SwingSystemTray.this, statusText);
|
||||
// status is ALWAYS at 0 index...
|
||||
menuEntries.add(0, menuEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public
|
||||
void setImage_(final File iconFile) {
|
||||
dispatch(new Runnable() {
|
||||
@ -185,14 +133,14 @@ class SwingSystemTray extends SwingMenu {
|
||||
|
||||
try {
|
||||
tray.add(trayIcon);
|
||||
((SwingSystemTrayMenuPopup) _native).setIcon(iconFile);
|
||||
} catch (AWTException e) {
|
||||
dorkbox.systemTray.SystemTray.logger.error("TrayIcon could not be added.", e);
|
||||
}
|
||||
} else {
|
||||
((SwingSystemTrayMenuPopup) _native).setIcon(iconFile);
|
||||
trayIcon.setImage(trayImage);
|
||||
}
|
||||
|
||||
((SwingSystemTrayMenuPopup) _native).setTitleBarImage(iconFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import dorkbox.util.OS;
|
||||
*
|
||||
* This is our "golden standard" since we have 100% control over it.
|
||||
*/
|
||||
public
|
||||
class SwingSystemTrayMenuPopup extends JPopupMenu {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -47,6 +48,7 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
|
||||
private volatile File iconFile;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public
|
||||
SwingSystemTrayMenuPopup() {
|
||||
super();
|
||||
setFocusable(true);
|
||||
@ -113,24 +115,25 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon for the title-bar, so IF it shows in the task-bar, it will have the corresponding icon as the SystemTray icon
|
||||
* Sets the image for the title-bar, so IF it shows in the task-bar, it will have the corresponding image as the SystemTray image
|
||||
*/
|
||||
void setIcon(final File iconFile) {
|
||||
if (this.iconFile == null || !this.iconFile.equals(iconFile)) {
|
||||
this.iconFile = iconFile;
|
||||
void setTitleBarImage(final File imageFile) {
|
||||
if (this.iconFile == null || !this.iconFile.equals(imageFile)) {
|
||||
this.iconFile = imageFile;
|
||||
|
||||
try {
|
||||
Image image = new ImageIcon(ImageIO.read(iconFile)).getImage();
|
||||
Image image = new ImageIcon(ImageIO.read(imageFile)).getImage();
|
||||
image.flush();
|
||||
|
||||
// we set the dialog window to have the same icon as what is on the system tray
|
||||
hiddenDialog.setIconImage(image);
|
||||
} catch (IOException e) {
|
||||
SystemTray.logger.error("Error setting the icon for the popup menu task tray dialog");
|
||||
SystemTray.logger.error("Error setting the title-bar image for the popup menu task tray dialog");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public
|
||||
void doShow(final int x, final int y) {
|
||||
// critical to get the keyboard listeners working for the popup menu
|
||||
setInvoker(hiddenDialog.getContentPane());
|
||||
|
Loading…
Reference in New Issue
Block a user