(menuEntries);
- menuEntries.clear();
- }
-
- for (Entry entry : copy) {
- entry.remove();
- }
- }
-
-
/**
* This removes all menu entries from this menu AND this menu from it's parent
*/
@Override
public
void remove() {
- clear();
+ synchronized (menuEntries) {
+ menuEntries.clear();
+ }
super.remove();
}
diff --git a/src/dorkbox/systemTray/MenuItem.java b/src/dorkbox/systemTray/MenuItem.java
index a0bc7be..f00e10c 100644
--- a/src/dorkbox/systemTray/MenuItem.java
+++ b/src/dorkbox/systemTray/MenuItem.java
@@ -33,6 +33,8 @@ import dorkbox.util.SwingUtil;
@SuppressWarnings({"unused", "SameParameterValue", "WeakerAccess"})
public
class MenuItem extends Entry {
+ private static boolean alreadyEmittedTooltipWarning = false;
+
private volatile String text;
private volatile File imageFile;
private volatile ActionListener callback;
@@ -40,6 +42,7 @@ class MenuItem extends Entry {
// default enabled is always true
private volatile boolean enabled = true;
private volatile char mnemonicKey;
+ private volatile String tooltip;
public
MenuItem() {
@@ -133,6 +136,7 @@ class MenuItem extends Entry {
peer.setText(this);
peer.setCallback(this);
peer.setShortcut(this);
+ peer.setTooltip(this);
}
protected
@@ -343,15 +347,44 @@ class MenuItem extends Entry {
}
}
- @Override
+ /**
+ * Specifies the tooltip text, usually this is used to brand the SystemTray icon with your product's name, or to provide extra
+ * information during mouse-over for menu entries.
+ *
+ * NOTE: Maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop Environments.
+ *
+ * For more details on Linux see https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12.
+ *
+ * @param tooltipText the text to use as a mouse-over tooltip for the tray icon or menu entry, null to remove.
+ */
public
- void remove() {
- if (peer != null) {
- setImage_(null);
- setText(null);
- setCallback(null);
+ void setTooltip(final String tooltipText) {
+ if (tooltipText != null) {
+ // this is a safety precaution, since the behavior of really long text is undefined.
+ if (tooltipText.length() > 64) {
+ throw new RuntimeException("Tooltip text cannot be longer than 64 characters.");
+ }
+
+ if (!alreadyEmittedTooltipWarning) {
+ alreadyEmittedTooltipWarning = true;
+ SystemTray.logger.warn("Tooltips are not consistent across all platforms and tray types.");
+ }
}
- super.remove();
+ this.tooltip = tooltipText;
+
+ if (peer != null) {
+ ((MenuItemPeer) peer).setTooltip(this);
+ }
+ }
+
+ /**
+ * Gets the mouse-over tooltip for the meme entry.
+ *
+ * NOTE: This is not consistent across all platforms and tray types.
+ */
+ public
+ String getTooltip() {
+ return this.tooltip;
}
}
diff --git a/src/dorkbox/systemTray/Separator.java b/src/dorkbox/systemTray/Separator.java
index f4f5e63..894941e 100644
--- a/src/dorkbox/systemTray/Separator.java
+++ b/src/dorkbox/systemTray/Separator.java
@@ -15,6 +15,8 @@
*/
package dorkbox.systemTray;
+import dorkbox.systemTray.peer.SeparatorPeer;
+
/**
* This represents a common menu-spacer entry, that is cross platform in nature.
*
@@ -39,4 +41,14 @@ class Separator extends Entry {
Separator() {
super();
}
+
+ /**
+ * @param peer the platform specific implementation for all actions for this type
+ * @param parent the parent of this menu, null if the parent is the system tray
+ * @param systemTray the system tray (which is the object that sits in the system tray)
+ */
+ public
+ void bind(final SeparatorPeer peer, final Menu parent, final SystemTray systemTray) {
+ super.bind(peer, parent, systemTray);
+ }
}
diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java
index 478660f..9db6573 100644
--- a/src/dorkbox/systemTray/SystemTray.java
+++ b/src/dorkbox/systemTray/SystemTray.java
@@ -48,19 +48,22 @@ import dorkbox.systemTray.nativeUI._WindowsNativeTray;
import dorkbox.systemTray.swingUI.SwingUIFactory;
import dorkbox.systemTray.swingUI._SwingTray;
import dorkbox.systemTray.util.ImageResizeUtil;
-import dorkbox.systemTray.util.JavaFX;
import dorkbox.systemTray.util.LinuxSwingUI;
import dorkbox.systemTray.util.SizeAndScalingUtil;
-import dorkbox.systemTray.util.Swt;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.systemTray.util.WindowsSwingUI;
import dorkbox.util.CacheUtil;
import dorkbox.util.IO;
+import dorkbox.util.JavaFX;
import dorkbox.util.OS;
import dorkbox.util.OSUtil;
import dorkbox.util.Property;
import dorkbox.util.SwingUtil;
-import sun.security.action.GetPropertyAction;
+import dorkbox.util.Swt;
+import dorkbox.util.jna.linux.AppIndicator;
+import dorkbox.util.jna.linux.Gtk;
+import dorkbox.util.jna.linux.GtkCheck;
+import dorkbox.util.jna.linux.GtkEventDispatch;
/**
@@ -99,6 +102,10 @@ class SystemTray {
/** Forces the system tray to always choose GTK2 (even when GTK3 might be available). */
public static boolean FORCE_GTK2 = false;
+ @Property
+ /** Prefer to load GTK3 before trying to load GTK2. */
+ public static boolean PREFER_GTK3 = true;
+
@Property
/**
* Forces the system tray detection to be AutoDetect, GtkStatusIcon, AppIndicator, Swing, or AWT.
@@ -126,6 +133,15 @@ class SystemTray {
*/
public static boolean AUTO_FIX_INCONSISTENCIES = true;
+ @Property
+ /**
+ * Allows the SystemTray logic to ignore if root is detected. Usually when running as root it won't work (because of how DBUS
+ * operates), but in rare situations, it might work.
+ *
+ * This is an advanced feature, and it is recommended to leave as true
+ */
+ public static boolean ENABLE_ROOT_CHECK = true;
+
@Property
/**
* Allows a custom look and feel for the Swing UI, if defined. See the test example for specific use.
@@ -136,41 +152,12 @@ class SystemTray {
/**
* This property is provided for debugging any errors in the logic used to determine the system-tray type.
*/
- public static boolean DEBUG = true;
+ public static boolean DEBUG = false;
private static volatile SystemTray systemTray = null;
private static volatile Tray systemTrayMenu = null;
- public final static boolean isJavaFxLoaded;
- public final static boolean isSwtLoaded;
-
-
- static {
- boolean isJavaFxLoaded_ = false;
- boolean isSwtLoaded_ = false;
- try {
- // this is important to use reflection, because if JavaFX is not being used, calling getToolkit() will initialize it...
- java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
- m.setAccessible(true);
- ClassLoader cl = ClassLoader.getSystemClassLoader();
-
- // JavaFX Java7,8 is GTK2 only. Java9 can have it be GTK3 if -Djdk.gtk.version=3 is specified
- // see http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
- isJavaFxLoaded_ = (null != m.invoke(cl, "com.sun.javafx.tk.Toolkit")) || (null != m.invoke(cl, "javafx.application.Application"));
-
- // maybe we should load the SWT version? (In order for us to work with SWT, BOTH must be the same!!
- // SWT is GTK2, but if -DSWT_GTK3=1 is specified, it can be GTK3
- isSwtLoaded_ = null != m.invoke(cl, "org.eclipse.swt.widgets.Display");
- } catch (Throwable e) {
- if (DEBUG) {
- logger.debug("Error detecting javaFX/SWT mode", e);
- }
- }
-
- isJavaFxLoaded = isJavaFxLoaded_;
- isSwtLoaded = isSwtLoaded_;
- }
private static
boolean isTrayType(final Class extends Tray> tray, final TrayType trayType) {
@@ -247,7 +234,7 @@ class SystemTray {
OSUtil.DesktopEnv.Env de = OSUtil.DesktopEnv.get();
if (DEBUG) {
- logger.debug("Currently using the '{}' desktop environment", de);
+ logger.debug("Currently using the '{}' desktop environment" + OS.LINE_SEPARATOR + OSUtil.Linux.getInfo(), de);
}
switch (de) {
@@ -288,11 +275,21 @@ class SystemTray {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("default".equalsIgnoreCase(GDM)) {
- // this can be gnome3 on debian
+ Tray.usingGnome = true;
+ // this can be gnome3 on debian/kali
- if (OSUtil.Linux.isDebian()) {
- // note: Debian Gnome3 does NOT work! (tested on Debian 8.5 and 8.6 default installs)
- logger.warn("Debian with Gnome detected. SystemTray support is not known to work.");
+ if (OSUtil.Linux.isKali()) {
+ return selectTypeQuietly(TrayType.GtkStatusIcon);
+ }
+
+ if (OSUtil.Linux.isDebian() && Extension.ENABLE_EXTENSION_INSTALL) {
+ logger.warn("Debian with Gnome detected. SystemTray works, but will only show via SUPER+M.");
+
+ if (DEBUG) {
+ logger.debug("Disabling the extension install. It won't work.");
+ }
+
+ Extension.ENABLE_EXTENSION_INSTALL = false;
}
return selectTypeQuietly(TrayType.GtkStatusIcon);
@@ -304,6 +301,13 @@ class SystemTray {
return selectTypeQuietly(TrayType.GtkStatusIcon);
}
else if ("ubuntu".equalsIgnoreCase(GDM)) {
+ // ubuntu 17.10 uses the NEW gnome DE, which screws up previous Ubuntu workarounds, since it's now proper Gnome
+ int[] version = OSUtil.Linux.getUbuntuVersion();
+ if (version[0] > 17 || (version[0] == 17 && version[1] == 10)) {
+ // this is gnome 3.26.1
+ logger.debug("This Ubuntu 17.10 is not yet supported!");
+ }
+
return selectTypeQuietly(TrayType.AppIndicator);
}
break;
@@ -324,6 +328,10 @@ class SystemTray {
// Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell".
return selectTypeQuietly(TrayType.AppIndicator);
}
+ case Unity7: {
+ // Ubuntu Unity7 (17.04+, which has MIR) is a weird combination. It's "Gnome", but it's not "Gnome Shell".
+ return selectTypeQuietly(TrayType.AppIndicator);
+ }
case XFCE: {
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
// see: https://ask.fedoraproject.org/en/question/23116/how-to-fix-missing-icons-in-program-menus-and-context-menus/
@@ -343,6 +351,12 @@ class SystemTray {
// With eOS, we CANNOT show the spacer image. It does not work.
return selectTypeQuietly(TrayType.AppIndicator);
}
+ case ChromeOS:
+ // ChromeOS cannot use the swing tray (ChromeOS is not supported!), nor AppIndicaitor/GtkStatusIcon, as those
+ // libraries do not exist on ChromeOS. Additionally, Java cannot load external libraries unless they are in /bin,
+ // BECAUSE of the `noexec` bit set. If JNA is moved into /bin, and the JNA library is specified to load from that
+ // location, we can use JNA.
+ return null;
}
// Try to autodetect if we can use app indicators (or if we need to fallback to GTK indicators)
@@ -394,6 +408,19 @@ class SystemTray {
logger.error("Error detecting gnome version", e);
}
}
+
+ if (OS.isLinux()) {
+ // now just blanket query what we are to guess...
+ if (OSUtil.Linux.isUbuntu()) {
+ return selectTypeQuietly(TrayType.AppIndicator);
+ }
+ else if (OSUtil.Linux.isFedora()) {
+ return selectTypeQuietly(TrayType.AppIndicator);
+ } else {
+ // AppIndicators are now the "default" for most linux distro's.
+ return selectTypeQuietly(TrayType.AppIndicator);
+ }
+ }
}
return null;
@@ -406,8 +433,6 @@ class SystemTray {
return;
}
- systemTray = new SystemTray();
-
// if (DEBUG) {
// Properties properties = System.getProperties();
// for (Map.Entry entry : properties.entrySet()) {
@@ -441,7 +466,7 @@ class SystemTray {
// cannot mix Swing/AWT and JavaFX for MacOSX in java7 (fixed in java8) without special stuff.
// https://bugs.openjdk.java.net/browse/JDK-8116017
// https://bugs.openjdk.java.net/browse/JDK-8118714
- if (isJavaFxLoaded && OS.javaVersion <= 7 && !System.getProperty("javafx.macosx.embedded", "false").equals("true")) {
+ if (JavaFX.isLoaded && OS.javaVersion <= 7 && !System.getProperty("javafx.macosx.embedded", "false").equals("true")) {
logger.error("MacOSX JavaFX (Java7) is incompatible with the SystemTray by default. See issue: " +
"'https://bugs.openjdk.java.net/browse/JDK-8116017' and 'https://bugs.openjdk.java.net/browse/JDK-8118714'\n" +
@@ -459,7 +484,7 @@ class SystemTray {
// cannot mix Swing and SWT on MacOSX (for all versions of java) so we force native menus instead, which work just fine with SWT
// http://mail.openjdk.java.net/pipermail/bsd-port-dev/2008-December/000173.html
- if (isSwtLoaded && FORCE_TRAY_TYPE == TrayType.Swing) {
+ if (Swt.isLoaded && FORCE_TRAY_TYPE == TrayType.Swing) {
if (AUTO_FIX_INCONSISTENCIES) {
logger.warn("Unable to load Swing + SWT (for all versions of Java). Using the AWT Tray type instead.");
@@ -483,40 +508,57 @@ class SystemTray {
}
}
else if (isNix) {
- // linux/unix can use all of the tray types
+ // linux/unix can use all of the tray types. AWT looks horrid. GTK versions are really sensitive...
- // NOTE: if the UI uses the 'getSystemLookAndFeelClassName' and is on Linux, this will cause GTK2 to get loaded first,
- // which will cause conflicts if one tries to use GTK3
- if (!FORCE_GTK2 && !isJavaFxLoaded && !isSwtLoaded) {
+ // this checks to see if Swing/SWT/JavaFX has loaded GTK yet, and if so, what version they loaded.
+ int loadedGtkVersion = GtkCheck.getLoadedGtkVersion();
+ if (loadedGtkVersion == 2) {
+ if (AUTO_FIX_INCONSISTENCIES) {
+ if (!FORCE_GTK2) {
+ if (JavaFX.isLoaded) {
+ // JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
+ // see
+ // http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
+ // https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
+ // from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
- String currentUI = UIManager.getLookAndFeel()
- .getClass()
- .getName();
+ // we must use GTK2, because JavaFX is GTK2
+ FORCE_GTK2 = true;
+ if (DEBUG) {
+ logger.debug("Forcing GTK2 because JavaFX is GTK2");
+ }
+ }
+ else if (Swt.isLoaded) {
+ // Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
+ // System.setProperty("SWT_GTK3", "0");
- boolean mustForceGtk2 = false;
+ // we must use GTK2, because SWT is GTK2
+ FORCE_GTK2 = true;
+ if (DEBUG) {
+ logger.debug("Forcing GTK2 because SWT is GTK2");
+ }
+ }
+ else {
+ // we are NOT using javaFX/SWT and our UI is GTK2 and we want GTK3
+ // JavaFX/SWT can be GTK3, but Swing is not GTK3.
- // GTKLookAndFeel.class.getCanonicalName()
- if (currentUI.equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")) {
- // this means our look and feel is the GTK look and feel... THIS CREATES PROBLEMS!
-
- // THIS IS NOT DOCUMENTED ANYWHERE...
- String swingGtkVersion = java.security.AccessController.doPrivileged(new GetPropertyAction("swing.gtk.version"));
- mustForceGtk2 = swingGtkVersion == null || swingGtkVersion.startsWith("2");
- }
-
- if (mustForceGtk2) {
- // we are NOT using javaFX/SWT and our UI is GTK2 and we want GTK3
- // JavaFX/SWT can be GTK3, but Swing can not be GTK3.
-
- if (AUTO_FIX_INCONSISTENCIES) {
- // we must use GTK2 because Swing is configured to use GTK2
- FORCE_GTK2 = true;
-
- logger.warn("Forcing GTK2 because the Swing UIManager is GTK2");
+ // we must use GTK2 because Java is configured to use GTK2
+ FORCE_GTK2 = true;
+ if (DEBUG) {
+ logger.debug("Forcing GTK2 because Java has already loaded GTK2");
+ }
+ }
} else {
- logger.error("Unable to use the SystemTray when the Swing UIManager is configured to use the native L&F, which " +
- "uses GTK2. This is incompatible with GTK3. " +
- "Please set `SystemTray.AUTO_FIX_INCONSISTENCIES=true;` to automatically fix this problem.");
+ // we are already forcing GTK2, so no extra actions necessary
+ }
+ } else {
+ // !AUTO_FIX_INCONSISTENCIES
+
+ if (!FORCE_GTK2) {
+ // clearly the app developer did not want us to automatically fix anything, and have not correctly specified how
+ // to load GTK, so abort with an error message.
+ logger.error("Unable to use the SystemTray when there is a mismatch for GTK loaded preferences. Please correctly " +
+ "set `SystemTray.FORCE_GTK2=true` or set `SystemTray.AUTO_FIX_INCONSISTENCIES=true`. Aborting...");
systemTrayMenu = null;
systemTray = null;
@@ -524,63 +566,124 @@ class SystemTray {
}
}
}
- else if (isSwtLoaded) {
- // Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
- // System.setProperty("SWT_GTK3", "0");
+ else if (loadedGtkVersion == 3) {
+ if (AUTO_FIX_INCONSISTENCIES) {
+ if (JavaFX.isLoaded) {
+ // JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
+ // see
+ // http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
+ // https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
+ // from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
- // was SWT forced?
- String swt_gtk3 = System.getProperty("SWT_GTK3");
- boolean isSwt_GTK3 = swt_gtk3 != null && !swt_gtk3.equals("0");
- if (!isSwt_GTK3) {
- // check a different property
- String property = System.getProperty("org.eclipse.swt.internal.gtk.version");
- isSwt_GTK3 = property != null && !property.startsWith("2.");
- }
+ if (FORCE_GTK2) {
+ // if we are java9, then we can change it -- otherwise we cannot.
+ if (OS.javaVersion == 9) {
+ FORCE_GTK2 = false;
+ logger.warn("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is " +
+ "configured to use GTK2. Please configure JavaFX to use GTK2 (via `System.setProperty(\"jdk.gtk.version\", \"3\");`) " +
+ "before JavaFX is initialized, or set `SystemTray.FORCE_GTK2=false;` Undoing `FORCE_GTK2`.");
- if (isSwt_GTK3 && FORCE_GTK2) {
- logger.error("Unable to use the SystemTray when SWT is configured to use GTK3 and the SystemTray is configured to use " +
- "GTK2. Please configure SWT to use GTK2, via `System.setProperty(\"SWT_GTK3\", \"0\");` before SWT is " +
- "initialized, or set `SystemTray.FORCE_GTK2=false;`");
+ }
+ }
- systemTrayMenu = null;
- systemTray = null;
- return;
- } else if (!isSwt_GTK3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) {
- // we must use GTK2, because SWT is GTK2
- FORCE_GTK2 = true;
+ if (!PREFER_GTK3) {
+ // we should use GTK3, since that is what is already loaded
+ PREFER_GTK3 = true;
+ if (DEBUG) {
+ logger.debug("Preferring GTK3 even though specified otherwise, because JavaFX is GTK3");
+ }
+ }
+ }
+ else if (Swt.isLoaded) {
+ // Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
+ // System.setProperty("SWT_GTK3", "0");
- logger.warn("Forcing GTK2 because SWT is GTK2");
- }
- }
- else if (isJavaFxLoaded) {
- // JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
- // see
- // http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
- // https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
- // from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
- boolean isJava_GTK3_Possible = OS.javaVersion >= 9 && System.getProperty("jdk.gtk.version", "2").equals("3");
- if (isJava_GTK3_Possible && FORCE_GTK2) {
- // if we are java9, then we can change it -- otherwise we cannot.
- if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) {
- FORCE_GTK2 = false;
+ if (FORCE_GTK2) {
+ FORCE_GTK2 = false;
+ logger.warn("Unable to use the SystemTray when SWT is configured to use GTK3 and the SystemTray is configured to use " +
+ "GTK2. Please configure SWT to use GTK2, via `System.setProperty(\"SWT_GTK3\", \"0\");` before SWT is " +
+ "initialized, or set `SystemTray.FORCE_GTK2=false;`");
+ }
- logger.warn("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is " +
- "configured to use GTK2. Please configure JavaFX to use GTK2 (via `System.setProperty(\"jdk.gtk.version\", \"3\");`) " +
- "before JavaFX is initialized, or set `SystemTray.FORCE_GTK2=false;` Undoing `FORCE_GTK2`.");
+ if (!PREFER_GTK3) {
+ // we should use GTK3, since that is what is already loaded
+ PREFER_GTK3 = true;
+ if (DEBUG) {
+ logger.debug("Preferring GTK3 even though specified otherwise, because SWT is GTK3");
+ }
+ }
+ }
+ else {
+ // we are NOT using javaFX/SWT and our UI is GTK3 and we want GTK3
+ // JavaFX/SWT can be GTK3, but Swing is (maybe in the future?) GTK3.
- } else {
- logger.error("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is configured to use " +
- "GTK2. Please set `SystemTray.FORCE_GTK2=false;` if that is not possible then it will not work.");
+ if (FORCE_GTK2) {
+ FORCE_GTK2 = false;
+ logger.warn("Unable to use the SystemTray when Swing is configured to use GTK3 and the SystemTray is " +
+ "configured to use GTK2. Undoing `FORCE_GTK2.");
+ }
+
+ if (!PREFER_GTK3) {
+ // we should use GTK3, since that is what is already loaded
+ PREFER_GTK3 = true;
+ if (DEBUG) {
+ logger.debug("Preferring GTK3 even though specified otherwise, because Java has already loaded GTK3");
+ }
+ }
+ }
+
+ } else {
+ // !AUTO_FIX_INCONSISTENCIES
+
+ if (JavaFX.isLoaded) {
+ // JavaFX Java7,8 is GTK2 only. Java9 can MAYBE have it be GTK3 if `-Djdk.gtk.version=3` is specified
+ // see
+ // http://mail.openjdk.java.net/pipermail/openjfx-dev/2016-May/019100.html
+ // https://docs.oracle.com/javafx/2/system_requirements_2-2-3/jfxpub-system_requirements_2-2-3.htm
+ // from the page: JavaFX 2.2.3 for Linux requires gtk2 2.18+.
+
+ if (FORCE_GTK2) {
+ // if we are java9, then we can change it -- otherwise we cannot.
+ if (OS.javaVersion == 9) {
+ logger.error("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is " +
+ "configured to use GTK2. Please configure JavaFX to use GTK2 (via `System.setProperty(\"jdk.gtk.version\", \"3\");`) " +
+ "before JavaFX is initialized, or set `SystemTray.FORCE_GTK2=false;` Aborting.");
+
+ }
+ else {
+ logger.error("Unable to use the SystemTray when JavaFX is configured to use GTK3 and the SystemTray is configured to use " +
+ "GTK2. Please set `SystemTray.FORCE_GTK2=false;` Aborting.");
+ }
+
+ systemTrayMenu = null;
+ systemTray = null;
+ return;
+ }
+
+ }
+ else if (Swt.isLoaded) {
+ // Necessary for us to work with SWT based on version info. We can try to set us to be compatible with whatever it is set to
+ // System.setProperty("SWT_GTK3", "0");
+
+ if (FORCE_GTK2) {
+ logger.error("Unable to use the SystemTray when SWT is configured to use GTK3 and the SystemTray is configured to use " +
+ "GTK2. Please configure SWT to use GTK2, via `System.setProperty(\"SWT_GTK3\", \"0\");` before SWT is " +
+ "initialized, or set `SystemTray.FORCE_GTK2=false;`");
+
+ systemTrayMenu = null;
+ systemTray = null;
+ return;
+ }
+ }
+
+ else if (FORCE_GTK2) {
+ logger.error("Unable to use the SystemTray when Swing is configured to use GTK3 and the SystemTray is " +
+ "configured to use GTK2. Aborting.");
systemTrayMenu = null;
systemTray = null;
return;
}
- } else if (!isJava_GTK3_Possible && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) {
- // we must use GTK2, because JavaFX is GTK2
- FORCE_GTK2 = true;
-
- logger.warn("Forcing GTK2 because JavaFX is GTK2");
}
}
}
@@ -598,8 +701,8 @@ class SystemTray {
logger.debug("Is Auto sizing tray/menu? {}", AUTO_SIZE);
- logger.debug("Is JavaFX detected? {}", isJavaFxLoaded);
- logger.debug("Is SWT detected? {}", isSwtLoaded);
+ logger.debug("Is JavaFX detected? {}", JavaFX.isLoaded);
+ logger.debug("Is SWT detected? {}", Swt.isLoaded);
logger.debug("Java Swing L&F: {}", UIManager.getLookAndFeel().getID());
if (FORCE_TRAY_TYPE == TrayType.AutoDetect) {
logger.debug("Auto-detecting tray type");
@@ -607,7 +710,8 @@ class SystemTray {
else {
logger.debug("Forced tray type: {}", FORCE_TRAY_TYPE.name());
}
- logger.debug("FORCE_GTK2: {}", FORCE_GTK2);
+ logger.debug("Force GTK2: {}", FORCE_GTK2);
+ logger.debug("Prefer GTK3: {}", PREFER_GTK3);
}
// Note: AppIndicators DO NOT support tooltips. We could try to create one, by creating a GTK widget and attaching it on
@@ -623,48 +727,70 @@ class SystemTray {
trayType = selectTypeQuietly(SystemTray.FORCE_TRAY_TYPE);
}
+ if (trayType == null && OSUtil.DesktopEnv.isChromeOS()) {
+ logger.error("ChromeOS detected and it is not supported. Aborting.");
- // fix various incompatibilities
+ systemTrayMenu = null;
+ systemTray = null;
+ return;
+ }
+
+
+ // fix various incompatibilities with selected tray types
if (isNix) {
// Ubuntu UNITY has issues with GtkStatusIcon (it won't work at all...)
- if (isTrayType(trayType, TrayType.GtkStatusIcon) && OSUtil.DesktopEnv.get() == OSUtil.DesktopEnv.Env.Unity && OSUtil.Linux.isUbuntu()) {
- if (AUTO_FIX_INCONSISTENCIES) {
- // GTK2 does not support AppIndicators!
- if (Gtk.isGtk2) {
- trayType = selectTypeQuietly(TrayType.Swing);
- logger.warn("Forcing Swing Tray type because Ubuntu Unity display environment removed support for GtkStatusIcons " +
- "and GTK2+ was specified.");
+ if (isTrayType(trayType, TrayType.GtkStatusIcon)) {
+ OSUtil.DesktopEnv.Env de = OSUtil.DesktopEnv.get();
+
+ if (OSUtil.DesktopEnv.isUnity(de) && OSUtil.Linux.isUbuntu()) {
+ if (AUTO_FIX_INCONSISTENCIES) {
+ // GTK2 does not support AppIndicators!
+ if (Gtk.isGtk2) {
+ trayType = selectTypeQuietly(TrayType.Swing);
+ logger.warn("Forcing Swing Tray type because Ubuntu Unity display environment removed support for GtkStatusIcons " +
+ "and GTK2+ was specified.");
+ }
+ else {
+ // we must use AppIndicator because Ubuntu Unity removed GtkStatusIcon support
+ SystemTray.FORCE_TRAY_TYPE = TrayType.AppIndicator; // this is required because of checks inside of AppIndicator...
+ trayType = selectTypeQuietly(TrayType.AppIndicator);
+
+ logger.warn("Forcing AppIndicator because Ubuntu Unity display environment removed support for GtkStatusIcons.");
+ }
}
else {
- // we must use AppIndicator because Ubuntu Unity removed GtkStatusIcon support
- SystemTray.FORCE_TRAY_TYPE = TrayType.AppIndicator; // this is required because of checks inside of AppIndicator...
- trayType = selectTypeQuietly(TrayType.AppIndicator);
+ logger.error("Unable to use the GtkStatusIcons when running on Ubuntu with the Unity display environment, and thus" +
+ " the SystemTray will not work. " +
+ "Please set `SystemTray.AUTO_FIX_INCONSISTENCIES=true;` to automatically fix this problem.");
- logger.warn("Forcing AppIndicator because Ubuntu Unity display environment removed support for GtkStatusIcons.");
+ systemTrayMenu = null;
+ systemTray = null;
+ return;
}
}
- else {
- logger.error("Unable to use the GtkStatusIcons when running on Ubuntu with the Unity display environment, and thus" +
- " the SystemTray will not work. " +
- "Please set `SystemTray.AUTO_FIX_INCONSISTENCIES=true;` to automatically fix this problem.");
- systemTrayMenu = null;
- systemTray = null;
- return;
+ if (de == OSUtil.DesktopEnv.Env.Gnome && (OSUtil.Linux.isKali() || OSUtil.Linux.isFedora())) {
+ // Fedora and Kali linux has some WEIRD graphical oddities via GTK3. GTK2 looks just fine.
+ PREFER_GTK3 = false;
+
+ if (DEBUG) {
+ logger.debug("Preferring GTK2 because this OS has weird graphical issues with GTK3 status icons");
+ }
}
}
- if (isTrayType(trayType, TrayType.AppIndicator) && OSUtil.Linux.isRoot()) {
+ if (SystemTray.ENABLE_ROOT_CHECK && isTrayType(trayType, TrayType.AppIndicator) && OSUtil.Linux.isRoot()) {
// if are we running as ROOT, there can be issues (definitely on Ubuntu 16.04, maybe others)!
if (AUTO_FIX_INCONSISTENCIES) {
trayType = selectTypeQuietly(TrayType.Swing);
logger.warn("Attempting to load the SystemTray as the 'root/sudo' user. This will likely not work because of dbus " +
- "restrictions. Using the Swing Tray type instead.");
+ "restrictions. Using the Swing Tray type instead. Please refer to the readme notes or issue #63 on " +
+ "how to work around this.");
} else {
logger.error("Attempting to load the SystemTray as the 'root/sudo' user. This will likely NOT WORK because of dbus " +
- "restrictions.");
+ "restrictions. Please refer to the readme notes or issue #63 on how to work around this.");
}
}
}
@@ -683,78 +809,84 @@ class SystemTray {
// - swing version loads as an image (which can be stream or path, we use path)
CacheUtil.tempDir = "SystemTrayImages";
+
try {
// at this point, the tray type is what it should be. If there are failures or special cases, all types will fall back to
// Swing.
if (isNix) {
- // NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3.
- // appindicator3 doesn't support menu icons via GTK2!!
- if (!Gtk.isLoaded) {
- trayType = selectTypeQuietly(TrayType.Swing);
+ // linux/unix need access to GTK, so load it up before the tray is loaded!
+ // Swing gets the image size info VIA gtk, so this is important as well.
+ GtkEventDispatch.startGui(FORCE_GTK2, PREFER_GTK3, DEBUG);
+ GtkEventDispatch.waitForEventsToComplete();
- logger.error("Unable to initialize GTK! Something is severely wrong! Using the Swing Tray type instead.");
+ if (DEBUG) {
+ // output what version of GTK we have loaded.
+ logger.debug("GTK Version: " + Gtk.MAJOR + "." + Gtk.MINOR + "." + Gtk.MICRO);
+ logger.debug("Is the system already running GTK? {}", Gtk.alreadyRunningGTK);
}
- else if (OSUtil.Linux.isArch()) {
- // arch linux is fun!
- if (isTrayType(trayType, TrayType.AppIndicator) && !AppIndicator.isLoaded) {
- // appindicators
-
- // requires the install of libappindicator which is GTK2 (as of 25DEC2016)
- // requires the install of libappindicator3 which is GTK3 (as of 25DEC2016)
+ if (!isTrayType(trayType, TrayType.Swing)) {
+ // NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3.
+ // appindicator3 doesn't support menu icons via GTK2!!
+ if (!Gtk.isLoaded) {
trayType = selectTypeQuietly(TrayType.Swing);
- if (Gtk.isGtk2) {
- logger.warn("Unable to initialize AppIndicator for Arch linux, it requires GTK2! " +
- "Please install libappindicator, for example: 'sudo pacman -S libappindicator'. " +
+ logger.error("Unable to initialize GTK! Something is severely wrong! Using the Swing Tray type instead.");
+ }
+ else if (OSUtil.Linux.isArch()) {
+ // arch linux is fun!
+
+ if (isTrayType(trayType, TrayType.AppIndicator) && !AppIndicator.isLoaded) {
+ // appindicators
+
+ // requires the install of libappindicator which is GTK2 (as of 25DEC2016)
+ // requires the install of libappindicator3 which is GTK3 (as of 25DEC2016)
+ trayType = selectTypeQuietly(TrayType.Swing);
+
+ if (Gtk.isGtk2) {
+ logger.warn("Unable to initialize AppIndicator for Arch linux, it requires GTK2! " +
+ "Please install libappindicator, for example: 'sudo pacman -S libappindicator'. " +
+ "Using the Swing Tray type instead.");
+ } else {
+ logger.error("Unable to initialize AppIndicator for Arch linux, it requires GTK3! " +
+ "Please install libappindicator3, for example: 'sudo pacman -S libappindicator3'. " +
+ "Using the Swing Tray type instead."); // GTK3
+ }
+ } else if (isTrayType(trayType, TrayType.GtkStatusIcon)) {
+ if (!Extension.isInstalled()) {
+ // Automatically install the extension for everyone except Arch. They are bonkers.
+ Extension.ENABLE_EXTENSION_INSTALL = false;
+ SystemTray.logger.info("You may need a work-around for showing the SystemTray icon - we suggest installing the " +
+ "the [Top Icons] plugin (https://extensions.gnome.org/extension/1031/topicons/) which moves " +
+ "icons from the *notification drawer* (it is normally collapsed) at the bottom left corner " +
+ "of the screen to the menu panel next to the clock.");
+ }
+ }
+ }
+ else if (isTrayType(trayType, TrayType.AppIndicator)) {
+ if (Gtk.isGtk2 && AppIndicator.isVersion3) {
+ trayType = selectTypeQuietly(TrayType.Swing);
+
+ logger.warn("AppIndicator3 detected with GTK2, falling back to GTK2 system tray type. " +
+ "Please install libappindicator1 OR GTK3, for example: 'sudo apt-get install libappindicator1'. " +
"Using the Swing Tray type instead.");
- } else {
- logger.error("Unable to initialize AppIndicator for Arch linux, it requires GTK3! " +
- "Please install libappindicator3, for example: 'sudo pacman -S libappindicator3'. " +
- "Using the Swing Tray type instead."); // GTK3
+
+ }
+ else if (!AppIndicator.isLoaded) {
+ // YIKES. Try to fallback to GtkStatusIndicator, since AppIndicator couldn't load.
+ trayType = selectTypeQuietly(TrayType.Swing);
+
+ logger.warn("Unable to initialize the AppIndicator correctly. Using the Swing Tray type instead.");
}
}
}
- else if (isTrayType(trayType, TrayType.AppIndicator)) {
- if (Gtk.isGtk2 && AppIndicator.isVersion3) {
- trayType = selectTypeQuietly(TrayType.Swing);
-
- logger.warn("AppIndicator3 detected with GTK2, falling back to GTK2 system tray type. " +
- "Please install libappindicator1 OR GTK3, for example: 'sudo apt-get install libappindicator1'. " +
- "Using the Swing Tray type instead.");
-
- }
- else if (!AppIndicator.isLoaded) {
- // YIKES. Try to fallback to GtkStatusIndicator, since AppIndicator couldn't load.
- trayType = selectTypeQuietly(TrayType.Swing);
-
- logger.warn("Unable to initialize the AppIndicator correctly. Using the Swing Tray type instead.");
- }
- }
}
-
- if (isJavaFxLoaded) {
- // This will initialize javaFX dispatch methods
- JavaFX.init();
- }
- else if (isSwtLoaded) {
- // This will initialize swt dispatch methods
- Swt.init();
- }
-
- if (isNix) {
- // linux/unix need access to GTK, so load it up before the tray is loaded!
- GtkEventDispatch.startGui();
- GtkEventDispatch.waitForEventsToComplete();
- }
-
-
// have to make adjustments BEFORE the tray/menu image size calculations
- if (AUTO_FIX_INCONSISTENCIES && isTrayType(trayType, TrayType.Swing) && SystemTray.SWING_UI == null && SwingUtil.isDefaultLookAndFeel()) {
+ if (AUTO_FIX_INCONSISTENCIES && isTrayType(trayType, TrayType.Swing) && SystemTray.SWING_UI == null) {
if (isNix) {
SystemTray.SWING_UI = new LinuxSwingUI();
}
@@ -765,11 +897,13 @@ class SystemTray {
// initialize tray/menu image sizes. This must be BEFORE the system tray has been created
- int trayImageSize = SizeAndScalingUtil.getTrayImageSize(trayType);
+ int trayImageSize = SizeAndScalingUtil.getTrayImageSize();
int menuImageSize = SizeAndScalingUtil.getMenuImageSize(trayType);
- logger.debug("Tray indicator image size: {}", trayImageSize);
- logger.debug("Tray menu image size: {}", menuImageSize);
+ if (DEBUG) {
+ logger.debug("Tray indicator image size: {}", trayImageSize);
+ logger.debug("Tray menu image size: {}", menuImageSize);
+ }
if (AUTO_FIX_INCONSISTENCIES) {
// this logic has to be before we create the system Tray, but after GTK is started (if applicable)
@@ -790,7 +924,7 @@ class SystemTray {
- if ((isJavaFxLoaded || isSwtLoaded) && SwingUtilities.isEventDispatchThread()) {
+ if ((JavaFX.isLoaded || Swt.isLoaded) && SwingUtilities.isEventDispatchThread()) {
// This WILL NOT WORK. Let the dev know
logger.error("SystemTray initialization for JavaFX or SWT **CAN NOT** occur on the Swing Event Dispatch Thread " +
"(EDT). Something is seriously wrong.");
@@ -803,9 +937,10 @@ class SystemTray {
// javaFX and SWT **CAN NOT** start on the EDT!!
// linux + GTK/AppIndicator menus must not start on the EDT!
+ systemTray = new SystemTray();
// AWT/Swing must be constructed on the EDT however...
- if (isJavaFxLoaded || isSwtLoaded ||
+ if (JavaFX.isLoaded || Swt.isLoaded ||
(isNix && (isTrayType(trayType, TrayType.GtkStatusIcon) || isTrayType(trayType, TrayType.AppIndicator)))
) {
try {
@@ -854,7 +989,7 @@ class SystemTray {
// These install a shutdown hook in JavaFX/SWT, so that when the main window is closed -- the system tray is ALSO closed.
if (ENABLE_SHUTDOWN_HOOK) {
- if (isJavaFxLoaded) {
+ if (JavaFX.isLoaded) {
// Necessary because javaFX **ALSO** runs a gtk main loop, and when it stops (if we don't stop first), we become unresponsive.
// Also, it's nice to have us shutdown at the same time as the main application
JavaFX.onShutdown(new Runnable() {
@@ -867,10 +1002,10 @@ class SystemTray {
}
});
}
- else if (isSwtLoaded) {
+ else if (Swt.isLoaded) {
// this is because SWT **ALSO** runs a gtk main loop, and when it stops (if we don't stop first), we become unresponsive
// Also, it's nice to have us shutdown at the same time as the main application
- Swt.onShutdown(new Runnable() {
+ dorkbox.util.Swt.onShutdown(new Runnable() {
@Override
public
void run() {
@@ -899,7 +1034,7 @@ class SystemTray {
*/
public static
String getVersion() {
- return "3.1";
+ return "3.12";
}
/**
@@ -924,13 +1059,14 @@ class SystemTray {
*/
public
void shutdown() {
- // this will call "dispatchAndWait()" behind the scenes, so it is thread-safe
+ // this is thread-safe
final Menu menu = systemTrayMenu;
if (menu != null) {
menu.remove();
}
systemTrayMenu = null;
+ EventDispatch.shutdown();
}
/**
@@ -1153,7 +1289,7 @@ class SystemTray {
*/
public
int getTrayImageSize() {
- return SizeAndScalingUtil.getTrayImageSize(systemTrayMenu.getClass());
+ return SizeAndScalingUtil.getTrayImageSize();
}
diff --git a/src/dorkbox/systemTray/Tray.java b/src/dorkbox/systemTray/Tray.java
index d99cfcd..f0d36af 100644
--- a/src/dorkbox/systemTray/Tray.java
+++ b/src/dorkbox/systemTray/Tray.java
@@ -22,9 +22,7 @@ import java.net.URL;
import javax.imageio.stream.ImageInputStream;
-import dorkbox.systemTray.gnomeShell.Extension;
import dorkbox.systemTray.util.ImageResizeUtil;
-import dorkbox.util.OSUtil;
// This is public ONLY so that it is in the scope for SwingUI and NativeUI system tray components
public
@@ -32,27 +30,6 @@ class Tray extends Menu {
// true if we are using gnome (and things depend on it) or false
public static volatile boolean usingGnome = false;
- protected static
- void installExtension() {
- // do we need to install the GNOME extension??
- if (Tray.usingGnome) {
- if (OSUtil.Linux.isArch()) {
- if (SystemTray.DEBUG) {
- SystemTray.logger.debug("Running Arch Linux.");
- }
- if (!Extension.isInstalled()) {
- SystemTray.logger.info("You may need a work-around for showing the SystemTray icon - we suggest installing the " +
- "the [Top Icons] plugin (https://extensions.gnome.org/extension/1031/topicons/) which moves " +
- "icons from the *notification drawer* (it is normally collapsed) at the bottom left corner " +
- "of the screen to the menu panel next to the clock.");
- }
- } else {
- // Automatically install the extension for everyone except Arch. It's bonkers.
- Extension.install();
- }
- }
- }
-
// appindicators DO NOT support anything other than PLAIN gtk-menus
// they ALSO do not support tooltips!
// https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
@@ -113,32 +90,6 @@ class Tray extends Menu {
}
}
- // method that is meant to be overridden by the tray implementations
- protected
- void setTooltip_(final String tooltipText) {
- // default is NO OP
- }
-
- /**
- * Specifies the tooltip text, usually this is used to brand the SystemTray icon with your product's name.
- *
- * The maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop
- * Environments.
- *
- * For more details on Linux see https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12.
- *
- * @param tooltipText the text to use as tooltip for the tray icon, null to remove
- */
- final
- void setTooltip(final String tooltipText) {
- // this is a safety precaution, since the behavior of really long text is undefined.
- if (tooltipText.length() > 64) {
- throw new RuntimeException("Tooltip text cannot be longer than 64 characters.");
- }
-
- setTooltip_(tooltipText);
- }
-
/**
* Specifies the new image to set for the tray icon.
*
diff --git a/src/dorkbox/systemTray/gnomeShell/Extension.java b/src/dorkbox/systemTray/gnomeShell/Extension.java
index c3f1a6b..72e5f19 100644
--- a/src/dorkbox/systemTray/gnomeShell/Extension.java
+++ b/src/dorkbox/systemTray/gnomeShell/Extension.java
@@ -19,7 +19,6 @@ import static dorkbox.systemTray.SystemTray.logger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -27,17 +26,17 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
-import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import dorkbox.executor.ShellAsyncExecutor;
+import dorkbox.executor.ShellExecutor;
import dorkbox.systemTray.SystemTray;
import dorkbox.util.IO;
import dorkbox.util.OSUtil;
import dorkbox.util.Property;
-import dorkbox.util.process.ShellProcessBuilder;
@SuppressWarnings({"DanglingJavadoc", "WeakerAccess"})
public
@@ -61,19 +60,15 @@ class Extension {
public static
List getEnabledExtensions() {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
- PrintStream outputStream = new PrintStream(byteArrayOutputStream);
-
-
// gsettings get org.gnome.shell enabled-extensions
- final ShellProcessBuilder gsettings = new ShellProcessBuilder(outputStream);
+ final ShellExecutor gsettings = new ShellExecutor();
gsettings.setExecutable("gsettings");
gsettings.addArgument("get");
gsettings.addArgument("org.gnome.shell");
gsettings.addArgument("enabled-extensions");
gsettings.start();
- String output = ShellProcessBuilder.getOutput(byteArrayOutputStream);
+ String output = gsettings.getOutput();
// now we have to enable us if we aren't already enabled
@@ -137,9 +132,6 @@ class Extension {
public static
void setEnabledExtensions(List extensions) {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
- PrintStream outputStream = new PrintStream(byteArrayOutputStream);
-
StringBuilder stringBuilder = new StringBuilder("[");
for (int i = 0, extensionsSize = extensions.size(), limit = extensionsSize-1; i < extensionsSize; i++) {
@@ -166,7 +158,7 @@ class Extension {
// gsettings set org.gnome.shell enabled-extensions "['SystemTray@Dorkbox']"
// gsettings set org.gnome.shell enabled-extensions "['background-logo@fedorahosted.org']"
// gsettings set org.gnome.shell enabled-extensions "['background-logo@fedorahosted.org', 'SystemTray@Dorkbox']"
- final ShellProcessBuilder setGsettings = new ShellProcessBuilder(outputStream);
+ final ShellExecutor setGsettings = new ShellExecutor();
setGsettings.setExecutable("gsettings");
setGsettings.addArgument("set");
setGsettings.addArgument("org.gnome.shell");
@@ -179,7 +171,8 @@ class Extension {
void restartShell() {
if (ENABLE_SHELL_RESTART) {
if (SystemTray.DEBUG) {
- logger.debug("DEBUG mode enabled. You need to manually restart the shell via '{}'", SHELL_RESTART_COMMAND);
+ logger.debug("DEBUG mode enabled. You need to log-in/out or manually restart the shell via '{}' to apply the changes.",
+ SHELL_RESTART_COMMAND);
return;
}
@@ -187,11 +180,8 @@ class Extension {
logger.debug("Restarting gnome-shell so tray notification changes can be applied.");
}
- // now we have to restart the gnome shell via bash
- final ShellProcessBuilder restartShell = new ShellProcessBuilder();
- // restart shell in background process
- restartShell.addArgument(SHELL_RESTART_COMMAND);
- restartShell.start();
+ // now we have to restart the gnome shell via bash in a background process
+ ShellAsyncExecutor.runShell(SHELL_RESTART_COMMAND);
// We don't care when the shell restarts, since WHEN IT DOES restart, our extension will show our icon.
}
@@ -215,12 +205,15 @@ class Extension {
*/
public static
void install() {
- boolean isGnome = OSUtil.DesktopEnv.isGnome();
- if (!ENABLE_EXTENSION_INSTALL || !isGnome || (OSUtil.Linux.isDebian())) {
- // note: Debian Gnome3 does NOT work! (tested on Debian 8.5 and 8.6 default installs)
+ if (!ENABLE_EXTENSION_INSTALL) {
+ // note: Debian Gnome3 does NOT work! (tested on Debian 8.5 and 8.6 default installs).
return;
}
+ if (SystemTray.DEBUG) {
+ SystemTray.logger.debug("Installing Gnome extension.");
+ }
+
boolean hasTopIcons;
boolean hasSystemTray;
diff --git a/src/dorkbox/systemTray/jna/linux/AppIndicator.java b/src/dorkbox/systemTray/jna/linux/AppIndicator.java
deleted file mode 100644
index 90ebf65..0000000
--- a/src/dorkbox/systemTray/jna/linux/AppIndicator.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux;
-
-import static dorkbox.systemTray.SystemTray.logger;
-
-import com.sun.jna.NativeLibrary;
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.jna.linux.structs.AppIndicatorInstanceStruct;
-import dorkbox.util.OS;
-import dorkbox.util.jna.JnaHelper;
-
-/**
- * bindings for libappindicator
- *
- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
- */
-@SuppressWarnings({"Duplicates", "SameParameterValue", "DanglingJavadoc"})
-public
-class AppIndicator {
- public static final boolean isVersion3;
- public static final boolean isLoaded;
-
- /**
- * 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 works, and are able to map the
- * symbols we need. There are bash commands that will tell us the linked library name, however - I'd rather not run bash commands
- * to determine this.
- *
- * This is so hacky it makes me sick.
- */
- static {
- boolean _isVersion3 = false;
- boolean _isLoaded = false;
-
- boolean shouldLoadAppIndicator = !(OS.isWindows() || OS.isMacOsX());
- if (!shouldLoadAppIndicator) {
- _isLoaded = true;
- }
-
- // objdump -T /usr/lib/x86_64-linux-gnu/libappindicator.so.1 | grep foo
- // objdump -T /usr/lib/x86_64-linux-gnu/libappindicator3.so.1 | grep foo
-
- // NOTE:
- // ALSO WHAT VERSION OF GTK to use? appindiactor1 -> GTK2, appindicator3 -> GTK3.
- // appindiactor1 is GKT2 only (can't use GTK3 bindings with it)
- // appindicator3 doesn't support menu icons via GTK2!!
-
- if (!_isLoaded && SystemTray.FORCE_TRAY_TYPE == SystemTray.TrayType.GtkStatusIcon) {
- // if we force GTK type system tray, don't attempt to load AppIndicator libs
- if (SystemTray.DEBUG) {
- logger.debug("Forced GtkStatusIcon tray type, not using AppIndicator");
- }
- _isLoaded = true;
- }
-
- if (!_isLoaded && SystemTray.FORCE_GTK2) {
- // if specified, try loading appindicator1 first, maybe it's there?
- // note: we can have GTK2 + appindicator3, but NOT ALWAYS.
- try {
- // deliberately without the "1" at the end.
- final NativeLibrary library = JnaHelper.register("appindicator", AppIndicator.class);
- if (library != null) {
- _isLoaded = true;
- }
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.debug("Error loading GTK2 explicit appindicator. {}", e.getMessage());
- }
- }
- }
-
- String nameToCheck1;
- String nameToCheck2;
-
- if (Gtk.isGtk2) {
- nameToCheck1 = "appindicator"; // deliberately without the "1" at the end.
- }
- else {
- nameToCheck1 = "appindicator3";
- }
-
- // start with base version using whatever the OS specifies as the proper symbolic link
- if (!_isLoaded) {
- try {
- final NativeLibrary library = JnaHelper.register(nameToCheck1, AppIndicator.class);
- String s = library.getFile().getName();
-
- if (SystemTray.DEBUG) {
- logger.debug("Loading library (first attempt): '{}'", s);
- }
-
- if (s.contains("appindicator3")) {
- _isVersion3 = true;
- }
-
- _isLoaded = true;
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.debug("Error loading library: '{}'. \n{}", nameToCheck1, e.getMessage());
- }
- }
- }
-
- // maybe it's really GTK2 version? who knows...
- if (!_isLoaded) {
- try {
- JnaHelper.register("appindicator", AppIndicator.class);
- _isLoaded = true;
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.debug("Error loading library: '{}'. \n{}", "appindicator", e.getMessage());
- }
- }
- }
-
- // If we are GTK2, change the order we check and load libraries
-
- if (Gtk.isGtk2) {
- nameToCheck1 = "appindicator-gtk";
- nameToCheck2 = "appindicator-gtk3";
- }
- else {
- nameToCheck1 = "appindicator-gtk3";
- nameToCheck2 = "appindicator-gtk";
- }
-
- // another type. who knows...
- if (!_isLoaded) {
- try {
- JnaHelper.register(nameToCheck1, AppIndicator.class);
- _isLoaded = true;
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.debug("Error loading library: '{}'. \n{}", nameToCheck1, e.getMessage());
- }
- }
- }
-
- // this is HORRID. such a PITA
- if (!_isLoaded) {
- try {
- JnaHelper.register(nameToCheck2, AppIndicator.class);
- _isLoaded = true;
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.debug("Error loading library: '{}'. \n{}", nameToCheck2, e.getMessage());
- }
- }
- }
-
- // We fall back to GtkStatusIndicator or Swing if this cannot load
- if (shouldLoadAppIndicator && _isLoaded) {
- isLoaded = true;
- isVersion3 = _isVersion3;
- } else {
- isLoaded = false;
- isVersion3 = false;
- }
- }
-
- // Note: AppIndicators DO NOT support tooltips, as per mark shuttleworth. Rather stupid IMHO.
- // See: https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
-
- public static final int CATEGORY_APPLICATION_STATUS = 0;
-// public static final int CATEGORY_COMMUNICATIONS = 1;
-// public static final int CATEGORY_SYSTEM_SERVICES = 2;
-// public static final int CATEGORY_HARDWARE = 3;
-// public static final int CATEGORY_OTHER = 4;
-
- public static final int STATUS_PASSIVE = 0;
- public static final int STATUS_ACTIVE = 1;
-// public static final int STATUS_ATTENTION = 2;
-
-
- public static native
- AppIndicatorInstanceStruct app_indicator_new(String id, String icon_name, int category);
-
- public static native void app_indicator_set_title(AppIndicatorInstanceStruct self, String title);
- 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);
-}
diff --git a/src/dorkbox/systemTray/jna/linux/FuncCallback.java b/src/dorkbox/systemTray/jna/linux/FuncCallback.java
deleted file mode 100644
index fb177c1..0000000
--- a/src/dorkbox/systemTray/jna/linux/FuncCallback.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux;
-
-import com.sun.jna.Callback;
-import com.sun.jna.Pointer;
-
-import dorkbox.util.Keep;
-
-@Keep
-interface FuncCallback extends Callback {
- /**
- * @return Gtk.FALSE if it will be automatically removed from the stack once it's handled
- */
- int callback(Pointer data);
-}
diff --git a/src/dorkbox/systemTray/jna/linux/GEventCallback.java b/src/dorkbox/systemTray/jna/linux/GEventCallback.java
deleted file mode 100644
index 32e2686..0000000
--- a/src/dorkbox/systemTray/jna/linux/GEventCallback.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux;
-
-import com.sun.jna.Callback;
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.jna.linux.structs.GdkEventButton;
-import dorkbox.util.Keep;
-
-@Keep
-public
-interface GEventCallback extends Callback {
- void callback(Pointer instance, GdkEventButton event);
-}
diff --git a/src/dorkbox/systemTray/jna/linux/Glib.java b/src/dorkbox/systemTray/jna/linux/Glib.java
deleted file mode 100644
index adb17b2..0000000
--- a/src/dorkbox/systemTray/jna/linux/Glib.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux;
-
-import com.sun.jna.Callback;
-import com.sun.jna.NativeLibrary;
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.SystemTray;
-import dorkbox.util.jna.JnaHelper;
-
-/**
- * bindings for glib-2.0
- *
- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
- */
-public
-class Glib {
- static {
- try {
- NativeLibrary library = JnaHelper.register("glib-2.0", Glib.class);
- if (library == null) {
- SystemTray.logger.error("Error loading Glib library, it failed to load.");
- }
- } catch (Throwable e) {
- SystemTray.logger.error("Error loading Glib library, it failed to load {}", e.getMessage());
- }
- }
-
- public interface GLogLevelFlags {
- public static final int RECURSION = 1 << 0;
- public static final int FATAL = 1 << 1;
- /* GLib log levels */
- public static final int ERROR = 1 << 2; /* always fatal */
- public static final int CRITICAL = 1 << 3;
- public static final int WARNING = 1 << 4;
- public static final int MESSAGE = 1 << 5;
- public static final int INFO = 1 << 6;
- public static final int DEBUG = 1 << 7;
- public static final int MASK = ~(RECURSION | FATAL);
- }
-
- public interface GLogFunc extends Callback {
- void callback (String log_domain, int log_level, String message, Pointer data);
- }
-
- public static final Glib.GLogFunc nullLogFunc = new Glib.GLogFunc() {
- @Override
- public
- void callback(final String log_domain, final int log_level, final String message, final Pointer data) {
- // do nothing
- }
- };
-
- public static native int g_log_set_handler(String log_domain, int levels, GLogFunc handler, Pointer user_data);
- public static native void g_log_default_handler (String log_domain, int log_level, String message, Pointer unused_data);
- public static native GLogFunc g_log_set_default_handler(GLogFunc log_func, Pointer user_data);
- public static native void g_log_remove_handler (String log_domain, int handler_id);
-}
diff --git a/src/dorkbox/systemTray/jna/linux/Gobject.java b/src/dorkbox/systemTray/jna/linux/Gobject.java
deleted file mode 100644
index 5d0ed43..0000000
--- a/src/dorkbox/systemTray/jna/linux/Gobject.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux;
-
-import com.sun.jna.Callback;
-import com.sun.jna.NativeLibrary;
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.SystemTray;
-import dorkbox.util.jna.JnaHelper;
-
-/**
- * bindings for libgobject-2.0
- *
- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
- */
-public
-class Gobject {
-
- static {
- try {
- NativeLibrary library = JnaHelper.register("gobject-2.0", Gobject.class);
- if (library == null) {
- SystemTray.logger.error("Error loading GObject library, it failed to load.");
- }
- } catch (Throwable e) {
- SystemTray.logger.error("Error loading GObject library, it failed to load {}", e.getMessage());
- }
- }
-
- // objdump -T /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 | grep block
-
- public static native void g_object_unref(Pointer object);
-
- public static native void g_object_force_floating(Pointer object);
- public static native void g_object_ref_sink(Pointer object);
-
- // note: the return type here MUST be long to avoid issues on freeBSD. NativeLong (previously used) worked on everything except BSD.
- public static native long g_signal_connect_object(Pointer instance, String detailed_signal, Callback c_handler, Pointer object, int connect_flags);
-
- public static native void g_signal_handler_block(Pointer instance, long handlerId);
- public static native void g_signal_handler_unblock(Pointer instance, long handlerId);
-
- public static native void g_object_get(Pointer instance, String property_name, Pointer value, Pointer terminator);
-
-
-
- // Types are here https://developer.gnome.org/gobject/stable/gobject-Type-Information.html
- public static native void g_value_init(Pointer gvalue, double type);
-
- /**
- * Clears the current value in value (if any) and "unsets" the type, this releases all resources associated with this GValue. An unset value is the same as an uninitialized (zero-filled) GValue structure.
- * @param gvalue
- */
- public static native void g_value_unset(Pointer gvalue);
-
- public static native String g_value_get_string(Pointer gvalue);
- public static native int g_value_get_int(Pointer gvalue);
-
-
- public static native Pointer g_type_class_ref(Pointer widgetType);
- public static native void g_type_class_unref(Pointer widgetClass);
-
-}
diff --git a/src/dorkbox/systemTray/jna/linux/Gtk.java b/src/dorkbox/systemTray/jna/linux/Gtk.java
deleted file mode 100644
index f4257ea..0000000
--- a/src/dorkbox/systemTray/jna/linux/Gtk.java
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux;
-
-import static dorkbox.systemTray.SystemTray.logger;
-
-import com.sun.jna.Function;
-import com.sun.jna.NativeLibrary;
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.jna.linux.structs.GtkStyle;
-import dorkbox.util.OS;
-import dorkbox.util.jna.JnaHelper;
-
-/**
- * Bindings for GTK+ 2. Bindings that are exclusively for GTK+ 3 are in that respective class
- *
- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
- */
-@SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed", "WeakerAccess"})
-public
-class Gtk {
- // 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
- // objdump -T /usr/local/lib/libgtk-3.so.0 | grep gtk
-
- // For funsies to look at, SyncThing did a LOT of work on compatibility in python (unfortunate for us, but interesting).
- // https://github.com/syncthing/syncthing-gtk/blob/b7a3bc00e3bb6d62365ae62b5395370f3dcc7f55/syncthing_gtk/statusicon.py
-
- @SuppressWarnings({"unused", "PointlessBitwiseExpression"})
- public static class State {
- public static final int NORMAL = 0x0; // normal state.
- public static final int ACTIVE = 0x1; // pressed-in or activated; e.g. buttons while the mouse button is held down.
- public static final int PRELIGHT = 0x2; // color when the mouse is over an activatable widget.
- public static final int SELECTED = 0x3; // color when something is selected, e.g. when selecting some text to cut/copy.
- public static final int INSENSITIVE = 0x4; // color when the mouse is over an activatable widget.
-
- public static final int FLAG_NORMAL = 0;
- public static final int FLAG_ACTIVE = 1 << 0;
- public static final int FLAG_PRELIGHT = 1 << 1;
- public static final int FLAG_SELECTED = 1 << 2;
- public static final int FLAG_INSENSITIVE = 1 << 3;
- public static final int FLAG_INCONSISTENT = 1 << 4;
- public static final int FLAG_FOCUSED = 1 << 5;
- public static final int FLAG_BACKDROP = 1 << 6;
- }
-
- public static class IconSize {
- public static final int INVALID = 0; // Invalid size.
- public static final int MENU = 1; // Size appropriate for menus (16px).
- public static final int SMALL_TOOLBAR = 2; // Size appropriate for small toolbars (16px).
- public static final int LARGE_TOOLBAR = 3; // Size appropriate for large toolbars (24px)
- public static final int BUTTON = 4; // Size appropriate for buttons (16px)
- public static final int DND = 5; // Size appropriate for drag and drop (32px)
- public static final int DIALOG = 6; // Size appropriate for dialogs (48px)
- }
-
-
- // NOTE: AppIndicator uses this info to figure out WHAT VERSION OF appindicator to use: GTK2 -> appindicator1, GTK3 -> appindicator3
- public static final boolean isGtk2;
- public static final boolean isGtk3;
- public static final boolean isLoaded;
-
-
- public static final int FALSE = 0;
- public static final int TRUE = 1;
- static final boolean alreadyRunningGTK;
-
- public static Function gtk_status_icon_position_menu = null;
-
- public static final int MAJOR;
- public static final int MINOR;
- public static final int MICRO;
-
- /*
- * We can have GTK v3 or v2.
- *
- * Observations:
- * JavaFX uses GTK2, and we can't load GTK3 if GTK2 symbols are loaded
- * SWT uses GTK2 or GTK3. We do not work with the GTK3 version of SWT.
- */
- static {
- boolean shouldUseGtk2 = SystemTray.FORCE_GTK2;
- boolean _isGtk2 = false;
- boolean _isLoaded = false;
- boolean _alreadyRunningGTK = false;
- int major = 0;
- int minor = 0;
- int micro = 0;
-
- boolean shouldLoadGtk = !(OS.isWindows() || OS.isMacOsX());
- if (!shouldLoadGtk) {
- _isLoaded = true;
- }
-
- // we can force the system to use the swing indicator, which WORKS, but doesn't support transparency in the icon. However, there
- // are certain GTK functions we might want to use (even if we are Swing or AWT), so we load GTK anyways...
-
- // in some cases, we ALWAYS want to try GTK2 first
- String gtk2LibName = "gtk-x11-2.0";
- String gtk3LibName = "libgtk-3.so.0";
-
-
- if (!_isLoaded && shouldUseGtk2) {
- try {
- NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk2.class);
- gtk_status_icon_position_menu = Function.getFunction(gtk2LibName, "gtk_status_icon_position_menu");
- _isGtk2 = true;
-
- // when running inside of JavaFX, this will be '1'. All other times this should be '0'
- // when it's '1', it means that someone else has started GTK -- so we DO NOT NEED TO.
- _alreadyRunningGTK = Gtk2.gtk_main_level() != 0;
- _isLoaded = true;
-
- major = library.getGlobalVariableAddress("gtk_major_version").getInt(0);
- minor = library.getGlobalVariableAddress("gtk_minor_version").getInt(0);
- micro = library.getGlobalVariableAddress("gtk_micro_version").getInt(0);
-
- if (SystemTray.DEBUG) {
- logger.debug("GTK: {}", gtk2LibName);
- }
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.error("Error loading library", e);
- }
- }
- }
-
- // now for the defaults...
-
- // start with version 3
- if (!_isLoaded) {
- try {
- // ALSO map Gtk2.java to GTK3 library. Cannot have Gtk3 extend Gtk2, it won't work.
- JnaHelper.register(gtk3LibName, Gtk2.class);
- gtk_status_icon_position_menu = Function.getFunction(gtk3LibName, "gtk_status_icon_position_menu");
-
- // ALSO have to load the SPECIFIC Gtk+ 3 methods. We cannot subclass because JNA doesn't like it.
- // This is BY FAR the best way to accomplish this, however because of the way static methods work, we are
- // stuck "loading it twice"
- JnaHelper.register(gtk3LibName, Gtk3.class);
-
- // when running inside of JavaFX, this will be '1'. All other times this should be '0'
- // when it's '1', it means that someone else has started GTK -- so we DO NOT NEED TO.
- _alreadyRunningGTK = Gtk2.gtk_main_level() != 0;
- _isLoaded = true;
-
- major = Gtk3.gtk_get_major_version();
- minor = Gtk3.gtk_get_minor_version();
- micro = Gtk3.gtk_get_micro_version();
-
- if (SystemTray.DEBUG) {
- logger.debug("GTK: {}", gtk3LibName);
- }
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.error("Error loading library", e);
- }
- }
- }
-
- // now version 2
- if (!_isLoaded) {
- try {
- NativeLibrary library = JnaHelper.register(gtk2LibName, Gtk2.class);
- gtk_status_icon_position_menu = Function.getFunction(gtk2LibName, "gtk_status_icon_position_menu");
- _isGtk2 = true;
-
- // when running inside of JavaFX, this will be '1'. All other times this should be '0'
- // when it's '1', it means that someone else has started GTK -- so we DO NOT NEED TO.
- _alreadyRunningGTK = Gtk2.gtk_main_level() != 0;
- _isLoaded = true;
-
- major = library.getGlobalVariableAddress("gtk_major_version").getInt(0);
- minor = library.getGlobalVariableAddress("gtk_minor_version").getInt(0);
- micro = library.getGlobalVariableAddress("gtk_micro_version").getInt(0);
-
- if (SystemTray.DEBUG) {
- logger.debug("GTK: {}", gtk2LibName);
- }
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- logger.error("Error loading library", e);
- }
- }
- }
-
- if (shouldLoadGtk && _isLoaded) {
- isLoaded = true;
-
- // depending on how the system is initialized, SWT may, or may not, have the gtk_main loop running. It will EVENTUALLY run, so we
- // do not want to run our own GTK event loop.
- _alreadyRunningGTK |= SystemTray.isSwtLoaded;
-
- if (SystemTray.DEBUG) {
- logger.debug("Is the system already running GTK? {}", _alreadyRunningGTK);
- }
-
- alreadyRunningGTK = _alreadyRunningGTK;
- isGtk2 = _isGtk2;
- isGtk3 = !_isGtk2;
-
- MAJOR = major;
- MINOR = minor;
- MICRO = micro;
- }
- else {
- isLoaded = false;
-
- alreadyRunningGTK = false;
- isGtk2 = false;
- isGtk3 = false;
-
- MAJOR = 0;
- MINOR = 0;
- MICRO = 0;
- }
-
- if (shouldLoadGtk) {
- // now we output what version of GTK we have loaded.
- if (SystemTray.DEBUG) {
- SystemTray.logger.debug("GTK Version: " + MAJOR + "." + MINOR + "." + MICRO);
- }
-
- if (!_isLoaded) {
- throw new RuntimeException("We apologize for this, but we are unable to determine the GTK library is in use, " +
- "or even if it is in use... Please create an issue for this and include your OS type and configuration.");
- }
- }
- }
-
- /**
- * Creates a new GtkMenu
- */
- public static
- Pointer gtk_menu_new() {
- return Gtk2.gtk_menu_new();
- }
-
- /**
- * Sets or replaces the menu item’s submenu, or removes it when a NULL submenu is passed.
- */
- public static
- void gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu) {
- Gtk2.gtk_menu_item_set_submenu(menuEntry, menu);
- }
-
- /**
- * Creates a new GtkSeparatorMenuItem.
- */
- public static
- Pointer gtk_separator_menu_item_new() {
- return Gtk2.gtk_separator_menu_item_new();
- }
-
- /**
- * Creates a new GtkImage displaying the file filename . If the file isn’t found or can’t be loaded, the resulting GtkImage will
- * display a “broken image” icon. This function never returns NULL, it always returns a valid GtkImage widget.
- *
- * If the file contains an animation, the image will contain an animation.
- */
- public static
- Pointer gtk_image_new_from_file(String iconPath) {
- return Gtk2.gtk_image_new_from_file(iconPath);
- }
-
- /**
- * Sets the active state of the menu item’s check box.
- */
- public static
- void gtk_check_menu_item_set_active(Pointer check_menu_item, boolean isChecked) {
- Gtk2.gtk_check_menu_item_set_active(check_menu_item, isChecked);
- }
-
- /**
- * Creates a new GtkImageMenuItem containing a label. The label will be created using gtk_label_new_with_mnemonic(), so underscores
- * in label indicate the mnemonic for the menu item.
- *
- * uses '_' to define which key is the mnemonic
- *
- * gtk_image_menu_item_new_with_mnemonic has been deprecated since version 3.10 and should not be used in newly-written code.
- * NOTE: Use gtk_menu_item_new_with_mnemonic() instead.
- */
- public static
- Pointer gtk_image_menu_item_new_with_mnemonic(String label) {
- return Gtk2.gtk_image_menu_item_new_with_mnemonic(label);
- }
-
- public static
- Pointer gtk_check_menu_item_new_with_mnemonic(String label) {
- return Gtk2.gtk_check_menu_item_new_with_mnemonic(label);
- }
-
- /**
- * Sets the image of image_menu_item to the given widget. Note that it depends on the show-menu-images setting whether the image
- * will be displayed or not.
- *
- * gtk_image_menu_item_set_image has been deprecated since version 3.10 and should not be used in newly-written code.
- */
- public static
- void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image) {
- Gtk2.gtk_image_menu_item_set_image(image_menu_item, image);
- }
-
- /**
- * If TRUE, the menu item will ignore the “gtk-menu-images” setting and always show the image, if available.
- * Use this property if the menuitem would be useless or hard to use without the image
- *
- * gtk_image_menu_item_set_always_show_image has been deprecated since version 3.10 and should not be used in newly-written code.
- */
- public static
- void gtk_image_menu_item_set_always_show_image(Pointer menu_item, boolean forceShow) {
- Gtk2.gtk_image_menu_item_set_always_show_image(menu_item, forceShow);
- }
-
- /**
- * Creates an empty status icon object.
- *
- * gtk_status_icon_new has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- public static
- Pointer gtk_status_icon_new() {
- return Gtk2.gtk_status_icon_new();
- }
-
- /**
- * Obtains the root window (parent all other windows are inside) for the default display and screen.
- *
- * @return the default root window
- */
- public static
- Pointer gdk_get_default_root_window() {
- return Gtk2.gdk_get_default_root_window();
- }
-
- /**
- * Gets the default screen for the default display. (See gdk_display_get_default()).
- *
- * @return a GdkScreen, or NULL if there is no default display.
- *
- * @since 2.2
- */
- public static
- Pointer gdk_screen_get_default() {
- return Gtk2.gdk_screen_get_default();
- }
-
- /**
- * Gets the resolution for font handling on the screen; see gdk_screen_set_resolution() for full details.
- *
- * IE:
- *
- * The resolution for font handling on the screen. This is a scale factor between points specified in a PangoFontDescription and
- * cairo units. The default value is 96, meaning that a 10 point font will be 13 units high. (10 * 96. / 72. = 13.3).
- *
- * @return the current resolution, or -1 if no resolution has been set.
- *
- * @since Since: 2.10
- */
- public static
- double gdk_screen_get_resolution(Pointer screen) {
- return Gtk2.gdk_screen_get_resolution(screen);
- }
-
- /**
- * Makes status_icon display the file filename . See gtk_status_icon_new_from_file() for details.
- *
- * gtk_status_icon_set_from_file has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- public static
- void gtk_status_icon_set_from_file(Pointer widget, String label) {
- Gtk2.gtk_status_icon_set_from_file(widget, label);
- }
-
- /**
- * Shows or hides a status icon.
- *
- * gtk_status_icon_set_visible has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- public static
- void gtk_status_icon_set_visible(Pointer widget, boolean visible) {
- Gtk2.gtk_status_icon_set_visible(widget, visible);
- }
-
-
- /**
- * Sets text as the contents of the tooltip.
- * This function will take care of setting “has-tooltip” to TRUE and of the default handler for the “query-tooltip” signal.
- *
- * app indicators don't support this
- *
- * gtk_status_icon_set_tooltip_text has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- public static
- void gtk_status_icon_set_tooltip_text(Pointer widget, String tooltipText) {
- Gtk2.gtk_status_icon_set_tooltip_text(widget, tooltipText);
- }
-
- /**
- * Sets the title of this tray icon. This should be a short, human-readable, localized string describing the tray icon. It may be used
- * by tools like screen readers to render the tray icon.
- *
- * gtk_status_icon_set_title has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- public static
- void gtk_status_icon_set_title(Pointer widget, String titleText) {
- Gtk2.gtk_status_icon_set_title(widget, titleText);
- }
-
- /**
- * Sets the name of this tray icon. This should be a string identifying this icon. It is may be used for sorting the icons in the
- * tray and will not be shown to the user.
- *
- * gtk_status_icon_set_name has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- public static
- void gtk_status_icon_set_name(Pointer widget, String name) {
- Gtk2.gtk_status_icon_set_name(widget, name);
- }
-
- /**
- * Displays a menu and makes it available for selection.
- *
- * gtk_menu_popup has been deprecated since version 3.22 and should not be used in newly-written code.
- * NOTE: Please use gtk_menu_popup_at_widget(), gtk_menu_popup_at_pointer(). or gtk_menu_popup_at_rect() instead
- */
- public static
- void gtk_menu_popup(Pointer menu, Pointer widget, Pointer bla, Function func, Pointer data, int button, int time) {
- Gtk2.gtk_menu_popup(menu, widget, bla, func, data, button, time);
- }
-
- /**
- * Sets text on the menu_item label
- */
- public static
- void gtk_menu_item_set_label(Pointer menu_item, String label) {
- Gtk2.gtk_menu_item_set_label(menu_item, label);
- }
-
- /**
- * Adds a new GtkMenuItem to the end of the menu shell's item list.
- */
- public static
- void gtk_menu_shell_append(Pointer menu_shell, Pointer child) {
- Gtk2.gtk_menu_shell_append(menu_shell, child);
- }
-
- /**
- * Sets the sensitivity of a widget. A widget is sensitive if the user can interact with it. Insensitive widgets are “grayed out”
- * and the user can’t interact with them. Insensitive widgets are known as “inactive”, “disabled”, or “ghosted” in some other toolkits.
- */
- public static
- void gtk_widget_set_sensitive(Pointer widget, boolean sensitive) {
- Gtk2.gtk_widget_set_sensitive(widget, sensitive);
- }
-
- /**
- * Recursively shows a widget, and any child widgets (if the widget is a container)
- */
- public static
- void gtk_widget_show_all(Pointer widget) {
- Gtk2.gtk_widget_show_all(widget);
- }
-
- /**
- * Removes widget from container . widget must be inside container . Note that container will own a reference to widget , and that
- * this may be the last reference held; so removing a widget from its container can destroy that widget.
- *
- * If you want to use widget again, you need to add a reference to it before removing it from a container, using g_object_ref().
- * If you don’t want to use widget again it’s usually more efficient to simply destroy it directly using gtk_widget_destroy()
- * since this will remove it from the container and help break any circular reference count cycles.
- */
- public static
- void gtk_container_remove(Pointer parentWidget, Pointer widget) {
- Gtk2.gtk_container_remove(parentWidget, widget);
- }
-
- /**
- * Destroys a widget.
- * When a widget is destroyed all references it holds on other objects will be released:
- * - if the widget is inside a container, it will be removed from its parent
- * - if the widget is a container, all its children will be destroyed, recursively
- * - if the widget is a top level, it will be removed from the list of top level widgets that GTK+ maintains internally
- *
- * It's expected that all references held on the widget will also be released; you should connect to the “destroy” signal if you
- * hold a reference to widget and you wish to remove it when this function is called. It is not necessary to do so if you are
- * implementing a GtkContainer, as you'll be able to use the GtkContainerClass.remove() virtual function for that.
- *
- * It's important to notice that gtk_widget_destroy() will only cause the widget to be finalized if no additional references,
- * acquired using g_object_ref(), are held on it. In case additional references are in place, the widget will be in an "inert" state
- * after calling this function; widget will still point to valid memory, allowing you to release the references you hold, but you
- * may not query the widget's own state.
- *
- * NOTE You should typically call this function on top level widgets, and rarely on child widgets.
- */
- public static
- void gtk_widget_destroy(Pointer widget) {
- Gtk2.gtk_widget_destroy(widget);
- }
-
- /**
- * Gets the GtkSettings object for screen , creating it if necessary.
- *
- * @since 2.2
- */
- public static
- Pointer gtk_settings_get_for_screen(Pointer screen) {
- return Gtk2.gtk_settings_get_for_screen(screen);
- }
-
- /**
- * Simply an accessor function that returns @widget->style.
- */
- public static
- GtkStyle.ByReference gtk_widget_get_style(Pointer widget) {
- return Gtk2.gtk_widget_get_style(widget);
- }
-
- /**
- * Finds all matching RC styles for a given widget, composites them together, and then creates a GtkStyle representing the composite
- * appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
- */
- public static
- Pointer gtk_rc_get_style(Pointer widget) {
- return Gtk2.gtk_rc_get_style(widget);
- }
-
- /**
- * Looks up color_name in the style’s logical color mappings, filling in color and returning TRUE if found, otherwise returning
- * FALSE. Do not cache the found mapping, because it depends on the GtkStyle and might change when a theme switch occurs.
- *
- * @since 2.10
- */
- public static
- boolean gtk_style_lookup_color(Pointer widgetStyle, String color_name, Pointer color) {
- return Gtk2.gtk_style_lookup_color(widgetStyle, color_name, color);
- }
-
- /**
- * Adds widget to container . Typically used for simple containers such as GtkWindow, GtkFrame, or GtkButton; for more complicated
- * layout containers such as GtkBox or GtkTable, this function will pick default packing parameters that may not be correct. So
- * consider functions such as gtk_box_pack_start() and gtk_table_attach() as an alternative to gtk_container_add() in those cases.
- * A widget may be added to only one container at a time; you can't place the same widget inside two different containers.
- */
- public static
- void gtk_container_add(Pointer offscreen, Pointer widget) {
- Gtk2.gtk_container_add(offscreen, widget);
- }
-
- /**
- * Get's the child from a GTK Bin object
- */
- public static
- Pointer gtk_bin_get_child(Pointer bin) {
- return Gtk2.gtk_bin_get_child(bin);
- }
-
- /**
- * Gets the PangoLayout used to display the label. The layout is useful to e.g. convert text positions to pixel positions, in
- * combination with gtk_label_get_layout_offsets(). The returned layout is owned by the label so need not be freed by the caller.
- *
- * The label is free to recreate its layout at any time, so it should be considered read-only.
- */
- public static
- Pointer gtk_label_get_layout(Pointer label) {
- return Gtk2.gtk_label_get_layout(label);
- }
-
- /**
- * Computes the logical and ink extents of layout in device units. This function just calls pango_layout_get_extents() followed
- * by two pango_extents_to_pixels() calls, rounding ink_rect and logical_rect such that the rounded rectangles fully contain the
- * unrounded one (that is, passes them as first argument to pango_extents_to_pixels()).
- *
- * @param layout a PangoLayout
- * @param ink_rect rectangle used to store the extents of the layout as drawn or NULL to indicate that the result is not needed.
- * @param logical_rect rectangle used to store the logical extents of the layout or NULL to indicate that the result is not needed.
- */
- public static
- void pango_layout_get_pixel_extents(Pointer layout, Pointer ink_rect, Pointer logical_rect) {
- Gtk2.pango_layout_get_pixel_extents(layout, ink_rect, logical_rect);
- }
-
- /**
- * Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen.
- *
- * @since 2.20
- */
- public static
- Pointer gtk_offscreen_window_new() {
- return Gtk2.gtk_offscreen_window_new();
- }
-
- /**
- * This function is typically used when implementing a GtkContainer subclass. Obtains the preferred size of a widget. The
- * container uses this information to arrange its child widgets and decide what size allocations to give them with
- * gtk_widget_size_allocate().
- *
- * You can also call this function from an application, with some caveats. Most notably, getting a size request requires the
- * widget to be associated with a screen, because font information may be needed. Multihead-aware applications should keep this in mind.
- *
- * Also remember that the size request is not necessarily the size a widget will actually be allocated.
- */
- public static
- void gtk_widget_size_request(final Pointer widget, final Pointer requisition) {
- if (isGtk2) {
- Gtk2.gtk_widget_size_request(widget, requisition);
- }
- else {
- Gtk3.gtk_widget_get_preferred_size(widget, requisition, null);
- }
- }
-
- /**
- * Creates a new GtkImageMenuItem containing the image and text from a stock item. Some stock ids have preprocessor macros
- * like GTK_STOCK_OK and GTK_STOCK_APPLY.
- *
- * @param stock_id the name of the stock item.
- * @param accel_group the GtkAccelGroup to add the menu items accelerator to, or NULL.
- *
- * @return a new GtkImageMenuItem.
- */
- public static
- Pointer gtk_image_menu_item_new_from_stock(String stock_id, Pointer accel_group) {
- return Gtk2.gtk_image_menu_item_new_from_stock(stock_id, accel_group);
- }
-}
-
diff --git a/src/dorkbox/systemTray/jna/linux/Gtk2.java b/src/dorkbox/systemTray/jna/linux/Gtk2.java
deleted file mode 100644
index e6e7db6..0000000
--- a/src/dorkbox/systemTray/jna/linux/Gtk2.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux;
-
-import com.sun.jna.Function;
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.jna.linux.structs.GtkStyle;
-
-/**
- * Bindings for GTK+ 2. Bindings that are exclusively for GTK+ 3 are in that respective class
- *
- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
- */
-public
-class Gtk2 {
- // objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk
-
-
- /**
- * Adds a function to be called whenever there are no higher priority events pending. If the function returns FALSE it is automatically
- * removed from the list of event sources and will not be called again.
- *
- * This variant of g_idle_add_full() calls function with the GDK lock held. It can be thought of a MT-safe version for GTK+ widgets
- * for the following use case, where you have to worry about idle_callback() running in thread A and accessing self after it has
- * been finalized in thread B.
- */
- static native
- int gdk_threads_add_idle_full(int priority, FuncCallback function, Pointer data, Pointer notify);
-
- /**
- * This would NORMALLY have a 2nd argument that is a String[] -- however JNA direct-mapping DOES NOT support this. We are lucky
- * enough that we just pass 'null' as the second argument, therefore, we don't have to define that parameter here.
- */
- static native
- boolean gtk_init_check(int argc);
-
- /**
- * Runs the main loop until gtk_main_quit() is called. You can nest calls to gtk_main(). In that case gtk_main_quit() will make the
- * innermost invocation of the main loop return.
- */
- static native
- void gtk_main();
-
- /**
- * aks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already running
- */
- static native
- int gtk_main_level();
-
- /**
- * Makes the innermost invocation of the main loop return when it regains control. ONLY CALL FROM THE GtkSupport class, UNLESS you know
- * what you're doing!
- */
- static native
- void gtk_main_quit();
-
-
-
-
- /**
- * Creates a new GtkMenu
- */
- public static native
- Pointer gtk_menu_new();
-
- /**
- * Sets or replaces the menu item’s submenu, or removes it when a NULL submenu is passed.
- */
- public static native
- void gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu);
-
- /**
- * Creates a new GtkSeparatorMenuItem.
- */
- public static native
- Pointer gtk_separator_menu_item_new();
-
- /**
- * Creates a new GtkImage displaying the file filename . If the file isn’t found or can’t be loaded, the resulting GtkImage will
- * display a “broken image” icon. This function never returns NULL, it always returns a valid GtkImage widget.
- *
- * If the file contains an animation, the image will contain an animation.
- */
- public static native
- Pointer gtk_image_new_from_file(String iconPath);
-
- /**
- * Sets the active state of the menu item’s check box.
- */
- public static native
- void gtk_check_menu_item_set_active(Pointer check_menu_item, boolean isChecked);
-
- /**
- * Creates a new GtkImageMenuItem containing a label. The label will be created using gtk_label_new_with_mnemonic(), so underscores
- * in label indicate the mnemonic for the menu item.
- *
- * uses '_' to define which key is the mnemonic
- *
- * gtk_image_menu_item_new_with_mnemonic has been deprecated since version 3.10 and should not be used in newly-written code.
- * NOTE: Use gtk_menu_item_new_with_mnemonic() instead.
- */
- @Deprecated
- public static native
- Pointer gtk_image_menu_item_new_with_mnemonic(String label);
-
- public static native
- Pointer gtk_check_menu_item_new_with_mnemonic(String label);
-
- /**
- * Sets the image of image_menu_item to the given widget. Note that it depends on the show-menu-images setting whether the image
- * will be displayed or not.
- *
- * gtk_image_menu_item_set_image has been deprecated since version 3.10 and should not be used in newly-written code.
- */
- @Deprecated
- public static native
- void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image);
-
- /**
- * If TRUE, the menu item will ignore the “gtk-menu-images” setting and always show the image, if available.
- * Use this property if the menuitem would be useless or hard to use without the image
- *
- * gtk_image_menu_item_set_always_show_image has been deprecated since version 3.10 and should not be used in newly-written code.
- */
- @Deprecated
- public static native
- void gtk_image_menu_item_set_always_show_image(Pointer menu_item, boolean forceShow);
-
- /**
- * Creates an empty status icon object.
- *
- * gtk_status_icon_new has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- @Deprecated
- public static native
- Pointer gtk_status_icon_new();
-
- /**
- * Obtains the root window (parent all other windows are inside) for the default display and screen.
- *
- * @return the default root window
- */
- public static native
- Pointer gdk_get_default_root_window();
-
- /**
- * Gets the default screen for the default display. (See gdk_display_get_default()).
- *
- * @return a GdkScreen, or NULL if there is no default display.
- *
- * @since 2.2
- */
- public static native
- Pointer gdk_screen_get_default();
-
- /**
- * Gets the resolution for font handling on the screen; see gdk_screen_set_resolution() for full details.
- *
- * IE:
- *
- * The resolution for font handling on the screen. This is a scale factor between points specified in a PangoFontDescription and
- * cairo units. The default value is 96, meaning that a 10 point font will be 13 units high. (10 * 96. / 72. = 13.3).
- *
- * @return the current resolution, or -1 if no resolution has been set.
- *
- * @since Since: 2.10
- */
- public static native
- double gdk_screen_get_resolution(Pointer screen);
-
- /**
- * Makes status_icon display the file filename . See gtk_status_icon_new_from_file() for details.
- *
- * gtk_status_icon_set_from_file has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- @Deprecated
- public static native
- void gtk_status_icon_set_from_file(Pointer widget, String label);
-
- /**
- * Shows or hides a status icon.
- *
- * gtk_status_icon_set_visible has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- @Deprecated
- public static native
- void gtk_status_icon_set_visible(Pointer widget, boolean visible);
-
-
- /**
- * Sets text as the contents of the tooltip.
- * This function will take care of setting “has-tooltip” to TRUE and of the default handler for the “query-tooltip” signal.
- *
- * app indicators don't support this
- *
- * gtk_status_icon_set_tooltip_text has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- @Deprecated
- public static native
- void gtk_status_icon_set_tooltip_text(Pointer widget, String tooltipText);
-
- /**
- * Sets the title of this tray icon. This should be a short, human-readable, localized string describing the tray icon. It may be used
- * by tools like screen readers to render the tray icon.
- *
- * gtk_status_icon_set_title has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- @Deprecated
- public static native
- void gtk_status_icon_set_title(Pointer widget, String titleText);
-
- /**
- * Sets the name of this tray icon. This should be a string identifying this icon. It is may be used for sorting the icons in the
- * tray and will not be shown to the user.
- *
- * gtk_status_icon_set_name has been deprecated since version 3.14 and should not be used in newly-written code.
- * Use notifications
- */
- @Deprecated
- public static native
- void gtk_status_icon_set_name(Pointer widget, String name);
-
- /**
- * Displays a menu and makes it available for selection.
- *
- * gtk_menu_popup has been deprecated since version 3.22 and should not be used in newly-written code.
- * NOTE: Please use gtk_menu_popup_at_widget(), gtk_menu_popup_at_pointer(). or gtk_menu_popup_at_rect() instead
- */
- @Deprecated
- public static native
- void gtk_menu_popup(Pointer menu, Pointer widget, Pointer bla, Function func, Pointer data, int button, int time);
-
- /**
- * Sets text on the menu_item label
- */
- public static native
- void gtk_menu_item_set_label(Pointer menu_item, String label);
-
- /**
- * Adds a new GtkMenuItem to the end of the menu shell's item list.
- */
- public static native
- void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
-
- /**
- * Sets the sensitivity of a widget. A widget is sensitive if the user can interact with it. Insensitive widgets are “grayed out”
- * and the user can’t interact with them. Insensitive widgets are known as “inactive”, “disabled”, or “ghosted” in some other toolkits.
- */
- public static native
- void gtk_widget_set_sensitive(Pointer widget, boolean sensitive);
-
- /**
- * Recursively shows a widget, and any child widgets (if the widget is a container)
- */
- public static native
- void gtk_widget_show_all(Pointer widget);
-
- /**
- * Removes widget from container . widget must be inside container . Note that container will own a reference to widget , and that
- * this may be the last reference held; so removing a widget from its container can destroy that widget.
- *
- * If you want to use widget again, you need to add a reference to it before removing it from a container, using g_object_ref().
- * If you don’t want to use widget again it’s usually more efficient to simply destroy it directly using gtk_widget_destroy()
- * since this will remove it from the container and help break any circular reference count cycles.
- */
- public static native
- void gtk_container_remove(Pointer parentWidget, Pointer widget);
-
- /**
- * Destroys a widget.
- * When a widget is destroyed all references it holds on other objects will be released:
- * - if the widget is inside a container, it will be removed from its parent
- * - if the widget is a container, all its children will be destroyed, recursively
- * - if the widget is a top level, it will be removed from the list of top level widgets that GTK+ maintains internally
- *
- * It's expected that all references held on the widget will also be released; you should connect to the “destroy” signal if you
- * hold a reference to widget and you wish to remove it when this function is called. It is not necessary to do so if you are
- * implementing a GtkContainer, as you'll be able to use the GtkContainerClass.remove() virtual function for that.
- *
- * It's important to notice that gtk_widget_destroy() will only cause the widget to be finalized if no additional references,
- * acquired using g_object_ref(), are held on it. In case additional references are in place, the widget will be in an "inert" state
- * after calling this function; widget will still point to valid memory, allowing you to release the references you hold, but you
- * may not query the widget's own state.
- *
- * NOTE You should typically call this function on top level widgets, and rarely on child widgets.
- */
- public static native
- void gtk_widget_destroy(Pointer widget);
-
- /**
- * Gets the GtkSettings object for screen , creating it if necessary.
- *
- * @since 2.2
- */
- public static native
- Pointer gtk_settings_get_for_screen(Pointer screen);
-
- /**
- * Simply an accessor function that returns @widget->style.
- */
- public static native
- GtkStyle.ByReference gtk_widget_get_style(Pointer widget);
-
- /**
- * Finds all matching RC styles for a given widget, composites them together, and then creates a GtkStyle representing the composite
- * appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
- */
- public static native
- Pointer gtk_rc_get_style(Pointer widget);
-
- /**
- * Looks up color_name in the style’s logical color mappings, filling in color and returning TRUE if found, otherwise returning
- * FALSE. Do not cache the found mapping, because it depends on the GtkStyle and might change when a theme switch occurs.
- *
- * @since 2.10
- */
- public static native
- boolean gtk_style_lookup_color(Pointer widgetStyle, String color_name, Pointer color);
-
- /**
- * Adds widget to container . Typically used for simple containers such as GtkWindow, GtkFrame, or GtkButton; for more complicated
- * layout containers such as GtkBox or GtkTable, this function will pick default packing parameters that may not be correct. So
- * consider functions such as gtk_box_pack_start() and gtk_table_attach() as an alternative to gtk_container_add() in those cases.
- * A widget may be added to only one container at a time; you can't place the same widget inside two different containers.
- */
- public static native
- void gtk_container_add(Pointer offscreen, Pointer widget);
-
- /**
- * Get's the child from a GTK Bin object
- */
- public static native
- Pointer gtk_bin_get_child(Pointer bin);
-
- /**
- * Gets the PangoLayout used to display the label. The layout is useful to e.g. convert text positions to pixel positions, in
- * combination with gtk_label_get_layout_offsets(). The returned layout is owned by the label so need not be freed by the caller.
- *
- * The label is free to recreate its layout at any time, so it should be considered read-only.
- */
- public static native
- Pointer gtk_label_get_layout(Pointer label);
-
- /**
- * Computes the logical and ink extents of layout in device units. This function just calls pango_layout_get_extents() followed
- * by two pango_extents_to_pixels() calls, rounding ink_rect and logical_rect such that the rounded rectangles fully contain the
- * unrounded one (that is, passes them as first argument to pango_extents_to_pixels()).
- *
- *
- * @param layout a PangoLayout
- * @param ink_rect rectangle used to store the extents of the layout as drawn or NULL to indicate that the result is not needed.
- * @param logical_rect rectangle used to store the logical extents of the layout or NULL to indicate that the result is not needed.
- *
- */
- public static native
- void pango_layout_get_pixel_extents (Pointer layout, Pointer ink_rect, Pointer logical_rect);
-
- /**
- * Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen.
- *
- * @since 2.20
- */
- public static native
- Pointer gtk_offscreen_window_new ();
-
- /**
- * This function is typically used when implementing a GtkContainer subclass. Obtains the preferred size of a widget. The
- * container uses this information to arrange its child widgets and decide what size allocations to give them with
- * gtk_widget_size_allocate().
- *
- * You can also call this function from an application, with some caveats. Most notably, getting a size request requires the
- * widget to be associated with a screen, because font information may be needed. Multihead-aware applications should keep this in mind.
- *
- * Also remember that the size request is not necessarily the size a widget will actually be allocated.
- */
- @Deprecated
- public static native
- void gtk_widget_size_request(final Pointer widget, final Pointer requisition);
-
- /**
- * Creates a new GtkImageMenuItem containing the image and text from a stock item. Some stock ids have preprocessor macros
- * like GTK_STOCK_OK and GTK_STOCK_APPLY.
- *
- * @param stock_id the name of the stock item.
- * @param accel_group the GtkAccelGroup to add the menu items accelerator to, or NULL.
- *
- * @return a new GtkImageMenuItem.
- */
- public static native
- Pointer gtk_image_menu_item_new_from_stock(String stock_id, Pointer accel_group);
-}
diff --git a/src/dorkbox/systemTray/jna/linux/Gtk3.java b/src/dorkbox/systemTray/jna/linux/Gtk3.java
deleted file mode 100644
index 66e544b..0000000
--- a/src/dorkbox/systemTray/jna/linux/Gtk3.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux;
-
-import com.sun.jna.Pointer;
-
-/**
- * bindings for GTK+ 3.
- *
- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md
- */
-public
-class Gtk3 {
- // objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
- // objdump -T /usr/local/lib/libgtk-3.so.0 | grep gtk
-
- public static native
- int gtk_get_major_version();
-
- public static native
- int gtk_get_minor_version();
-
- public static native
- int gtk_get_micro_version();
-
-
- /**
- * Loads a theme from the usual theme paths
- *
- * @param name A theme name
- * @param variant variant to load, for example, "dark", or NULL for the default.
- *
- * @return a GtkCssProvider with the theme loaded. This memory is owned by GTK+, and you must not free it.
- *
- * @since 3.0
- */
- public static native
- Pointer gtk_css_provider_get_named(String name, String variant);
-
- /**
- * Returns the provider containing the style settings used as a fallback for all widgets.
- *
- * @return a GtkCssProvider with the theme loaded. This memory is owned by GTK+, and you must not free it.
- *
- * @since 3.0
- */
- public static native
- Pointer gtk_css_provider_get_default();
-
- /**
- * Converts the provider into a string representation in CSS format.
- *
- * Using gtk_css_provider_load_from_data() with the return value from this function on a new provider created with
- * gtk_css_provider_new() will basically create a duplicate of this provider .
- *
- * @since 3.2 (released in 2011)
- */
- public static native
- String gtk_css_provider_to_string(Pointer provider);
-
- /**
- * Gets the foreground color for a given state.
- *
- * @since 3.0
- */
- public static native
- void gtk_style_context_get_color(Pointer context, int stateFlags, Pointer color);
-
- /**
- * Returns the state used for style matching.
- *
- * @since 3.0
- */
- public static native
- int gtk_style_context_get_state(Pointer context);
-
- /**
- * Looks up and resolves a color name in the context color map.
- *
- * @since 3.0 (but not in the documentation...)
- */
- public static native
- boolean gtk_style_context_lookup_color(Pointer widget, String name, Pointer color);
-
- /**
- * Returns the style context associated to widget . The returned object is guaranteed to be the same for the lifetime of widget .
- *
- * @since 3.0 (but not in the documentation...)
- */
- public static native
- Pointer gtk_widget_get_style_context(Pointer widget);
-
- /**
- * Saves the context state, so temporary modifications done through gtk_style_context_add_class(), gtk_style_context_remove_class(),
- * gtk_style_context_set_state(), etc. can quickly be reverted in one go through gtk_style_context_restore().
- *
- * The matching call to gtk_style_context_restore() must be done before GTK returns to the main loop.
- *
- * @since 3.0
- */
- public static native
- void gtk_style_context_save(Pointer context);
-
-
- /**
- * Restores context state to a previous stage. See gtk_style_context_save().
- *
- * @since 3.0
- */
- public static native
- void gtk_style_context_restore(Pointer context);
-
- /**
- * Adds a style class to context , so posterior calls to gtk_style_context_get() or any of the gtk_render_*() functions will make
- * use of this new class for styling.
- *
- * @since 3.0
- */
- public static native
- void gtk_style_context_add_class(Pointer context, String className);
-
- /**
- * Gets the padding for a given state as a GtkBorder. See gtk_style_context_get() and GTK_STYLE_PROPERTY_PADDING for details.
- *
- * @since 3.0
- */
- public static native
- void gtk_style_context_get_padding(Pointer context, int state, Pointer border);
-
- /**
- * Gets the border for a given state as a GtkBorder.
- *
- * See gtk_style_context_get_property() and GTK_STYLE_PROPERTY_BORDER_WIDTH for details.
- *
- * @since 3.0
- */
- public static native
- void gtk_style_context_get_border(Pointer context, int state, Pointer border);
-
- /**
- * Returns the internal scale factor that maps from window coordinates to the actual device pixels. On traditional systems this is 1,
- * but on very high density outputs this can be a higher value (often 2).
- *
- * A higher value means that drawing is automatically scaled up to a higher resolution, so any code doing drawing will automatically
- * look nicer. However, if you are supplying pixel-based data the scale value can be used to determine whether to use a pixel
- * resource with higher resolution data.
- *
- * The scale of a window may change during runtime, if this happens a configure event will be sent to the toplevel window.
- *
- * @return the scale factor
- *
- * @since 3.10
- */
- public static native
- int gdk_window_get_scale_factor(Pointer window);
-
- /**
- * Retrieves the minimum and natural size of a widget, taking into account the widget’s preference for height-for-width management.
- *
- * This is used to retrieve a suitable size by container widgets which do not impose any restrictions on the child placement.
- * It can be used to deduce toplevel window and menu sizes as well as child widgets in free-form containers such as GtkLayout.
- *
- * Handle with care. Note that the natural height of a height-for-width widget will generally be a smaller size than the minimum
- * height, since the required height for the natural width is generally smaller than the required height for the minimum width.
- *
- * Use gtk_widget_get_preferred_height_and_baseline_for_width() if you want to support baseline alignment.
- *
- * @param widget a GtkWidget instance
- * @param minimum_size location for storing the minimum size, or NULL.
- * @param natural_size location for storing the natural size, or NULL.
- */
- public static native
- void gtk_widget_get_preferred_size(final Pointer widget, final Pointer minimum_size, final Pointer natural_size);
-}
diff --git a/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java b/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java
deleted file mode 100644
index e728d27..0000000
--- a/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux;
-
-import static dorkbox.systemTray.SystemTray.logger;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.LinkedList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import com.sun.jna.Pointer;
-
-import dorkbox.systemTray.Entry;
-import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.util.JavaFX;
-import dorkbox.systemTray.util.Swt;
-
-public
-class GtkEventDispatch {
- // have to save these in a field to prevent GC on the objects (since they go out-of-scope from java)
- private static final LinkedList gtkCallbacks = new LinkedList();
-
- // This is required because the EDT needs to have it's own value for this boolean, that is a different value than the main thread
- private static ThreadLocal isDispatch = new ThreadLocal() {
- @Override
- protected
- Boolean initialValue() {
- return false;
- }
- };
-
- private static volatile boolean started = false;
-
- @SuppressWarnings("FieldCanBeLocal")
- private static Thread gtkUpdateThread = null;
-
- // when debugging the EDT, we need a longer timeout.
- private static final boolean debugEDT = true;
-
- // timeout is in seconds
- private static final int TIMEOUT = debugEDT ? 10000000 : 2;
-
-
- public static
- void startGui() {
- // only permit one startup per JVM instance
- if (!started) {
- started = true;
-
- // startup the GTK GUI event loop. There can be multiple/nested loops.
-
-
- if (!Gtk.alreadyRunningGTK) {
- // If JavaFX/SWT is used, this is UNNECESSARY (we can detect if the GTK main_loop is running)
-
- gtkUpdateThread = new Thread() {
- @Override
- public
- void run() {
- Glib.GLogFunc orig = null;
- if (SystemTray.DEBUG) {
- logger.debug("Running GTK Native Event Loop");
- } else {
- // NOTE: This can output warnings, so we suppress them
- orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
- }
-
-
- // prep for the event loop.
- // GThread.g_thread_init(null); would be needed for g_idle_add()
-
- if (!Gtk2.gtk_init_check(0)) {
- if (SystemTray.DEBUG) {
- logger.error("Error starting GTK");
- }
- return;
- }
-
- // gdk_threads_enter(); would be needed for g_idle_add()
-
- if (orig != null) {
- Glib.g_log_set_default_handler(orig, null);
- }
-
- // blocks unit quit
- Gtk2.gtk_main();
-
- // clean up threads
- // gdk_threads_leave(); would be needed for g_idle_add()
- }
- };
- gtkUpdateThread.setDaemon(false); // explicitly NOT daemon so that this will hold the JVM open as necessary
- gtkUpdateThread.setName("GTK Native Event Loop");
- gtkUpdateThread.start();
- }
- }
- }
-
- /**
- * Waits for the all posted events to GTK to finish loading
- */
- @SuppressWarnings("Duplicates")
- public static
- void waitForEventsToComplete() {
- final CountDownLatch blockUntilStarted = new CountDownLatch(1);
-
- dispatch(new Runnable() {
- @Override
- public
- void run() {
- blockUntilStarted.countDown();
- }
- });
-
- if (SystemTray.isJavaFxLoaded) {
- if (!JavaFX.isEventThread()) {
- try {
- if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
- if (SystemTray.DEBUG) {
- SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
- new Exception(""));
- }
- }
-
- // we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
- while (true) {
- Thread.sleep(100);
-
- synchronized (gtkCallbacks) {
- if (gtkCallbacks.isEmpty()) {
- break;
- }
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- else if (SystemTray.isSwtLoaded) {
- if (!Swt.isEventThread()) {
- // we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
- try {
- if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
- if (SystemTray.DEBUG) {
- SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
- new Exception(""));
- }
- }
-
- while (true) {
- Thread.sleep(100);
-
- synchronized (gtkCallbacks) {
- if (gtkCallbacks.isEmpty()) {
- break;
- }
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- else {
- try {
- if (!blockUntilStarted.await(10, TimeUnit.SECONDS)) {
- if (SystemTray.DEBUG) {
- SystemTray.logger.error("Something is very wrong. The waitForEventsToComplete took longer than expected.",
- new Exception(""));
- }
- }
-
- // we have to WAIT until all events are done processing, OTHERWISE we have initialization issues
- while (true) {
- Thread.sleep(100);
-
- synchronized (gtkCallbacks) {
- if (gtkCallbacks.isEmpty()) {
- break;
- }
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * Best practices for GTK, is to call EVERYTHING for it on the GTK THREAD. This accomplishes that.
- */
- public static
- void dispatch(final Runnable runnable) {
- if (Gtk.alreadyRunningGTK) {
- if (SystemTray.isJavaFxLoaded) {
- // JavaFX only
- if (JavaFX.isEventThread()) {
- // Run directly on the JavaFX event thread
- runnable.run();
- }
- else {
- JavaFX.dispatch(runnable);
- }
-
- return;
- }
-
- if (SystemTray.isSwtLoaded) {
- if (Swt.isEventThread()) {
- // Run directly on the SWT event thread. If it's not on the dispatch thread, we can use raw GTK to put it there
- runnable.run();
-
- return;
- }
- }
- }
-
- // not javafx
- // gtk/swt are **mostly** the same in how events are dispatched, so we can use "raw" gtk methods for SWT
- if (isDispatch.get()) {
- // Run directly on the dispatch thread
- runnable.run();
- }
- else {
- final FuncCallback callback = new FuncCallback() {
- @Override
- public
- int callback(final Pointer data) {
- synchronized (gtkCallbacks) {
- gtkCallbacks.removeFirst(); // now that we've 'handled' it, we can remove it from our callback list
- }
-
- isDispatch.set(true);
-
- try {
- runnable.run();
- } finally {
- isDispatch.set(false);
- }
-
- return Gtk.FALSE; // don't want to call this again
- }
- };
-
- synchronized (gtkCallbacks) {
- gtkCallbacks.offer(callback); // prevent GC from collecting this object before it can be called
- }
-
- // the correct way to do it. Add with a slightly higher value
- Gtk2.gdk_threads_add_idle_full(100, callback, null, null);
- }
- }
-
- public static
- void shutdownGui() {
- dispatchAndWait(new Runnable() {
- @Override
- public
- void run() {
- // If JavaFX/SWT is used, this is UNNECESSARY (and will break SWT/JavaFX shutdown)
- if (!Gtk.alreadyRunningGTK) {
- Gtk2.gtk_main_quit();
- }
-
- started = false;
- }
- });
- }
-
- public static
- void dispatchAndWait(final Runnable runnable) {
- if (isDispatch.get()) {
- // Run directly on the dispatch thread (should not "redispatch" this again)
- runnable.run();
- }
- else {
- final CountDownLatch countDownLatch = new CountDownLatch(1);
-
- dispatch(new Runnable() {
- @Override
- public
- void run() {
- try {
- runnable.run();
- } catch (Exception e) {
- SystemTray.logger.error("Error during GTK run loop: ", e);
- } 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)) {
- if (SystemTray.DEBUG) {
- SystemTray.logger.error(
- "Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
- "to complete.", new Exception(""));
- }
- else {
- throw new RuntimeException("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT +
- " seconds " + "to complete.");
- }
- }
- } catch (InterruptedException e) {
- SystemTray.logger.error("Error waiting for dispatch to complete.", new Exception(""));
- }
- }
- }
-
- /**
- * required to properly setup the dispatch flag when using native menus
- *
- * @param callback will never be null.
- */
- public static
- void proxyClick(final Entry menuEntry, final ActionListener callback) {
- isDispatch.set(true);
-
- try {
- if (menuEntry != null) {
- callback.actionPerformed(new ActionEvent(menuEntry, ActionEvent.ACTION_PERFORMED, ""));
- }
- else {
- // checkbox entries will not pass the menuEntry in, because they redispatch the click event so that the checkbox state is
- // toggled
- callback.actionPerformed(null);
- }
- } finally {
- isDispatch.set(false);
- }
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java
deleted file mode 100644
index afdd638..0000000
--- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java
+++ /dev/null
@@ -1,1272 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux;
-
-import static dorkbox.systemTray.util.CssParser.injectAdditionalCss;
-import static dorkbox.systemTray.util.CssParser.removeComments;
-
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import com.sun.jna.Pointer;
-import com.sun.jna.ptr.PointerByReference;
-
-import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.Tray;
-import dorkbox.systemTray.jna.linux.structs.GdkColor;
-import dorkbox.systemTray.jna.linux.structs.GdkRGBAColor;
-import dorkbox.systemTray.jna.linux.structs.GtkRequisition;
-import dorkbox.systemTray.jna.linux.structs.GtkStyle;
-import dorkbox.systemTray.jna.linux.structs.PangoRectangle;
-import dorkbox.systemTray.util.CssParser;
-import dorkbox.systemTray.util.CssParser.Css;
-import dorkbox.systemTray.util.CssParser.CssNode;
-import dorkbox.systemTray.util.CssParser.Entry;
-import dorkbox.util.FileUtil;
-import dorkbox.util.MathUtil;
-import dorkbox.util.OS;
-import dorkbox.util.OSUtil;
-import dorkbox.util.process.ShellProcessBuilder;
-
-/**
- * Class to contain all of the methods needed to get the text color from the AppIndicator/GtkStatusIcon menu entry. This is primarily
- * used to get the color needed for the checkmark icon. In GTK, the checkmark icon can be defined to be it's OWN color and
- * shape, however getting/parsing that would be even significantly more difficult -- so we decided to make the icon the same color
- * as the text.
- *
- * Additionally, CUSTOM, user theme modifications in ~/.gtkrc-2.0 (for example), will be ignored.
- *
- * Also note: not all themes have CSS or Theme files!!!
- */
-@SuppressWarnings({"deprecation", "WeakerAccess"})
-public
-class GtkTheme {
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_SHOW_CSS = false;
- private static final boolean DEBUG_VERBOSE = false;
-
- // CSS nodes that we care about, in oder of preference from left to right.
- private static final
- String[] cssNodes = new String[] {".menuitem", ".entry", "*"};
-
- public static
- Rectangle getPixelTextHeight(String text) {
- // have to use pango to get the size of text (for the checkmark size)
- Pointer offscreen = Gtk.gtk_offscreen_window_new();
-
- // we use the size of "X" as the checkmark
- Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
-
- Gtk.gtk_container_add(offscreen, item);
-
- // get the text widget (GtkAccelLabel) from inside the GtkMenuItem
- Pointer textLabel = Gtk.gtk_bin_get_child(item);
- Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel);
-
- // ink pixel size is how much exact space it takes on the screen
- PangoRectangle ink = new PangoRectangle();
-
- Gtk.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null);
- ink.read();
-
- Rectangle size = new Rectangle(ink.width, ink.height);
-
- Gtk.gtk_widget_destroy(item);
- Gtk.gtk_widget_destroy(offscreen);
-
- return size;
- }
-
- public static
- Rectangle getLogicalTextHeight(String text) {
- // have to use pango to get the size of text (for the checkmark size)
- Pointer offscreen = Gtk.gtk_offscreen_window_new();
-
- // we use the size of "X" as the checkmark
- Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
-
- Gtk.gtk_container_add(offscreen, item);
-
- // get the text widget (GtkAccelLabel) from inside the GtkMenuItem
- Pointer textLabel = Gtk.gtk_bin_get_child(item);
- Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel);
-
- // logical pixel size (ascent + descent)
- PangoRectangle logical = new PangoRectangle();
-
- Gtk.pango_layout_get_pixel_extents(pangoLayout, null, logical.getPointer());
- logical.read();
-
- Rectangle size = new Rectangle(logical.width, logical.height);
-
- Gtk.gtk_widget_destroy(item);
- Gtk.gtk_widget_destroy(offscreen);
-
- return size;
- }
-
- /**
- * @return the size of the GTK menu entry's IMAGE, as best as we can tell, for determining how large of icons to use for the menu entry
- */
- public static
- int getMenuEntryImageSize() {
- final AtomicReference imageHeight = new AtomicReference();
-
- GtkEventDispatch.dispatchAndWait(new Runnable() {
- @Override
- public
- void run() {
- Pointer offscreen = Gtk.gtk_offscreen_window_new();
-
- // get the default icon size for the "paste" icon.
- Pointer item = Gtk.gtk_image_menu_item_new_from_stock("gtk-paste", null);
-
- Gtk.gtk_container_add(offscreen, item);
-
- PointerByReference r = new PointerByReference();
- Gobject.g_object_get(item, "image", r.getPointer(), null);
-
- Pointer imageWidget = r.getValue();
- GtkRequisition gtkRequisition = new GtkRequisition();
- Gtk.gtk_widget_size_request(imageWidget, gtkRequisition.getPointer());
- gtkRequisition.read();
-
- imageHeight.set(gtkRequisition.height);
- }
- });
-
- int height = imageHeight.get();
- if (height > 0) {
- return height;
- }
- else {
- return 16; // who knows?
- }
- }
-
- /**
- * Gets the system tray indicator size.
- * - AppIndicator: will properly scale the image if it's not the correct size
- * - GtkStatusIndicator: ??
- */
- public static
- int getIndicatorSize(final Class extends Tray> trayType) {
- // Linux is similar enough, that it just uses this method
- // https://wiki.archlinux.org/index.php/HiDPI
-
- // 96 DPI is the default
- final double defaultDPI = 96.0;
-
- final AtomicReference screenScale = new AtomicReference();
- final AtomicInteger screenDPI = new AtomicInteger();
-
- GtkEventDispatch.dispatchAndWait(new Runnable() {
- @Override
- public
- void run() {
- // screen DPI
- Pointer screen = Gtk.gdk_screen_get_default();
- if (screen != null) {
- // this call makes NO SENSE, but reading the documentation shows it is the CORRECT call.
- screenDPI.set((int) Gtk.gdk_screen_get_resolution(screen));
- }
-
- if (Gtk.isGtk3) {
- Pointer window = Gtk.gdk_get_default_root_window();
- if (window != null) {
- screenScale.set((double) Gtk3.gdk_window_get_scale_factor(window));
- }
- }
- }
- });
-
- // fallback
- if (screenDPI.get() == 0) {
- // GET THE DPI IN LINUX
- // https://wiki.archlinux.org/index.php/Talk:GNOME
- Object detectedValue = Toolkit.getDefaultToolkit().getDesktopProperty("gnome.Xft/DPI");
- if (detectedValue instanceof Integer) {
- int dpi = ((Integer) detectedValue) / 1024;
- if (dpi == -1) {
- screenDPI.set((int) defaultDPI);
- }
- if (dpi < 50) {
- // 50 dpi is the minimum value gnome allows
- screenDPI.set(50);
- }
- }
- }
-
-
- // check system ENV variables.
- if (screenScale.get() == 0) {
- String envVar = System.getenv("QT_AUTO_SCREEN_SCALE_FACTOR");
- if (envVar != null) {
- try {
- screenScale.set(Double.parseDouble(envVar));
- } catch (Exception ignored) {
- }
- }
- }
-
- // check system ENV variables.
- if (screenScale.get() == 0) {
- String envVar = System.getenv("QT_SCALE_FACTOR");
- if (envVar != null) {
- try {
- screenScale.set(Double.parseDouble(envVar));
- } catch (Exception ignored) {
- }
- }
- }
-
- // check system ENV variables.
- if (screenScale.get() == 0) {
- String envVar = System.getenv("GDK_SCALE");
- if (envVar != null) {
- try {
- screenScale.set(Double.parseDouble(envVar));
- } catch (Exception ignored) {
- }
- }
- }
-
- // check system ENV variables.
- if (screenScale.get() == 0) {
- String envVar = System.getenv("ELM_SCALE");
- if (envVar != null) {
- try {
- screenScale.set(Double.parseDouble(envVar));
- } catch (Exception ignored) {
- }
- }
- }
-
-
-
- OSUtil.DesktopEnv.Env env = OSUtil.DesktopEnv.get();
- // sometimes the scaling-factor is set
- if (env == OSUtil.DesktopEnv.Env.Gnome) {
- try {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
- PrintStream outputStream = new PrintStream(byteArrayOutputStream);
-
- // gsettings get org.gnome.desktop.interface scaling-factor
- final ShellProcessBuilder shellVersion = new ShellProcessBuilder(outputStream);
- shellVersion.setExecutable("gsettings");
- shellVersion.addArgument("get");
- shellVersion.addArgument("org.gnome.desktop.interface");
- shellVersion.addArgument("scaling-factor");
- shellVersion.start();
-
- String output = ShellProcessBuilder.getOutput(byteArrayOutputStream);
-
- if (!output.isEmpty()) {
- // DEFAULT icon size is 16. HiDpi changes this scale, so we should use it as well.
- // should be: uint32 0 or something
- if (output.contains("uint32")) {
- String value = output.substring(output.indexOf("uint") + 7, output.length());
-
- // 0 is disabled (no scaling)
- // 1 is enabled (default scale)
- // 2 is 2x scale
- // 3 is 3x scale
- // etc
-
- double scalingFactor = Double.parseDouble(value);
- if (scalingFactor >= 1) {
- screenScale.set(scalingFactor);
- }
-
- // A setting of 2, 3, etc, which is all you can do with scaling-factor
- // To enable HiDPI, use gsettings:
- // gsettings set org.gnome.desktop.interface scaling-factor 2
- }
- }
- } catch (Throwable ignore) {
- }
- }
- else if (OSUtil.DesktopEnv.isKDE()) {
- // check the custom KDE override file
- try {
- File customSettings = new File("/usr/bin/startkde-custom");
- if (customSettings.canRead()) {
- List lines = FileUtil.readLines(customSettings);
- for (String line : lines) {
- String str = "export GDK_SCALE=";
- int i = line.indexOf(str);
- if (i > -1) {
- String scale = line.substring(i + str.length());
- double scalingFactor = Double.parseDouble(scale);
- if (scalingFactor >= 1) {
- screenScale.set(scalingFactor);
- break;
- }
- }
- }
- }
- } catch (Exception ignored) {
- }
- }
-
-// System.err.println("screen scale: " + screenScale.get());
-// System.err.println("screen DPI: " + screenDPI.get());
-
- if (OSUtil.DesktopEnv.isKDE()) {
- /*
- *
- * Looking in plasma-framework/src/declarativeimports/core/units.cpp:
- // Scale the icon sizes up using the devicePixelRatio
- // This function returns the next stepping icon size
- // and multiplies the global settings with the dpi ratio.
- const qreal ratio = devicePixelRatio();
-
- if (ratio < 1.5) {
- return size;
- } else if (ratio < 2.0) {
- return size * 1.5;
- } else if (ratio < 2.5) {
- return size * 2.0;
- } else if (ratio < 3.0) {
- return size * 2.5;
- } else if (ratio < 3.5) {
- return size * 3.0;
- } else {
- return size * ratio;
- }
-My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 factor existing. Is it reasonable? Wouldn't it make more sense to use the factor the closest to the ratio rather than what is done here?
-
- */
-
- File mainFile = new File("/usr/share/plasma/plasmoids/org.kde.plasma.private.systemtray/contents/config/main.xml");
- if (mainFile.canRead()) {
- List lines = FileUtil.readLines(mainFile);
- boolean found = false;
- int index = 0;
- for (final String line : lines) {
- if (line.contains("")) {
- found = true;
- // have to get the "default" line value
- }
-
- String str = "";
- if (found && (index = line.indexOf(str)) > -1) {
- // this is our line. now get the value.
- String substring = line.substring(index + str.length(), line.indexOf(" ", index));
-
- if (MathUtil.isInteger(substring)) {
- // Default icon size for the systray icons, it's an enum which values mean,
- // Small, SmallMedium, Medium, Large, Huge, Enormous respectively.
- // On low DPI systems they correspond to :
- // 16, 22, 32, 48, 64, 128 pixels.
- // On high DPI systems those values would be scaled up, depending on the DPI.
- int imageSize = 0;
- int imageSizeEnum = Integer.parseInt(substring);
- switch (imageSizeEnum) {
- case 0:
- imageSize = 16;
- break;
- case 1:
- imageSize = 22;
- break;
- case 2:
- imageSize = 32;
- break;
- case 3:
- imageSize = 48;
- break;
- case 4:
- imageSize = 64;
- break;
- case 5:
- imageSize = 128;
- break;
- }
-
- if (imageSize > 0) {
- double scaleRatio = screenDPI.get() / defaultDPI;
-
- return (int) (scaleRatio * imageSize);
- }
- }
- }
- }
- }
- }
- else {
- if (OSUtil.Linux.isUbuntu() && env == OSUtil.DesktopEnv.Env.Unity) {
- // if we measure on ubuntu unity using a screen shot (using swing, so....) , the max size was 24, HOWEVER this goes from
- // the top->bottom of the indicator bar -- and since it was swing, it uses a different rendering method and it (honestly)
- // looks weird, because there is no padding for the icon. The official AppIndicator size is hardcoded...
- // http://bazaar.launchpad.net/~indicator-applet-developers/libindicator/trunk.16.10/view/head:/libindicator/indicator-image-helper.c
-
- return 22;
- }
- else {
- // xfce is easy, because it's not a GTK setting for the size (xfce notification area maximum icon size)
- if (env == OSUtil.DesktopEnv.Env.XFCE) {
- String properties = OSUtil.DesktopEnv.queryXfce("xfce4-panel", null);
- List propertiesAsList = Arrays.asList(properties.split(OS.LINE_SEPARATOR));
- for (String prop : propertiesAsList) {
- if (prop.startsWith("/plugins/") && prop.endsWith("/size-max")) {
- // this is the property we are looking for (we just don't know which panel it's on)
-
- String size = OSUtil.DesktopEnv.queryXfce("xfce4-panel", prop);
- try {
- return Integer.parseInt(size);
- } catch (Exception e) {
- SystemTray.logger.error("Unable to get XFCE notification panel size for channel '{}', property '{}'",
- "xfce4-panel", prop, e);
- }
- }
- }
-
- // default...
- return 22;
- }
-
-
- // try to use GTK to get the tray icon size
- final AtomicInteger traySize = new AtomicInteger();
-
- GtkEventDispatch.dispatchAndWait(new Runnable() {
- @Override
- public
- void run() {
- Pointer screen = Gtk.gdk_screen_get_default();
- Pointer settings = null;
-
- if (screen != null) {
- settings = Gtk.gtk_settings_get_for_screen(screen);
- }
-
- if (settings != null) {
- PointerByReference pointer = new PointerByReference();
-
- // https://wiki.archlinux.org/index.php/GTK%2B
- // To use smaller icons, use a line like this:
- // gtk-icon-sizes = "panel-menu=16,16:panel=16,16:gtk-menu=16,16:gtk-large-toolbar=16,16:gtk-small-toolbar=16,16:gtk-button=16,16"
-
- // this gets icon sizes. On XFCE, ubuntu, it returns "panel-menu-bar=24,24"
- // NOTE: gtk-icon-sizes is deprecated and ignored since GTK+ 3.10.
-
- // A list of icon sizes. The list is separated by colons, and item has the form: size-name = width , height
- Gobject.g_object_get(settings, "gtk-icon-sizes", pointer.getPointer(), null);
-
- Pointer value = pointer.getValue();
- if (value != null) {
- String iconSizes = value.getString(0);
- String[] strings = new String[] {"panel-menu-bar=", "panel=", "gtk-large-toolbar=", "gtk-small-toolbar="};
- for (String var : strings) {
- int i = iconSizes.indexOf(var);
- if (i >= 0) {
- String size = iconSizes.substring(i + var.length(), iconSizes.indexOf(",", i));
-
- if (MathUtil.isInteger(size)) {
- traySize.set(Integer.parseInt(size));
- return;
- }
- }
- }
- }
- }
- }
- });
-
- int i = traySize.get();
- if (i != 0) {
- return i;
- }
- }
- }
-
- // sane default
- return 22;
- }
-
- /**
- * @return the widget color of text for the current theme, or black. It is important that this is called AFTER GTK has been initialized.
- */
- public static
- Color getTextColor() {
- // NOTE: when getting CSS, we redirect STDERR to null (via GTK), so that we won't spam the console if there are parse errors.
- // this is a horrid hack, but the only way to work around the errors we have no control over. The parse errors, if bad enough
- // just mean that we are unable to get the CSS as we want.
-
- // these methods are from most accurate (but limited in support) to compatible across Linux OSes.. Strangely enough, GTK makes
- // this information insanely difficult to get.
- final AtomicReference color = new AtomicReference(null);
- GtkEventDispatch.dispatchAndWait(new Runnable() {
- @SuppressWarnings("UnusedAssignment")
- @Override
- public
- void run() {
- Color c;
-
- // see if we can get the info via CSS properties (> GTK+ 3.2 uses an API, GTK2 gets it from disk).
- // This is often the BEST way to get information, since GTK **DOES NOT** make it easy to get widget information BEFORE
- // the widget is realized -- which in our case, we must do.
- c = getColorFromCss();
- if (c != null) {
- if (DEBUG) {
- System.err.println("Got from CSS");
- }
- color.set(c);
- return;
- }
-
-
- // try to get via the color scheme.
- c = getFromColorScheme();
- if (c != null) {
- if (DEBUG) {
- System.err.println("Got from color scheme");
- }
- color.set(c);
- return;
- }
-
-
- // if we get here, this means that there was NO "gtk-color-scheme" value in the theme file.
- // This usually happens when the theme does not have @fg_color (for example), but instead has each color explicitly
- // defined for every widget instance in the theme file. Old/bizzare themes tend to do it this way...
- if (Gtk.isGtk2) {
- c = getFromGtk2ThemeText();
- if (c != null) {
- if (DEBUG) {
- System.err.println("Got from gtk2 color theme file");
- }
- color.set(c);
- return;
- }
- }
-
-
- // the following methods all require an offscreen widget to get the style information from.
-
- // create an off-screen widget (don't forget to destroy everything!)
- Pointer offscreen = Gtk.gtk_offscreen_window_new();
- Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic("a");
-
- Gtk.gtk_container_add(offscreen, item);
- Gtk.gtk_widget_show_all(item);
-
- // Try to get via RC style... Sometimes this works (sometimes it does not...)
- {
- Pointer style = Gtk.gtk_rc_get_style(item);
-
- GdkColor gdkColor = new GdkColor();
- boolean success = false;
-
- success = Gtk.gtk_style_lookup_color(style, "menu_fg_color", gdkColor.getPointer());
- if (!success) {
- success = Gtk.gtk_style_lookup_color(style, "text_color", gdkColor.getPointer());
- if (success) {
- System.err.println("a");
- }
- }
- if (!success) {
- success = Gtk.gtk_style_lookup_color(style, "theme_text_color", gdkColor.getPointer());
- if (success) {
- System.err.println("a");
- }
- }
- if (success) {
- c = gdkColor.getColor();
- }
- }
-
- if (c != null) {
- if (DEBUG) {
- System.err.println("Got from gtk offscreen gtk_style_lookup_color");
- }
- color.set(c);
- Gtk.gtk_widget_destroy(item);
- return;
- }
-
-
-
- if (Gtk.isGtk3) {
- Pointer context = Gtk3.gtk_widget_get_style_context(item);
-
- GdkRGBAColor gdkColor = new GdkRGBAColor();
- boolean success = false;
-
- success = Gtk3.gtk_style_context_lookup_color(context, "fg_color", gdkColor.getPointer());
- if (!success) {
- success = Gtk3.gtk_style_context_lookup_color(context, "text_color", gdkColor.getPointer());
- }
- if (!success) {
- success = Gtk3.gtk_style_context_lookup_color(context, "menu_fg_color", gdkColor.getPointer());
- }
-
- if (!success) {
- success = Gtk3.gtk_style_context_lookup_color(context, "color", gdkColor.getPointer());
- }
-
- if (success) {
- c = gdkColor.getColor();
- }
- }
-
- if (c != null) {
- color.set(c);
- if (DEBUG) {
- System.err.println("Got from gtk3 offscreen gtk_widget_get_style_context");
- }
- Gtk.gtk_widget_destroy(item);
- return;
- }
-
- // this doesn't always work...
- GtkStyle.ByReference style = Gtk.gtk_widget_get_style(item);
- color.set(style.text[Gtk.State.NORMAL].getColor());
-
- if (DEBUG) {
- System.err.println("Got from gtk gtk_widget_get_style");
- }
-
- Gtk.gtk_widget_destroy(item);
- }
- });
-
-
- Color c = color.get();
- if (c != null) {
- if (DEBUG) {
- System.err.println("COLOR FOUND: " + c);
- }
- return c;
- }
-
- SystemTray.logger.error("Unable to determine the text color in use by your system. Please create an issue and include your " +
- "full OS configuration and desktop environment, including theme details, such as the theme name, color " +
- "variant, and custom theme options (if any).");
-
- // who knows WHAT the color is supposed to be. This is just a "best guess" default value.
- return Color.BLACK;
- }
-
- /**
- * get the color we are interested in via raw CSS parsing. This is specifically to get the color of the text of the
- * appindicator/gtk-status-icon menu.
- *
- * > GTK+ 3.2 uses an API, GTK2 gets it from disk
- *
- * @return the color string, parsed from CSS,
- */
- private static
- Color getColorFromCss() {
- Css css = getCss();
- if (css != null) {
- if (DEBUG_SHOW_CSS) {
- System.err.println(css);
- }
-
- try {
- // collect a list of all of the sections that have what we are interested in.
- List sections = CssParser.getSections(css, cssNodes, null);
- List colorStrings = CssParser.getAttributeFromSections(sections, "color", true);
-
- String colorString = CssParser.selectMostRelevantAttribute(cssNodes, colorStrings);
-
- if (colorString != null) {
- if (colorString.startsWith("@")) {
- // it's a color definition
- String colorSubString = css.getColorDefinition(colorString.substring(1));
- return parseColor(colorSubString);
- }
- else {
- return parseColor(colorString);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- return null;
- }
-
- /**
- * @return the CSS for the current theme or null. It is important that this is called AFTER GTK has been initialized.
- */
- public static
- Css getCss() {
- String css;
- if (Gtk.isLoaded && Gtk.isGtk3) {
- final AtomicReference css_ = new AtomicReference(null);
-
- GtkEventDispatch.dispatchAndWait(new Runnable() {
- @Override
- public
- void run() {
- String themeName = getThemeName();
-
- if (themeName != null) {
- Pointer value = Gtk3.gtk_css_provider_get_named(themeName, null);
- if (value != null) {
- // we have the css provider!
-
- // NOTE: This can output warnings if the theme doesn't parse correctly by GTK, so we suppress them
- Glib.GLogFunc orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
-
- css_.set(Gtk3.gtk_css_provider_to_string(value));
-
- Glib.g_log_set_default_handler(orig, null);
- }
- }
- else {
- Pointer value = Gtk3.gtk_css_provider_get_default();
- if (value != null) {
- // we have the css provider!
-
- // NOTE: This can output warnings if the theme doesn't parse correctly by GTK, so we suppress them
- Glib.GLogFunc orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
-
- css_.set(Gtk3.gtk_css_provider_to_string(value));
-
- Glib.g_log_set_default_handler(orig, null);
- }
- }
- }
- });
-
- // will be either the string, or null.
- css = css_.get();
- }
- else {
- // GTK2 has to get the GTK3 theme text a different way (parsing it from disk). SOMETIMES, the app must be GTK2, even though
- // the system is GTK3. This works around the API restriction if we are an APP in GTK2 mode. This is not done ALL the time,
- // because this is not as accurate as using the GTK3 API.
- // This can also be a requirement if GTK is not loaded
- css = getGtk3ThemeCssViaFile();
- }
-
- return CssParser.parse(css);
- }
-
- /**
- * this works for GtkStatusIcon menus.
- *
- * @return the menu_fg/fg/text color from gtk-color-scheme or null
- */
- public static
- Color getFromColorScheme() {
- Pointer screen = Gtk.gdk_screen_get_default();
- Pointer settings = null;
-
- if (screen != null) {
- settings = Gtk.gtk_settings_get_for_screen(screen);
- }
-
- if (settings != null) {
- // see if we can get the info we want the EASY way (likely only when GTK+ 2 is used, but can be < GTK+ 3.2)...
-
- // been deprecated since version 3.8
- PointerByReference pointer = new PointerByReference();
- Gobject.g_object_get(settings, "gtk-color-scheme", pointer.getPointer(), null);
-
-
- // A palette of named colors for use in themes. The format of the string is
- // name1: color1
- // name2: color2
- //
- // Color names must be acceptable as identifiers in the gtkrc syntax, and color specifications must be in the format
- // accepted by gdk_color_parse().
- //
- // Note that due to the way the color tables from different sources are merged, color specifications will be converted
- // to hexadecimal form when getting this property.
- //
- // Starting with GTK+ 2.12, the entries can alternatively be separated by ';' instead of newlines:
- // name1: color1; name2: color2; ...
- //
- // GtkSettings:gtk-color-scheme has been deprecated since version 3.8 and should not be used in newly-written code.
- // Color scheme support was dropped and is no longer supported. You can still set this property, but it will be ignored.
-
-
- Pointer value = pointer.getValue();
- if (value != null) {
- String s = value.getString(0);
- if (!s.isEmpty()) {
- if (DEBUG) {
- System.out.println("\t string: " + s);
- }
-
- // Note: these are the values on my system when forcing GTK+ 2 (XUbuntu 16.04) with GtkStatusIcon and Aidwata theme
- // bg_color_dark: #686868686868
- // fg_color: #3c3c3c3c3c3c
- // fm_color: #f7f7f7f7f7f7
- // selected_fg_color: #ffffffffffff
- // panel_bg: #686868686868
- // text_color: #212121212121
- // text_color_dark: #ffffffffffff
- // tooltip_bg_color: #000000000000
- // link_color: #2d2d7171b8b8
- // tooltip_fg_color: #e1e1e1e1e1e1
- // base_color: #fcfcfcfcfcfc
- // bg_color: #cececececece
- // selected_bg_color: #39398e8ee7e7
-
- // list of colors, in order of importance, that we want to parse.
- String colors[] = new String[] {"menu_fg_color", "fg_color", "text_color"};
-
- for (String colorName : colors) {
- int i = 0;
- while (i != -1) {
- i = s.indexOf(colorName, i);
- if (i >= 0) {
- try {
- // the color will ALWAYS be in hex notation
-
- // it is also possible to be separated by ; instead of newline
- int endIndex = s.indexOf(';', i);
- if (endIndex == -1) {
- endIndex = s.indexOf('\n', i);
- }
-
- if (s.charAt(i - 1) == '_') {
- i = endIndex;
- continue;
- }
-
- int startIndex = s.indexOf('#', i);
- String colorString = s.substring(startIndex, endIndex)
- .trim();
-
- if (DEBUG_VERBOSE) {
- System.out.println("Color string: " + colorString);
- }
- return parseColor(colorString);
- } catch (Exception ignored) {
- }
- }
- }
- }
- }
- }
- }
-
- return null;
- }
-
- /**
- * Checks in the following locations for the current GTK3 theme.
- *
- * /usr/share/themes
- * /opt/gnome/share/themes
- */
- private static
- String getGtk3ThemeCssViaFile() {
- File themeDirectory = getThemeDirectory(true);
-
- if (themeDirectory == null) {
- return null;
- }
-
- File gtkFile = new File(themeDirectory, "gtk.css");
- try {
- StringBuilder stringBuilder = new StringBuilder((int) (gtkFile.length()));
- FileUtil.read(gtkFile, stringBuilder);
-
- removeComments(stringBuilder);
-
- // only comments in the file
- if (stringBuilder.length() < 2) {
- return null;
- }
-
- injectAdditionalCss(themeDirectory, stringBuilder);
-
- return stringBuilder.toString();
- } catch (IOException e) {
- // cant read the file or something else.
- if (SystemTray.DEBUG) {
- SystemTray.logger.error("Error getting RAW GTK3 theme file.", e);
- }
- }
-
- return null;
- }
-
- /**
- * @return the discovered fg[NORMAL] or text[NORMAL] color for this theme or null
- */
- public static
- Color getFromGtk2ThemeText() {
- String gtk2ThemeText = getGtk2ThemeText();
-
- if (gtk2ThemeText != null) {
- String[] colorText = new String[] {"fg[NORMAL]", "text[NORMAL]"};
- for (String text : colorText) {
- int i = 0;
- while (i != -1) {
- i = gtk2ThemeText.indexOf(text, i);
- if (i != -1) {
- if (i > 0 && gtk2ThemeText.charAt(i - 1) != '_') {
- i += text.length();
- continue;
- }
-
-
- int j = gtk2ThemeText.indexOf("=", i);
- if (j != -1) {
- int lineEnd = gtk2ThemeText.indexOf('\n', j);
-
- if (lineEnd != -1) {
- String colorName = gtk2ThemeText.substring(j + 1, lineEnd)
- .trim();
-
- colorName = colorName.replaceAll("\"", "");
- return parseColor(colorName);
- }
- }
- }
- }
- }
- }
-
- return null;
- }
-
-
- /**
- * Checks in the following locations for the current GTK2 theme.
- *
- * /usr/share/themes
- * /opt/gnome/share/themes
- */
- private static
- String getGtk2ThemeText() {
- File themeDirectory = getThemeDirectory(false);
-
- if (themeDirectory == null) {
- return null;
- }
-
-
- // ie: /usr/share/themes/Numix/gtk-2.0/gtkrc
- File gtkFile = new File(themeDirectory, "gtkrc");
-
- try {
- StringBuilder stringBuilder = new StringBuilder((int) (gtkFile.length()));
- FileUtil.read(gtkFile, stringBuilder);
-
- removeComments(stringBuilder);
-
- // only comments in the file
- if (stringBuilder.length() < 2) {
- return null;
- }
-
- return stringBuilder.toString();
- } catch (IOException ignored) {
- // cant read the file or something else.
- }
-
- return null;
- }
-
-
- /**
- * Figures out what the directory is for the specified type of GTK theme files (css/gtkrc/etc)
- *
- * @param gtk3 true if you want to look for the GTK3 theme dir, false if you want the GTK2 theme dir
- *
- * @return the directory or null if it cannot be found
- */
- public static
- File getThemeDirectory(boolean gtk3) {
- String themeName = getThemeName();
-
- if (themeName == null) {
- return null;
- }
-
- String gtkType;
- if (gtk3) {
- gtkType = "gtk-3.0";
- }
- else {
- gtkType = "gtk-2.0";
- }
-
-
- String[] dirs = new String[] {"/usr/share/themes", "/opt/gnome/share/themes"};
-
- // ie: /usr/share/themes
- for (String dirName : dirs) {
- File themesDir = new File(dirName);
-
- File[] themeDirs = themesDir.listFiles();
- if (themeDirs != null) {
- // ie: /usr/share/themes/Numix
- for (File themeDir : themeDirs) {
- File[] files1 = themeDir.listFiles();
- if (files1 != null) {
- boolean isCorrectTheme;
-
- File themeIndexFile = new File(themeDir, "index.theme");
- try {
- List read = FileUtil.read(themeIndexFile, false);
- for (String s : read) {
- if (s.startsWith("GtkTheme=")) {
- String calculatedThemeName = s.substring("GtkTheme=".length());
-
- isCorrectTheme = calculatedThemeName.equals(themeName);
-
- if (isCorrectTheme) {
- // ie: /usr/share/themes/Numix/gtk-3.0/gtk.css
- // the DARK variant is only used by some apps. The dark variant is NOT SYSTEM-WIDE!
- return new File(themeDir, gtkType);
- }
-
- break;
- }
- }
- } catch (IOException ignored) {
- }
- }
- }
- }
- }
-
- return null;
- }
-
- /**
- * Parses out the color from a color:
- *
- * - the word "transparent"
- * - hex 12 digit #ffffaaaaffff
- * - hex 6 digit #ffaaff
- * - hex 3 digit #faf
- * - rgb(r, g, b) rgb(33, 33, 33)
- * - rgb(r, g, b) rgb(.6, .3, .3)
- * - rgb(r%, g%, b%) rgb(10%, 20%, 30%)
- * - rgba(r, g, b, a) rgb(33, 33, 33, 0.53)
- * - rgba(r, g, b, a) rgb(.33, .33, .33, 0.53)
- * - rgba(r, g, b, a) rgb(10%, 20%, 30%, 0.53)
- *
- * Notes:
- * - rgb(), when an int, is between 0-255
- * - rgb(), when a float, is between 0.0-1.0
- * - rgb(), when a percent, is between 0-100
- * - alpha is always a float
- *
- * @return the parsed color
- */
- @SuppressWarnings("Duplicates")
- private static
- Color parseColor(String colorString) {
- if (colorString == null) {
- return null;
- }
-
- int red = 0;
- int green = 0;
- int blue = 0;
- int alpha = 255;
-
- if (colorString.startsWith("#")) {
- colorString = colorString.substring(1);
-
- if (colorString.length() > 11) {
- red = Integer.parseInt(colorString.substring(0, 4), 16);
- green = Integer.parseInt(colorString.substring(4, 8), 16);
- blue = Integer.parseInt(colorString.substring(8), 16);
-
- // Have to convert to positive int (value between 0 and 65535, these are 16 bits per pixel) that is from 0-255
- red = red & 0x0000FFFF;
- green = green & 0x0000FFFF;
- blue = blue & 0x0000FFFF;
-
- red = (red >> 8) & 0xFF;
- green = (green >> 8) & 0xFF;
- blue = (blue >> 8) & 0xFF;
- }
- else if (colorString.length() > 5) {
- red = Integer.parseInt(colorString.substring(0, 2), 16);
- green = Integer.parseInt(colorString.substring(2, 4), 16);
- blue = Integer.parseInt(colorString.substring(4), 16);
- }
- else {
- red = Integer.parseInt(colorString.substring(0, 1), 16);
- green = Integer.parseInt(colorString.substring(1, 2), 16);
- blue = Integer.parseInt(colorString.substring(2), 16);
- }
- }
- else if (colorString.startsWith("rgba")) {
- colorString = colorString.substring(colorString.indexOf('(') + 1, colorString.indexOf(')'));
- String[] split = colorString.split(",");
-
- String trim1 = split[0].trim();
- String trim2 = split[1].trim();
- String trim3 = split[2].trim();
- String trim4 = split[3].trim();
-
- if (colorString.contains("%")) {
- trim1 = trim1.replace("%", "");
- trim2 = trim2.replace("%", "");
- trim3 = trim3.replace("%", "");
-
- red = Integer.parseInt(trim1) * 255;
- green = Integer.parseInt(trim2) * 255;
- blue = Integer.parseInt(trim3) * 255;
- }
- else if (colorString.contains(".")) {
- red = (int) (Float.parseFloat(trim1) * 255);
- green = (int) (Float.parseFloat(trim2) * 255);
- blue = (int) (Float.parseFloat(trim3) * 255);
- }
- else {
- red = Integer.parseInt(trim1);
- green = Integer.parseInt(trim2);
- blue = Integer.parseInt(trim3);
- }
-
- float alphaF = Float.parseFloat(trim4);
- alpha = (int) (alphaF * 255);
- }
- else if (colorString.startsWith("rgb")) {
- colorString = colorString.substring(colorString.indexOf('(') + 1, colorString.indexOf(')'));
- String[] split = colorString.split(",");
-
- String trim1 = split[0].trim();
- String trim2 = split[1].trim();
- String trim3 = split[2].trim();
-
- if (colorString.contains("%")) {
- trim1 = trim1.replace("%", "");
- trim2 = trim2.replace("%", "");
- trim3 = trim3.replace("%", "");
-
- red = Integer.parseInt(trim1) * 255;
- green = Integer.parseInt(trim2) * 255;
- blue = Integer.parseInt(trim3) * 255;
- }
- else if (colorString.contains(".")) {
- red = (int) (Float.parseFloat(trim1) * 255);
- green = (int) (Float.parseFloat(trim2) * 255);
- blue = (int) (Float.parseFloat(trim3) * 255);
- }
- else {
- red = Integer.parseInt(trim1);
- green = Integer.parseInt(trim2);
- blue = Integer.parseInt(trim3);
- }
- }
- else if (colorString.contains("transparent")) {
- alpha = 0;
- }
- else {
- int index = colorString.indexOf(";");
- if (index > 0) {
- colorString = colorString.substring(0, index);
- }
- colorString = colorString.replaceAll("\"", "");
- colorString = colorString.replaceAll("'", "");
-
- // maybe it's just a "color" description, such as "red"?
- try {
- return Color.decode(colorString);
- } catch (Exception e) {
- return null;
- }
- }
-
- return new Color(red, green, blue, alpha);
- }
-
- /**
- * https://wiki.archlinux.org/index.php/GTK%2B
- *
- * gets the name of the currently loaded theme
- * GTK+ 2:
- * ~/.gtkrc-2.0
- * gtk-icon-theme-name = "Adwaita"
- * gtk-theme-name = "Adwaita"
- * gtk-font-name = "DejaVu Sans 11"
- *
- *
- * GTK+ 3:
- * $XDG_CONFIG_HOME/gtk-3.0/settings.ini
- * [Settings]
- * gtk-icon-theme-name = Adwaita
- * gtk-theme-name = Adwaita
- * gtk-font-name = DejaVu Sans 11
- *
- *
- * Note: The icon theme name is the name defined in the theme's index file, not the name of its directory.
- *
- * directories:
- * /usr/share/themes
- * /opt/gnome/share/themes
- *
- * GTK+ 2 user specific: ~/.gtkrc-2.0
- * GTK+ 2 system wide: /etc/gtk-2.0/gtkrc
- *
- * GTK+ 3 user specific: $XDG_CONFIG_HOME/gtk-3.0/settings.ini, or $HOME/.config/gtk-3.0/settings.ini if $XDG_CONFIG_HOME is not set
- * GTK+ 3 system wide: /etc/gtk-3.0/settings.ini
- *
- * @return the theme name, or null if it cannot find it.
- */
- public static
- String getThemeName() {
- final AtomicReference themeName = new AtomicReference(null);
-
- GtkEventDispatch.dispatchAndWait(new Runnable() {
- @Override
- public
- void run() {
- Pointer screen = Gtk.gdk_screen_get_default();
- Pointer settings = null;
-
- if (screen != null) {
- settings = Gtk.gtk_settings_get_for_screen(screen);
- }
-
- if (settings != null) {
- PointerByReference pointer = new PointerByReference();
- Gobject.g_object_get(settings, "gtk-theme-name", pointer.getPointer(), null);
-
- Pointer value = pointer.getValue();
- if (value != null) {
- themeName.set(value.getString(0));
- }
- }
-
- if (DEBUG) {
- System.err.println("Theme name: " + themeName);
- }
- }
- });
-
- // will be either the string, or null.
- return themeName.get();
-
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/AppIndicatorInstanceStruct.java b/src/dorkbox/systemTray/jna/linux/structs/AppIndicatorInstanceStruct.java
deleted file mode 100644
index 4d97eb1..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/AppIndicatorInstanceStruct.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-
-import dorkbox.util.Keep;
-
-@Keep
-public
-class AppIndicatorInstanceStruct extends Structure {
- public GObjectStruct parent;
- public Pointer priv;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("parent", "priv");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GObjectStruct.java b/src/dorkbox/systemTray/jna/linux/structs/GObjectStruct.java
deleted file mode 100644
index 14d74d5..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GObjectStruct.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-
-import dorkbox.util.Keep;
-
-@Keep
-public
-class GObjectStruct extends Structure {
- public
- class ByValue extends GObjectStruct implements Structure.ByValue {}
-
-
- public
- class ByReference extends GObjectStruct implements Structure.ByReference {}
-
-
- public GTypeInstanceStruct g_type_instance;
- public int ref_count;
- public Pointer qdata;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_type_instance", "ref_count", "qdata");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GParamSpecStruct.java b/src/dorkbox/systemTray/jna/linux/structs/GParamSpecStruct.java
deleted file mode 100644
index 62daede..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GParamSpecStruct.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Structure;
-
-import dorkbox.util.Keep;
-
-@Keep
-public
-class GParamSpecStruct extends Structure {
- public
- class ByValue extends GParamSpecStruct implements Structure.ByValue {}
-
-
- public
- class ByReference extends GParamSpecStruct implements Structure.ByReference {}
-
-
- public GTypeInstanceStruct g_type_instance;
-
- public String name; /* interned string */
-// Pointer flags;
-// double value_type;
-// double owner_type; /* class or interface using this property */
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_type_instance", "name");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GTypeInstanceStruct.java b/src/dorkbox/systemTray/jna/linux/structs/GTypeInstanceStruct.java
deleted file mode 100644
index 6f27fbb..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GTypeInstanceStruct.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-
-import dorkbox.util.Keep;
-
-@Keep
-public
-class GTypeInstanceStruct extends Structure {
- public
- class ByValue extends GTypeInstanceStruct implements Structure.ByValue {}
-
-
- public
- class ByReference extends GTypeInstanceStruct implements Structure.ByReference {}
-
-
- public Pointer g_class;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("g_class");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GdkColor.java b/src/dorkbox/systemTray/jna/linux/structs/GdkColor.java
deleted file mode 100644
index c22a18a..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GdkColor.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux.structs;
-
-import java.awt.Color;
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Structure;
-
-/**
- * https://developer.gnome.org/gdk3/stable/gdk3-Colors.html
- *
- * GdkColor has been deprecated since version 3.14 and should not be used in newly-written code.
- */
-public
-class GdkColor extends Structure {
-
- /* The color type.
- * A color consists of red, green and blue values in the
- * range 0-65535 and a pixel value. The pixel value is highly
- * dependent on the depth and colormap which this color will
- * be used to draw into. Therefore, sharing colors between
- * colormaps is a bad idea.
- */
- public int pixel;
- public short red;
- public short green;
- public short blue;
-
- /**
- * Convert to positive int (value between 0 and 65535, these are 16 bits per pixel) that is from 0-255
- */
- private static int convert(int inputColor) {
- return (inputColor & 0x0000FFFF >> 8) & 0xFF;
- }
-
- public int red() {
- return convert(red);
- }
-
- public int green() {
- return convert(green);
- }
-
- public int blue() {
- return convert(blue);
- }
-
- public
- Color getColor() {
- read(); // have to read the struct members first!
- return new Color(red(), green(), blue());
- }
-
- @Override
- public
- String toString() {
- return "[r=" + red() + ",g=" + green() + ",b=" + blue() + "]";
- }
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("pixel", "red", "green", "blue");
- }
-
-
- public
- class ByValue extends GdkColor implements Structure.ByValue {}
-
-
- public static
- class ByReference extends GdkColor implements Structure.ByReference {}
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GdkEventButton.java b/src/dorkbox/systemTray/jna/linux/structs/GdkEventButton.java
deleted file mode 100644
index e74510d..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GdkEventButton.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-
-import dorkbox.util.Keep;
-
-@Keep
-public
-class GdkEventButton extends Structure {
- public int type;
- public Pointer window;
- public int send_event;
- public int time;
- public double x;
- public double y;
- public Pointer axes;
- public int state;
- public int button;
- public Pointer device;
- public double x_root;
- public double y_root;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("type", "window", "send_event", "time", "x", "y", "axes", "state", "button", "device", "x_root", "y_root");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GdkRGBAColor.java b/src/dorkbox/systemTray/jna/linux/structs/GdkRGBAColor.java
deleted file mode 100644
index 9729496..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GdkRGBAColor.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux.structs;
-
-import java.awt.Color;
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Structure;
-
-/**
- * https://developer.gnome.org/gdk3/stable/gdk3-RGBA-Colors.html#GdkRGBA
- */
-public
-class GdkRGBAColor extends Structure {
-
- // these are from 0.0 to 1.0 inclusive
- public double red;
- public double green;
- public double blue;
- public double alpha;
-
- public float red() {
- return (float) red;
- }
-
- public float green() {
- return (float) green;
- }
-
- public float blue() {
- return (float) blue;
- }
-
- public
- Color getColor() {
- read(); // have to read the struct members first!
- return new Color(red(), green(), blue());
- }
-
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("red", "green", "blue", "alpha");
- }
-
-
- public
- class ByValue extends GdkRGBAColor implements Structure.ByValue {}
-
-
- public
- class ByReference extends GdkRGBAColor implements Structure.ByReference {}
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GtkRequisition.java b/src/dorkbox/systemTray/jna/linux/structs/GtkRequisition.java
deleted file mode 100644
index faac052..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GtkRequisition.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Structure;
-
-/**
- * https://developer.gimp.org/api/2.0/gtk/GtkWidget.html#GtkRequisition
- */
-public
-class GtkRequisition extends Structure {
-
- public int width;
- public int height;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("width", "height");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java b/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java
deleted file mode 100644
index b0b99c6..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Pointer;
-import com.sun.jna.Structure;
-
-import dorkbox.util.Keep;
-
-@Keep
-public
-class GtkStyle extends Structure {
- /*
- * There are several 'directives' to change the attributes of a widget.
- * fg - Sets the foreground color of a widget.
- * bg - Sets the background color of a widget.
- * text - Sets the foreground color for widgets that have editable text.
- * base - Sets the background color for widgets that have editable text.
- * bg_pixmap - Sets the background of a widget to a tiled pixmap.
- * font_name - Sets the font to be used with the given widget.
- * xthickness - Sets the left and right border width. This is not what you might think; it sets the borders of children(?)
- * ythickness - similar to above but for the top and bottom.
- *
- * There are several states a widget can be in, and you can set different colors, pixmaps and fonts for each state. These states are:
- * NORMAL - The normal state of a widget. Ie the mouse is not over it, and it is not being pressed, etc.
- * PRELIGHT - When the mouse is over top of the widget, colors defined using this state will be in effect.
- * ACTIVE - When the widget is pressed or clicked it will be active, and the attributes assigned by this tag will be in effect.
- * INSENSITIVE - This is the state when a widget is 'greyed out'. It is not active, and cannot be clicked on.
- * SELECTED - When an object is selected, it takes these attributes.
- */
-
- public static
- class ByReference extends GtkStyle implements Structure.ByReference {}
-
- public
- class ByValue extends GtkStyle implements Structure.ByValue {}
-
- // only list public fields
-
- /** fg: foreground for drawing GtkLabel */
- public GdkColor fg[] = new GdkColor[5];
-
- /** bg: the usual background color, gray by default */
- public GdkColor bg[] = new GdkColor[5];
- public GdkColor light[] = new GdkColor[5];
- public GdkColor dark[] = new GdkColor[5];
- public GdkColor mid[] = new GdkColor[5];
-
- /**
- * text: text for entries and text widgets (although in GTK 1.2 sometimes fg gets used, this is more or less a bug and fixed in GTK 2.0).
- */
- public GdkColor text[] = new GdkColor[5];
-
- /** base: background when using text, colored white in the default theme. */
- public GdkColor base[] = new GdkColor[5];
- public GdkColor text_aa[] = new GdkColor[5]; /* Halfway between text/base */
- public GdkColor black;
- public GdkColor white;
- public Pointer /*PangoFontDescription*/ font_desc;
- public int xthickness;
- public int ythickness;
- public Pointer /*cairo_pattern_t*/ background[] = new Pointer[5];
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("fg",
- "bg",
- "light",
- "dark",
- "mid",
- "text",
- "base",
- "text_aa",
- "black",
- "white",
- "font_desc",
- "xthickness",
- "ythickness",
- "background");
- }
-}
diff --git a/src/dorkbox/systemTray/jna/linux/structs/PangoRectangle.java b/src/dorkbox/systemTray/jna/linux/structs/PangoRectangle.java
deleted file mode 100644
index 1f58343..0000000
--- a/src/dorkbox/systemTray/jna/linux/structs/PangoRectangle.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2017 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.jna.linux.structs;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.sun.jna.Structure;
-
-/**
- * https://developer.gnome.org/pango/stable/pango-Glyph-Storage.html#PangoRectangle
- */
-public
-class PangoRectangle extends Structure {
-
- public int x;
- public int y;
- public int width;
- public int height;
-
- @Override
- protected
- List getFieldOrder() {
- return Arrays.asList("x", "y", "width", "height");
- }
-}
diff --git a/src/dorkbox/systemTray/nativeUI/NativeUI.java b/src/dorkbox/systemTray/nativeUI/NativeUI.java
deleted file mode 100644
index 2c300af..0000000
--- a/src/dorkbox/systemTray/nativeUI/NativeUI.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2016 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.nativeUI;
-
-
-/**
- * Represents a System Tray or menu, that will have it's menu rendered via the native subsystem.
- *
- * This is does not have as many features as the swing-based UI, however the trade off is that this will always have the native L&F of
- * the system (with the exception of Windows, whose native menu looks absolutely terrible).
- *
- * Noticeable differences that are limitations for the NativeUI only:
- * - AppIndicator Status entries must be plain text (they are not bold as they are everywhere else).
- * - MacOS cannot have images in their menu or sub-menu's -- only plain text is possible
- */
-public
-interface NativeUI {}
diff --git a/src/dorkbox/systemTray/peer/MenuItemPeer.java b/src/dorkbox/systemTray/peer/MenuItemPeer.java
index aa45c0d..db57442 100644
--- a/src/dorkbox/systemTray/peer/MenuItemPeer.java
+++ b/src/dorkbox/systemTray/peer/MenuItemPeer.java
@@ -31,4 +31,6 @@ interface MenuItemPeer extends EntryPeer {
void setCallback(MenuItem menuItem);
void setShortcut(MenuItem menuItem);
+
+ void setTooltip(MenuItem menuItem);
}
diff --git a/src/dorkbox/systemTray/jna/linux/GCallback.java b/src/dorkbox/systemTray/peer/SeparatorPeer.java
similarity index 64%
rename from src/dorkbox/systemTray/jna/linux/GCallback.java
rename to src/dorkbox/systemTray/peer/SeparatorPeer.java
index 53344e0..6898b6d 100644
--- a/src/dorkbox/systemTray/jna/linux/GCallback.java
+++ b/src/dorkbox/systemTray/peer/SeparatorPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 dorkbox, llc
+ * Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,18 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.jna.linux;
+package dorkbox.systemTray.peer;
-import com.sun.jna.Callback;
-import com.sun.jna.Pointer;
-
-import dorkbox.util.Keep;
-
-@Keep
+/**
+ * Internal component used to bind the API to the implementation
+ */
public
-interface GCallback extends Callback {
- /**
- * @return Gtk.TRUE if we handled this event
- */
- int callback(Pointer instance, Pointer data);
+interface SeparatorPeer extends EntryPeer {
}
diff --git a/src/dorkbox/systemTray/swingUI/SwingUI.java b/src/dorkbox/systemTray/swingUI/SwingUI.java
deleted file mode 100644
index 9eead8d..0000000
--- a/src/dorkbox/systemTray/swingUI/SwingUI.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2016 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.swingUI;
-
-/**
- * Represents a System Tray or menu, that will have it's menu rendered via Swing.
- *
- * This has the most standard L&F across all systems (as all systems will render this menu the exact same way), however the tradeoff is that
- * one loses the native L&F of the system (with the exception of Windows, whose native menu looks absolutely terrible).
- *
- * Noticeable differences that are limitations for the NativeUI only:
- * - AppIndicator Status entries must be plain text (they are not bold as they are everywhere else).
- * - MacOS cannot have images in their menu or sub-menu's -- only plain text is possible
- */
-public
-interface SwingUI {}
diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenu.java b/src/dorkbox/systemTray/ui/awt/AwtMenu.java
similarity index 96%
rename from src/dorkbox/systemTray/nativeUI/AwtMenu.java
rename to src/dorkbox/systemTray/ui/awt/AwtMenu.java
index 55e5e22..98f07e4 100644
--- a/src/dorkbox/systemTray/nativeUI/AwtMenu.java
+++ b/src/dorkbox/systemTray/ui/awt/AwtMenu.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.awt;
import java.awt.MenuShortcut;
@@ -136,6 +136,12 @@ class AwtMenu implements MenuPeer {
});
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ // no op. (awt menus cannot show tooltips)
+ }
+
@Override
public
void remove() {
diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenuItem.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java
similarity index 74%
rename from src/dorkbox/systemTray/nativeUI/AwtMenuItem.java
rename to src/dorkbox/systemTray/ui/awt/AwtMenuItem.java
index b4385fc..5ca7523 100644
--- a/src/dorkbox/systemTray/nativeUI/AwtMenuItem.java
+++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.awt;
import java.awt.MenuShortcut;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.MenuItemPeer;
+import dorkbox.systemTray.util.EventDispatch;
import dorkbox.util.SwingUtil;
class AwtMenuItem implements MenuItemPeer {
@@ -74,28 +76,32 @@ class AwtMenuItem implements MenuItemPeer {
_native.removeActionListener(callback);
}
- if (menuItem.getCallback() != null) {
+ callback = menuItem.getCallback(); // can be set to null
+
+ if (callback != null) {
callback = new ActionListener() {
+ final ActionListener cb = menuItem.getCallback();
+
@Override
public
void actionPerformed(ActionEvent e) {
- // we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
- ActionListener cb = menuItem.getCallback();
- if (cb != null) {
- try {
- cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
- } catch (Throwable throwable) {
- SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
- }
- }
+ // we want it to run on our own with our own action event info (so it is consistent across all platforms)
+ EventDispatch.runLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ try {
+ cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
+ } catch (Throwable throwable) {
+ SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ }
+ }
+ });
}
};
_native.addActionListener(callback);
}
- else {
- callback = null;
- }
}
@Override
@@ -114,6 +120,12 @@ class AwtMenuItem implements MenuItemPeer {
});
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ // no op. (awt menus cannot show tooltips)
+ }
+
@SuppressWarnings("Duplicates")
@Override
public
diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java
similarity index 71%
rename from src/dorkbox/systemTray/nativeUI/AwtMenuItemCheckbox.java
rename to src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java
index 1d14512..50e3182 100644
--- a/src/dorkbox/systemTray/nativeUI/AwtMenuItemCheckbox.java
+++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java
@@ -13,15 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.awt;
import java.awt.MenuShortcut;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.CheckboxPeer;
+import dorkbox.systemTray.util.EventDispatch;
import dorkbox.util.SwingUtil;
class AwtMenuItemCheckbox implements CheckboxPeer {
@@ -30,7 +33,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
private final java.awt.CheckboxMenuItem _native = new java.awt.CheckboxMenuItem();
// these have to be volatile, because they can be changed from any thread
- private volatile ActionListener callback;
+ private volatile ItemListener callback;
private volatile boolean isChecked = false;
// this is ALWAYS called on the EDT.
@@ -67,33 +70,39 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
@Override
public
void setCallback(final Checkbox menuItem) {
+ // of critical note: AWT only works with ItemListener -- but we use ActionListener for everything, so here we make things compatible
if (callback != null) {
- _native.removeActionListener(callback);
+ _native.removeItemListener(callback);
}
- callback = menuItem.getCallback(); // can be set to null
+ ActionListener callback = menuItem.getCallback(); // can be set to null
if (callback != null) {
- callback = new ActionListener() {
+ this.callback = new ItemListener() {
+ final ActionListener cb = menuItem.getCallback();
+
@Override
public
- void actionPerformed(ActionEvent e) {
+ void itemStateChanged(final ItemEvent e) {
// this will run on the EDT, since we are calling it from the EDT
menuItem.setChecked(!isChecked);
- // we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
- ActionListener cb = menuItem.getCallback();
- if (cb != null) {
- try {
- cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
- } catch (Throwable throwable) {
- SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ // we want it to run on our own with our own action event info (so it is consistent across all platforms)
+ EventDispatch.runLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ try {
+ cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
+ } catch (Throwable throwable) {
+ SystemTray.logger.error("Error calling menu checkbox entry {} click event.", menuItem.getText(), throwable);
+ }
}
- }
+ });
}
};
- _native.addActionListener(callback);
+ _native.addItemListener(this.callback);
}
}
@@ -144,7 +153,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
_native.setEnabled(false);
if (callback != null) {
- _native.removeActionListener(callback);
+ _native.removeItemListener(callback);
callback = null;
}
parent._native.remove(_native);
diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenuItemSeparator.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java
similarity index 97%
rename from src/dorkbox/systemTray/nativeUI/AwtMenuItemSeparator.java
rename to src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java
index 76b6d68..459ccf0 100644
--- a/src/dorkbox/systemTray/nativeUI/AwtMenuItemSeparator.java
+++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.awt;
import dorkbox.systemTray.peer.EntryPeer;
diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenuItemStatus.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java
similarity index 98%
rename from src/dorkbox/systemTray/nativeUI/AwtMenuItemStatus.java
rename to src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java
index 870f32f..e3ee146 100644
--- a/src/dorkbox/systemTray/nativeUI/AwtMenuItemStatus.java
+++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.awt;
import static java.awt.Font.DIALOG;
diff --git a/src/dorkbox/systemTray/nativeUI/_AwtTray.java b/src/dorkbox/systemTray/ui/awt/_AwtTray.java
similarity index 90%
rename from src/dorkbox/systemTray/nativeUI/_AwtTray.java
rename to src/dorkbox/systemTray/ui/awt/_AwtTray.java
index ff7dabb..883233c 100644
--- a/src/dorkbox/systemTray/nativeUI/_AwtTray.java
+++ b/src/dorkbox/systemTray/ui/awt/_AwtTray.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.awt;
import java.awt.AWTException;
import java.awt.Image;
@@ -42,7 +42,7 @@ import dorkbox.util.SwingUtil;
*/
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
public final
-class _AwtTray extends Tray implements NativeUI {
+class _AwtTray extends Tray {
private volatile SystemTray tray;
private volatile TrayIcon trayIcon;
@@ -182,6 +182,31 @@ class _AwtTray extends Tray implements NativeUI {
// no op
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ final String text = menuItem.getTooltip();
+
+ if (tooltipText != null && tooltipText.equals(text) ||
+ tooltipText == null && text != null) {
+ return;
+ }
+
+ tooltipText = text;
+
+ SwingUtil.invokeLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ // don't want to matter which (setImage/setTooltip/setEnabled) is done first, and if the image/enabled is changed, we
+ // want to make sure keep the tooltip text the same as before.
+ if (trayIcon != null) {
+ trayIcon.setToolTip(text);
+ }
+ }
+ });
+ }
+
@Override
public
void remove() {
@@ -209,31 +234,6 @@ class _AwtTray extends Tray implements NativeUI {
bind(awtMenu, null, systemTray);
}
- @Override
- protected
- void setTooltip_(final String tooltipText) {
- if (this.tooltipText.equals(tooltipText)){
- return;
- }
- this.tooltipText = tooltipText;
-
- SwingUtil.invokeLater(new Runnable() {
- @Override
- public
- void run() {
- if (tray == null) {
- tray = SystemTray.getSystemTray();
- }
-
- // don't want to matter which (setImage/setTooltip/setEnabled) is done first, and if the image/enabled is changed, we
- // want to make sure keep the tooltip text the same as before.
- if (trayIcon != null) {
- trayIcon.setToolTip(tooltipText);
- }
- }
- });
- }
-
@Override
public
boolean hasImage() {
diff --git a/src/dorkbox/systemTray/nativeUI/GtkBaseMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java
similarity index 69%
rename from src/dorkbox/systemTray/nativeUI/GtkBaseMenuItem.java
rename to src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java
index 7403c0d..72b69c9 100644
--- a/src/dorkbox/systemTray/nativeUI/GtkBaseMenuItem.java
+++ b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java
@@ -13,23 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
+
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
import java.io.File;
import com.sun.jna.Pointer;
-import dorkbox.systemTray.jna.linux.Gobject;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.EntryPeer;
import dorkbox.systemTray.util.ImageResizeUtil;
+import dorkbox.util.jna.linux.Gobject;
+import dorkbox.util.jna.linux.GtkEventDispatch;
abstract
class GtkBaseMenuItem implements EntryPeer {
// these are necessary BECAUSE GTK menus look funky as hell when there are some menu entries WITH icons and some WITHOUT
private static final File transparentIcon = ImageResizeUtil.getTransparentImage();
- private volatile boolean hasLegitImage = true;
+ private volatile boolean hasLegitImage = false; // default is to not have an image assigned
// these have to be volatile, because they can be changed from any thread
private volatile Pointer spacerImage;
@@ -50,6 +51,39 @@ class GtkBaseMenuItem implements EntryPeer {
hasLegitImage = isLegit;
}
+ /**
+ * always remove a spacer image.
+ *
+ * called on the DISPATCH thread
+ */
+ protected
+ void removeSpacerImage() {
+ if (spacerImage != null) {
+ Gtk2.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
+ spacerImage = null;
+ Gtk2.gtk_widget_show_all(_native);
+ }
+ }
+
+
+ /**
+ * always add a spacer image.
+ *
+ * called on the DISPATCH thread
+ */
+ protected
+ void addSpacerImage() {
+ if (spacerImage == null) {
+ spacerImage = Gtk2.gtk_image_new_from_file(transparentIcon.getAbsolutePath());
+ Gtk2.gtk_image_menu_item_set_image(_native, spacerImage);
+
+ // must always re-set always-show after setting the image
+ Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
+ }
+ }
+
+
+
/**
* the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images.
* This is primarily only with AppIndicators, although not always.
@@ -63,21 +97,13 @@ class GtkBaseMenuItem implements EntryPeer {
return;
}
- if (spacerImage != null) {
- Gtk.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
- spacerImage = null;
- Gtk.gtk_widget_show_all(_native);
- }
+ removeSpacerImage();
if (everyoneElseHasImages) {
- spacerImage = Gtk.gtk_image_new_from_file(transparentIcon.getAbsolutePath());
- Gtk.gtk_image_menu_item_set_image(_native, spacerImage);
-
- // must always re-set always-show after setting the image
- Gtk.gtk_image_menu_item_set_always_show_image(_native, true);
+ addSpacerImage();
}
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
@@ -85,7 +111,7 @@ class GtkBaseMenuItem implements EntryPeer {
// always on EDT
void onDeleteMenu(final Pointer parentNative) {
Gobject.g_object_force_floating(_native); // makes it a floating reference
- Gtk.gtk_container_remove(parentNative, _native);
+ Gtk2.gtk_container_remove(parentNative, _native);
}
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
@@ -95,9 +121,9 @@ class GtkBaseMenuItem implements EntryPeer {
setSpacerImage(hasImagesInMenu);
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
- Gtk.gtk_menu_shell_append(parentNative, _native);
+ Gtk2.gtk_menu_shell_append(parentNative, _native);
Gobject.g_object_ref_sink(_native); // undoes "floating"
- Gtk.gtk_widget_show_all(_native); // necessary to guarantee widget is visible
+ Gtk2.gtk_widget_show_all(_native); // necessary to guarantee widget is visible
}
@Override
@@ -108,7 +134,7 @@ class GtkBaseMenuItem implements EntryPeer {
public
void run() {
if (spacerImage != null) {
- Gtk.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
spacerImage = null;
}
}
diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java
similarity index 77%
rename from src/dorkbox/systemTray/nativeUI/GtkMenu.java
rename to src/dorkbox/systemTray/ui/gtk/GtkMenu.java
index f911061..2de5462 100644
--- a/src/dorkbox/systemTray/nativeUI/GtkMenu.java
+++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
+
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
import java.util.ArrayList;
import java.util.List;
@@ -27,9 +29,8 @@ import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.Status;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.MenuPeer;
+import dorkbox.util.jna.linux.GtkEventDispatch;
@SuppressWarnings("deprecation")
class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
@@ -61,7 +62,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
@SuppressWarnings("IncompleteCopyConstructor")
private
GtkMenu(final GtkMenu parent) {
- super(Gtk.gtk_image_menu_item_new_with_mnemonic("")); // is what is added to the parent menu (so images work)
+ super(Gtk2.gtk_image_menu_item_new_with_mnemonic("")); // is what is added to the parent menu (so images work)
this.parent = parent;
}
@@ -102,7 +103,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
menuEntry__.onDeleteMenu(_nativeMenu);
}
- Gtk.gtk_widget_destroy(_nativeMenu);
+ Gtk2.gtk_widget_destroy(_nativeMenu);
}
if (parent != null) {
@@ -110,11 +111,11 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
}
// makes a new one
- _nativeMenu = Gtk.gtk_menu_new();
+ _nativeMenu = Gtk2.gtk_menu_new();
// binds sub-menu to entry (if it exists! it does not for the root menu)
if (parent != null) {
- Gtk.gtk_menu_item_set_submenu(_native, _nativeMenu);
+ Gtk2.gtk_menu_item_set_submenu(_native, _nativeMenu);
}
}
@@ -158,8 +159,8 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
}
}
- Gtk.gtk_widget_show_all(_nativeMenu); // necessary to guarantee widget is visible (doesn't always show_all for all children)
onMenuAdded(_nativeMenu);
+ Gtk2.gtk_widget_show_all(_nativeMenu); // necessary to guarantee widget is visible (doesn't always show_all for all children)
}
/**
@@ -186,7 +187,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
}
menuEntriesCopy.clear();
- Gtk.gtk_widget_destroy(_nativeMenu);
+ Gtk2.gtk_widget_destroy(_nativeMenu);
_nativeMenu = null;
obliterateInProgress.set(false);
@@ -196,7 +197,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
@Override
public
void add(final Menu parentMenu, final Entry entry, final int index) {
- // must always be called on the GTK dispatch. This must be dispatchAndWait
+ // must always be called on the GTK dispatch. This must be dispatchAndWait() so it will properly executed immediately
GtkEventDispatch.dispatchAndWait(new Runnable() {
@Override
public
@@ -205,35 +206,50 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
// To work around this issue, we destroy then recreate the menu every time something is changed.
deleteMenu();
+ GtkBaseMenuItem item = null;
+
if (entry instanceof Menu) {
// some implementations of appindicator, do NOT like having a menu added, which has no menu items yet.
// see: https://bugs.launchpad.net/glipper/+bug/1203888
- GtkMenu item = new GtkMenu(GtkMenu.this);
+ item = new GtkMenu(GtkMenu.this);
menuEntries.add(index, item);
- ((Menu) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof Separator) {
- GtkMenuItemSeparator item = new GtkMenuItemSeparator(GtkMenu.this);
+ item = new GtkMenuItemSeparator(GtkMenu.this);
menuEntries.add(index, item);
- entry.bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof Checkbox) {
- GtkMenuItemCheckbox item = new GtkMenuItemCheckbox(GtkMenu.this);
+ item = new GtkMenuItemCheckbox(GtkMenu.this);
menuEntries.add(index, item);
- ((Checkbox) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof Status) {
- GtkMenuItemStatus item = new GtkMenuItemStatus(GtkMenu.this);
+ item = new GtkMenuItemStatus(GtkMenu.this);
menuEntries.add(index, item);
- ((Status) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof MenuItem) {
- GtkMenuItem item = new GtkMenuItem(GtkMenu.this);
+ item = new GtkMenuItem(GtkMenu.this);
menuEntries.add(index, item);
- ((MenuItem) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
createMenu();
+
+ // we must create the menu BEFORE binding the menu, otherwise the menus' children's GTK element can be added before
+ // their parent GTK elements are added (and the menu won't show up)
+ if (entry instanceof Menu) {
+ ((Menu) entry).bind((GtkMenu) item, parentMenu, parentMenu.getSystemTray());
+ }
+ else if (entry instanceof Separator) {
+ ((Separator)entry).bind((GtkMenuItemSeparator) item, parentMenu, parentMenu.getSystemTray());
+ }
+ else if (entry instanceof Checkbox) {
+ ((Checkbox) entry).bind((GtkMenuItemCheckbox) item, parentMenu, parentMenu.getSystemTray());
+ }
+ else if (entry instanceof Status) {
+ ((Status) entry).bind((GtkMenuItemStatus) item, parentMenu, parentMenu.getSystemTray());
+ }
+ else if (entry instanceof MenuItem) {
+ ((MenuItem) entry).bind((GtkMenuItem) item, parentMenu, parentMenu.getSystemTray());
+ }
}
});
}
@@ -256,21 +272,20 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
public
void run() {
if (image != null) {
- Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
image = null;
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
if (menuItem.getImage() != null) {
- image = Gtk.gtk_image_new_from_file(menuItem.getImage()
- .getAbsolutePath());
- Gtk.gtk_image_menu_item_set_image(_native, image);
+ image = Gtk2.gtk_image_new_from_file(menuItem.getImage().getAbsolutePath());
+ Gtk2.gtk_image_menu_item_set_image(_native, image);
// must always re-set always-show after setting the image
- Gtk.gtk_image_menu_item_set_always_show_image(_native, true);
+ Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
}
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
});
}
@@ -284,7 +299,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
@Override
public
void run() {
- Gtk.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
+ Gtk2.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
}
});
}
@@ -323,8 +338,8 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
@Override
public
void run() {
- Gtk.gtk_menu_item_set_label(_native, textWithMnemonic);
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_menu_item_set_label(_native, textWithMnemonic);
+ Gtk2.gtk_widget_show_all(_native);
}
});
}
@@ -345,6 +360,20 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
setText(menuItem);
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ GtkEventDispatch.dispatch(new Runnable() {
+ @Override
+ public
+ void run() {
+ // NOTE: this will not work for AppIndicator tray types!
+ // null will remove the tooltip
+ Gtk2.gtk_widget_set_tooltip_text(_native, menuItem.getTooltip());
+ }
+ });
+ }
+
/**
* called when a child removes itself from the parent menu. Does not work for sub-menus
*
@@ -379,7 +408,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
if (parent != null) {
// remove the gtk entry item from our menu NATIVE components
- Gtk.gtk_menu_item_set_submenu(_native, null);
+ Gtk2.gtk_menu_item_set_submenu(_native, null);
// have to rebuild the menu now...
parent.deleteMenu(); // must be on EDT
diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java
similarity index 57%
rename from src/dorkbox/systemTray/nativeUI/GtkMenuItem.java
rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java
index 70110a1..f68277d 100644
--- a/src/dorkbox/systemTray/nativeUI/GtkMenuItem.java
+++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java
@@ -13,25 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
+
+import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import com.sun.jna.Pointer;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.jna.linux.GCallback;
-import dorkbox.systemTray.jna.linux.Gobject;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.MenuItemPeer;
+import dorkbox.systemTray.util.EventDispatch;
+import dorkbox.util.jna.linux.GCallback;
+import dorkbox.util.jna.linux.Gobject;
+import dorkbox.util.jna.linux.GtkEventDispatch;
class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
private final GtkMenu parent;
// these have to be volatile, because they can be changed from any thread
- private volatile MenuItem menuItemForActionCallback;
+ private volatile ActionListener callback;
private volatile Pointer image;
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
@@ -44,7 +47,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
*/
GtkMenuItem(final GtkMenu parent) {
- super(Gtk.gtk_image_menu_item_new_with_mnemonic(""));
+ super(Gtk2.gtk_image_menu_item_new_with_mnemonic(""));
this.parent = parent;
Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
@@ -55,18 +58,12 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
int callback(final Pointer instance, final Pointer data) {
- if (menuItemForActionCallback != null) {
- final ActionListener cb = menuItemForActionCallback.getCallback();
- if (cb != null) {
- try {
- GtkEventDispatch.proxyClick(menuItemForActionCallback, cb);
- } catch (Exception e) {
- SystemTray.logger.error("Error calling menu entry {} click event.", menuItemForActionCallback.getText(), e);
- }
- }
+ ActionListener callback = this.callback;
+ if (callback != null) {
+ GtkEventDispatch.proxyClick(callback);
}
- return Gtk.TRUE;
+ return Gtk2.TRUE;
}
// NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted.
@@ -76,6 +73,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
void setImage(final MenuItem menuItem) {
+ final boolean hadImage = hasImage();
setLegitImage(menuItem.getImage() != null);
GtkEventDispatch.dispatch(new Runnable() {
@@ -83,21 +81,29 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
public
void run() {
if (image != null) {
- Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
image = null;
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
if (menuItem.getImage() != null) {
- image = Gtk.gtk_image_new_from_file(menuItem.getImage()
- .getAbsolutePath());
- Gtk.gtk_image_menu_item_set_image(_native, image);
+ // always remove the spacer image in case it's there. The spacer image will correctly added when the menu is created.
+ removeSpacerImage();
+
+ image = Gtk2.gtk_image_new_from_file(menuItem.getImage()
+ .getAbsolutePath());
+ Gtk2.gtk_image_menu_item_set_image(_native, image);
// must always re-set always-show after setting the image
- Gtk.gtk_image_menu_item_set_always_show_image(_native, true);
+ Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
+ }
+ else if (hadImage) {
+ // if at one point, we had an image, we should set the spacer image back, so that menu spacing looks correct.
+ // since we USED to have an image, it is safe to assume that we should have a spacer image.
+ addSpacerImage();
}
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
});
}
@@ -109,7 +115,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
void run() {
- Gtk.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
+ Gtk2.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
}
});
}
@@ -146,16 +152,40 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
void run() {
- Gtk.gtk_menu_item_set_label(_native, textWithMnemonic);
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_menu_item_set_label(_native, textWithMnemonic);
+ Gtk2.gtk_widget_show_all(_native);
}
});
}
+ @SuppressWarnings("Duplicates")
@Override
public
void setCallback(final MenuItem menuItem) {
- this.menuItemForActionCallback = menuItem;
+ callback = menuItem.getCallback(); // can be set to null
+
+ if (callback != null) {
+ callback = new ActionListener() {
+ final ActionListener cb = menuItem.getCallback();
+
+ @Override
+ public
+ void actionPerformed(ActionEvent e) {
+ // we want it to run on our own with our own action event info (so it is consistent across all platforms)
+ EventDispatch.runLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ try {
+ cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
+ } catch (Throwable throwable) {
+ SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ }
+ }
+ });
+ }
+ };
+ }
}
@Override
@@ -166,6 +196,20 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
setText(menuItem);
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ GtkEventDispatch.dispatch(new Runnable() {
+ @Override
+ public
+ void run() {
+ // NOTE: this will not work for AppIndicator tray types!
+ // null will remove the tooltip
+ Gtk2.gtk_widget_set_tooltip_text(_native, menuItem.getTooltip());
+ }
+ });
+ }
+
@SuppressWarnings("Duplicates")
@Override
public
@@ -174,13 +218,14 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@Override
public
void run() {
- Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
-
GtkMenuItem.super.remove();
- menuItemForActionCallback = null;
+ callback = null;
+
+ Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
+
if (image != null) {
- Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
image = null;
}
diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java
similarity index 75%
rename from src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java
rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java
index 1a130c9..222bfe2 100644
--- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java
+++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
+
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
import java.awt.Color;
import java.awt.Rectangle;
@@ -24,15 +26,15 @@ import com.sun.jna.Pointer;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.jna.linux.GCallback;
-import dorkbox.systemTray.jna.linux.Gobject;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
-import dorkbox.systemTray.jna.linux.GtkTheme;
import dorkbox.systemTray.peer.CheckboxPeer;
+import dorkbox.systemTray.util.EventDispatch;
import dorkbox.systemTray.util.HeavyCheckMark;
import dorkbox.systemTray.util.ImageResizeUtil;
import dorkbox.util.OSUtil;
+import dorkbox.util.jna.linux.GCallback;
+import dorkbox.util.jna.linux.Gobject;
+import dorkbox.util.jna.linux.GtkEventDispatch;
+import dorkbox.util.jna.linux.GtkTheme;
@SuppressWarnings("deprecation")
class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCallback {
@@ -51,7 +53,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
(SystemTray.get().getMenu() instanceof _AppIndicatorNativeTray) && OSUtil.Linux.isUbuntu()) {
useFakeCheckMark = true;
} else {
- useFakeCheckMark = true;
+ useFakeCheckMark = false;
}
if (SystemTray.DEBUG) {
@@ -91,14 +93,20 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
* show this as a GTK Status Icon (not an AppIndicator), this way the "proper" checkbox is shown.
*/
GtkMenuItemCheckbox(final GtkMenu parent) {
- super(useFakeCheckMark ? Gtk.gtk_image_menu_item_new_with_mnemonic("") : Gtk.gtk_check_menu_item_new_with_mnemonic(""));
+ super(useFakeCheckMark ? Gtk2.gtk_image_menu_item_new_with_mnemonic("") : Gtk2.gtk_check_menu_item_new_with_mnemonic(""));
this.parent = parent;
handlerId = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
if (useFakeCheckMark) {
if (checkedFile == null) {
- final Color color = GtkTheme.getTextColor();
+ Color color = GtkTheme.getTextColor();
+ if (color == null) {
+ SystemTray.logger.error("Unable to determine the text color in use by your system. Please create an issue and include your " +
+ "full OS configuration and desktop environment, including theme details, such as the theme name, color " +
+ "variant, and custom theme options (if any).");
+ color = Color.BLACK;
+ }
if (checkedFile == null) {
Rectangle size = GtkTheme.getPixelTextHeight("X");
@@ -117,7 +125,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
setCheckedIconForFakeCheckMarks();
} else {
Gobject.g_signal_handler_block(_native, handlerId);
- Gtk.gtk_check_menu_item_set_active(_native, false);
+ Gtk2.gtk_check_menu_item_set_active(_native, false);
Gobject.g_signal_handler_unblock(_native, handlerId);
}
}
@@ -126,12 +134,12 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
int callback(final Pointer instance, final Pointer data) {
+ ActionListener callback = this.callback;
if (callback != null) {
- // this will redispatch to our created callback via `setCallback`
- GtkEventDispatch.proxyClick(null, callback);
+ GtkEventDispatch.proxyClick(callback);
}
- return Gtk.TRUE;
+ return Gtk2.TRUE;
}
@Override
@@ -153,7 +161,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void run() {
- Gtk.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
+ Gtk2.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
}
});
}
@@ -185,8 +193,8 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void run() {
- Gtk.gtk_menu_item_set_label(_native, textWithMnemonic);
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_menu_item_set_label(_native, textWithMnemonic);
+ Gtk2.gtk_widget_show_all(_native);
}
});
}
@@ -199,21 +207,26 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
if (callback != null) {
callback = new ActionListener() {
+ final ActionListener cb = menuItem.getCallback();
+
@Override
public
void actionPerformed(ActionEvent e) {
// this will run on the EDT, since we are calling it from the EDT. This can ALSO recursively call the callback
menuItem.setChecked(!isChecked);
- // we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
- ActionListener cb = menuItem.getCallback();
- if (cb != null) {
- try {
- cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
- } catch (Throwable throwable) {
- SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ // we want it to run on our own with our own action event info (so it is consistent across all platforms)
+ EventDispatch.runLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ try {
+ cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
+ } catch (Throwable throwable) {
+ SystemTray.logger.error("Error calling menu checkbox entry {} click event.", menuItem.getText(), throwable);
+ }
}
- }
+ });
}
};
}
@@ -240,7 +253,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
// https://github.com/GNOME/gtk/blob/master/gtk/gtkcheckmenuitem.c#L317
// this disables the signal handler, then enables it
Gobject.g_signal_handler_block(_native, handlerId);
- Gtk.gtk_check_menu_item_set_active(_native, isChecked);
+ Gtk2.gtk_check_menu_item_set_active(_native, isChecked);
Gobject.g_signal_handler_unblock(_native, handlerId);
}
}
@@ -252,24 +265,24 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
private
void setCheckedIconForFakeCheckMarks() {
if (checkedImage != null) {
- Gtk.gtk_container_remove(_native, checkedImage); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(_native, checkedImage); // will automatically get destroyed if no other references to it
checkedImage = null;
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
if (this.isChecked) {
- checkedImage = Gtk.gtk_image_new_from_file(checkedFile);
+ checkedImage = Gtk2.gtk_image_new_from_file(checkedFile);
} else {
- checkedImage = Gtk.gtk_image_new_from_file(uncheckedFile);
+ checkedImage = Gtk2.gtk_image_new_from_file(uncheckedFile);
}
- Gtk.gtk_image_menu_item_set_image(_native, checkedImage);
+ Gtk2.gtk_image_menu_item_set_image(_native, checkedImage);
// must always re-set always-show after setting the image
- Gtk.gtk_image_menu_item_set_always_show_image(_native, true);
+ Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_widget_show_all(_native);
}
@Override
@@ -288,12 +301,14 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void run() {
- Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
-
GtkMenuItemCheckbox.super.remove();
+ callback = null;
+
+ Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
+
if (image != null) {
- Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
image = null;
}
diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemSeparator.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java
similarity index 76%
rename from src/dorkbox/systemTray/nativeUI/GtkMenuItemSeparator.java
rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java
index 1cde789..bbd16fe 100644
--- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemSeparator.java
+++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
-import dorkbox.systemTray.peer.EntryPeer;
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
-class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
+import dorkbox.systemTray.peer.SeparatorPeer;
+import dorkbox.util.jna.linux.GtkEventDispatch;
+
+class GtkMenuItemSeparator extends GtkBaseMenuItem implements SeparatorPeer {
private final GtkMenu parent;
@@ -28,7 +29,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
*/
GtkMenuItemSeparator(final GtkMenu parent) {
- super(Gtk.gtk_separator_menu_item_new());
+ super(Gtk2.gtk_separator_menu_item_new());
this.parent = parent;
}
@@ -40,7 +41,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
@Override
public
void run() {
- Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
+ Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
parent.remove(GtkMenuItemSeparator.this);
}
diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemStatus.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java
similarity index 80%
rename from src/dorkbox/systemTray/nativeUI/GtkMenuItemStatus.java
rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java
index a85241d..5ea179e 100644
--- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemStatus.java
+++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
+
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
import dorkbox.systemTray.Status;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.systemTray.peer.StatusPeer;
+import dorkbox.util.jna.linux.GtkEventDispatch;
// you might wonder WHY this extends MenuEntryItem -- the reason is that an AppIndicator "status" will be offset from everyone else,
// where a GtkStatusIconTray + SwingUI will have everything lined up. (with or without icons). This is to normalize how it looks
@@ -31,7 +32,7 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
*/
GtkMenuItemStatus(final GtkMenu parent) {
- super(Gtk.gtk_image_menu_item_new_with_mnemonic(""));
+ super(Gtk2.gtk_image_menu_item_new_with_mnemonic(""));
this.parent = parent;
// need that extra space so it matches windows/mac
@@ -48,10 +49,10 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
// AppIndicator strips out markup text.
// https://mail.gnome.org/archives/commits-list/2016-March/msg05444.html
- Gtk.gtk_menu_item_set_label(_native, menuItem.getText());
- Gtk.gtk_widget_show_all(_native);
+ Gtk2.gtk_menu_item_set_label(_native, menuItem.getText());
+ Gtk2.gtk_widget_show_all(_native);
- Gtk.gtk_widget_set_sensitive(_native, false);
+ Gtk2.gtk_widget_set_sensitive(_native, false);
}
});
}
@@ -64,10 +65,10 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
@Override
public
void run() {
- Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
-
GtkMenuItemStatus.super.remove();
+ Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
+
parent.remove(GtkMenuItemStatus.this);
}
});
diff --git a/src/dorkbox/systemTray/nativeUI/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java
similarity index 83%
rename from src/dorkbox/systemTray/nativeUI/_AppIndicatorNativeTray.java
rename to src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java
index abf6d07..b9efc95 100644
--- a/src/dorkbox/systemTray/nativeUI/_AppIndicatorNativeTray.java
+++ b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -24,11 +24,11 @@ import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.Tray;
import dorkbox.systemTray.gnomeShell.Extension;
-import dorkbox.systemTray.jna.linux.AppIndicator;
-import dorkbox.systemTray.jna.linux.Gobject;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
-import dorkbox.systemTray.jna.linux.structs.AppIndicatorInstanceStruct;
import dorkbox.systemTray.util.ImageResizeUtil;
+import dorkbox.util.jna.linux.AppIndicator;
+import dorkbox.util.jna.linux.Gobject;
+import dorkbox.util.jna.linux.GtkEventDispatch;
+import dorkbox.util.jna.linux.structs.AppIndicatorInstanceStruct;
/**
* Class for handling all system tray interactions.
@@ -77,7 +77,7 @@ import dorkbox.systemTray.util.ImageResizeUtil;
*/
@SuppressWarnings("Duplicates")
public final
-class _AppIndicatorNativeTray extends Tray implements NativeUI {
+class _AppIndicatorNativeTray extends Tray {
private volatile AppIndicatorInstanceStruct appIndicator;
private boolean isActive = false;
@@ -91,7 +91,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
// has the name already been set for the indicator?
private volatile boolean setName = false;
- // appindicators DO NOT support anything other than PLAIN gtk-menus (which we hack to support swing menus)
+ // appindicators DO NOT support anything other than PLAIN gtk-menus
// they ALSO do not support tooltips!!
// https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
@@ -110,7 +110,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
protected final
void onMenuAdded(final Pointer menu) {
// see: https://code.launchpad.net/~mterry/libappindicator/fix-menu-leak/+merge/53247
- AppIndicator.app_indicator_set_menu(appIndicator, menu);
+ appIndicator.app_indicator_set_menu(menu);
if (!setName) {
setName = true;
@@ -126,7 +126,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
// in extension.js, so don't change it
// additionally, this is required to be set HERE (not somewhere else)
- AppIndicator.app_indicator_set_title(appIndicator, Extension.DEFAULT_NAME);
+ appIndicator.app_indicator_set_title(Extension.DEFAULT_NAME);
}
}
@@ -141,11 +141,11 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
if (visible && !enabled) {
// STATUS_PASSIVE hides the indicator
- AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_PASSIVE);
+ appIndicator.app_indicator_set_status(AppIndicator.STATUS_PASSIVE);
visible = false;
}
else if (!visible && enabled) {
- AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
+ appIndicator.app_indicator_set_status(AppIndicator.STATUS_ACTIVE);
visible = true;
}
}
@@ -164,11 +164,11 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
@Override
public
void run() {
- AppIndicator.app_indicator_set_icon(appIndicator, imageFile.getAbsolutePath());
+ appIndicator.app_indicator_set_icon(imageFile.getAbsolutePath());
if (!isActive) {
isActive = true;
- AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
+ appIndicator.app_indicator_set_status(AppIndicator.STATUS_ACTIVE);
}
}
});
@@ -186,30 +186,35 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
// no op.
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ // no op. see https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
+ }
+
@Override
public
void remove() {
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
if (!shuttingDown.getAndSet(true)) {
- // must happen asap, so our hook properly notices we are in shutdown mode
- final AppIndicatorInstanceStruct savedAppIndicator = appIndicator;
- appIndicator = null;
+ super.remove();
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
+ // must happen asap, so our hook properly notices we are in shutdown mode
+ final AppIndicatorInstanceStruct savedAppIndicator = appIndicator;
+ appIndicator = null;
+
// STATUS_PASSIVE hides the indicator
- AppIndicator.app_indicator_set_status(savedAppIndicator, AppIndicator.STATUS_PASSIVE);
+ savedAppIndicator.app_indicator_set_status(AppIndicator.STATUS_PASSIVE);
Pointer p = savedAppIndicator.getPointer();
Gobject.g_object_unref(p);
+
+ GtkEventDispatch.shutdownGui();
}
});
-
- super.remove();
-
- // does not need to be called on the dispatch (it does that)
- GtkEventDispatch.shutdownGui();
}
}
};
@@ -231,12 +236,6 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
bind(gtkMenu, null, systemTray);
}
- @Override
- protected
- void setTooltip_(final String tooltipText) {
- // https://bugs.launchpad.net/indicator-application/+bug/527458/comments/12
- }
-
@Override
public
boolean hasImage() {
diff --git a/src/dorkbox/systemTray/nativeUI/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java
similarity index 78%
rename from src/dorkbox/systemTray/nativeUI/_GtkStatusIconNativeTray.java
rename to src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java
index 994da4b..ee4b95f 100644
--- a/src/dorkbox/systemTray/nativeUI/_GtkStatusIconNativeTray.java
+++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.nativeUI;
+package dorkbox.systemTray.ui.gtk;
+
+import static dorkbox.util.jna.linux.Gtk.Gtk2;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -24,11 +26,11 @@ import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.Tray;
import dorkbox.systemTray.gnomeShell.Extension;
-import dorkbox.systemTray.jna.linux.GEventCallback;
-import dorkbox.systemTray.jna.linux.Gobject;
-import dorkbox.systemTray.jna.linux.Gtk;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
-import dorkbox.systemTray.jna.linux.structs.GdkEventButton;
+import dorkbox.util.JavaFX;
+import dorkbox.util.jna.linux.GEventCallback;
+import dorkbox.util.jna.linux.Gobject;
+import dorkbox.util.jna.linux.GtkEventDispatch;
+import dorkbox.util.jna.linux.structs.GdkEventButton;
/**
* Class for handling all system tray interactions via GTK.
@@ -37,7 +39,7 @@ import dorkbox.systemTray.jna.linux.structs.GdkEventButton;
*/
@SuppressWarnings("Duplicates")
public final
-class _GtkStatusIconNativeTray extends Tray implements NativeUI {
+class _GtkStatusIconNativeTray extends Tray {
private volatile Pointer trayIcon;
// http://code.metager.de/source/xref/gnome/Platform/gtk%2B/gtk/deprecated/gtkstatusicon.c
@@ -76,11 +78,11 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
boolean enabled = menuItem.getEnabled();
if (visible && !enabled) {
- Gtk.gtk_status_icon_set_visible(trayIcon, enabled);
+ Gtk2.gtk_status_icon_set_visible(trayIcon, enabled);
visible = false;
}
else if (!visible && enabled) {
- Gtk.gtk_status_icon_set_visible(trayIcon, enabled);
+ Gtk2.gtk_status_icon_set_visible(trayIcon, enabled);
visible = true;
}
}
@@ -99,11 +101,11 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
@Override
public
void run() {
- Gtk.gtk_status_icon_set_from_file(trayIcon, imageFile.getAbsolutePath());
+ Gtk2.gtk_status_icon_set_from_file(trayIcon, imageFile.getAbsolutePath());
if (!isActive) {
isActive = true;
- Gtk.gtk_status_icon_set_visible(trayIcon, true);
+ Gtk2.gtk_status_icon_set_visible(trayIcon, true);
}
}
});
@@ -121,6 +123,27 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
// no op
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ final String text = menuItem.getTooltip();
+
+ if (tooltipText != null && tooltipText.equals(text) ||
+ tooltipText == null && text != null) {
+ return;
+ }
+
+ tooltipText = text;
+
+ GtkEventDispatch.dispatch(new Runnable() {
+ @Override
+ public
+ void run() {
+ Gtk2.gtk_status_icon_set_tooltip_text(trayIcon, text);
+ }
+ });
+ }
+
@Override
public
void remove() {
@@ -131,7 +154,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
public
void run() {
// this hides the indicator
- Gtk.gtk_status_icon_set_visible(trayIcon, false);
+ Gtk2.gtk_status_icon_set_visible(trayIcon, false);
Gobject.g_object_unref(trayIcon);
// mark for GC
@@ -152,7 +175,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
@Override
public
void run() {
- trayIcon = Gtk.gtk_status_icon_new();
+ trayIcon = Gtk2.gtk_status_icon_new();
gtkCallback = new GEventCallback() {
@Override
@@ -161,8 +184,8 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
// show the swing menu on the EDT
// BUTTON_PRESS only (any mouse click)
if (event.type == 4) {
- Gtk.gtk_menu_popup(gtkMenu._nativeMenu, null, null, Gtk.gtk_status_icon_position_menu,
- trayIcon, 0, event.time);
+ Gtk2.gtk_menu_popup(gtkMenu._nativeMenu, null, null, Gtk2.gtk_status_icon_position_menu,
+ trayIcon, 0, event.time);
}
}
};
@@ -182,7 +205,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
// necessary for gnome icon detection/placement because we move tray icons around by title. This is hardcoded
// in extension.js, so don't change it
- Gtk.gtk_status_icon_set_title(trayIcon, Extension.DEFAULT_NAME);
+ Gtk2.gtk_status_icon_set_title(trayIcon, Extension.DEFAULT_NAME);
// can cause
// Gdk-CRITICAL **: gdk_window_thaw_toplevel_updates: assertion 'window->update_and_descendants_freeze_count > 0' failed
@@ -192,33 +215,18 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
// BUT this is REQUIRED when running JavaFX or Gnome For unknown reasons, the title isn't pushed to GTK, so our
// gnome-shell extension cannot see our tray icon -- so naturally, it won't move it to the "top" area and
// we appear broken.
- if (SystemTray.isJavaFxLoaded || Tray.usingGnome) {
- Gtk.gtk_status_icon_set_name(trayIcon, Extension.DEFAULT_NAME);
+ if (JavaFX.isLoaded || Tray.usingGnome) {
+ Gtk2.gtk_status_icon_set_name(trayIcon, Extension.DEFAULT_NAME);
}
}
});
bind(gtkMenu, null, systemTray);
- // do we need to install the GNOME extension??
- Tray.installExtension();
- }
-
- @Override
- protected
- void setTooltip_(final String tooltipText) {
- if (this.tooltipText.equals(tooltipText)){
- return;
+ // install the Gnome extension
+ if (Tray.usingGnome) {
+ Extension.install();
}
- this.tooltipText = tooltipText;
-
- GtkEventDispatch.dispatch(new Runnable() {
- @Override
- public
- void run() {
- Gtk.gtk_status_icon_set_tooltip_text(trayIcon, tooltipText);
- }
- });
}
@Override
diff --git a/src/dorkbox/systemTray/swingUI/SwingMenu.java b/src/dorkbox/systemTray/ui/swing/SwingMenu.java
similarity index 98%
rename from src/dorkbox/systemTray/swingUI/SwingMenu.java
rename to src/dorkbox/systemTray/ui/swing/SwingMenu.java
index a234ec0..59350ea 100644
--- a/src/dorkbox/systemTray/swingUI/SwingMenu.java
+++ b/src/dorkbox/systemTray/ui/swing/SwingMenu.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.io.File;
@@ -173,6 +173,12 @@ class SwingMenu implements MenuPeer {
});
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+
+ }
+
/**
* This removes all menu entries from this menu AND this menu from it's parent
*/
diff --git a/src/dorkbox/systemTray/swingUI/SwingMenuItem.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java
similarity index 80%
rename from src/dorkbox/systemTray/swingUI/SwingMenuItem.java
rename to src/dorkbox/systemTray/ui/swing/SwingMenuItem.java
index 75b9f8b..555b3ad 100644
--- a/src/dorkbox/systemTray/swingUI/SwingMenuItem.java
+++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -27,6 +27,7 @@ import dorkbox.systemTray.Entry;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.MenuItemPeer;
+import dorkbox.systemTray.util.EventDispatch;
import dorkbox.systemTray.util.ImageResizeUtil;
import dorkbox.util.SwingUtil;
@@ -126,28 +127,32 @@ class SwingMenuItem implements MenuItemPeer {
_native.removeActionListener(callback);
}
- if (menuItem.getCallback() != null) {
+ callback = menuItem.getCallback(); // can be set to null
+
+ if (callback != null) {
callback = new ActionListener() {
+ final ActionListener cb = menuItem.getCallback();
+
@Override
public
void actionPerformed(ActionEvent e) {
- // we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
- ActionListener cb = menuItem.getCallback();
- if (cb != null) {
- try {
- cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
- } catch (Throwable throwable) {
- SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ // we want it to run on our own with our own action event info (so it is consistent across all platforms)
+ EventDispatch.runLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ try {
+ cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
+ } catch (Throwable throwable) {
+ SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ }
}
- }
+ });
}
};
_native.addActionListener(callback);
}
- else {
- callback = null;
- }
}
@Override
@@ -166,6 +171,18 @@ class SwingMenuItem implements MenuItemPeer {
});
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ SwingUtil.invokeLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ _native.setToolTipText(menuItem.getTooltip());
+ }
+ });
+ }
+
@Override
public
void remove() {
diff --git a/src/dorkbox/systemTray/swingUI/SwingMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java
similarity index 85%
rename from src/dorkbox/systemTray/swingUI/SwingMenuItemCheckbox.java
rename to src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java
index 848264f..a72e590 100644
--- a/src/dorkbox/systemTray/swingUI/SwingMenuItemCheckbox.java
+++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -25,6 +25,7 @@ import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.CheckboxPeer;
+import dorkbox.systemTray.util.EventDispatch;
import dorkbox.systemTray.util.HeavyCheckMark;
import dorkbox.util.FontUtil;
import dorkbox.util.SwingUtil;
@@ -106,21 +107,26 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer {
if (callback != null) {
callback = new ActionListener() {
+ final ActionListener cb = menuItem.getCallback();
+
@Override
public
void actionPerformed(ActionEvent e) {
// this will run on the EDT, since we are calling it from the EDT
menuItem.setChecked(!isChecked);
- // we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
- ActionListener cb = menuItem.getCallback();
- if (cb != null) {
- try {
- cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
- } catch (Throwable throwable) {
- SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
+ // we want it to run on our own with our own action event info (so it is consistent across all platforms)
+ EventDispatch.runLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ try {
+ cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
+ } catch (Throwable throwable) {
+ SystemTray.logger.error("Error calling menu checkbox entry {} click event.", menuItem.getText(), throwable);
+ }
}
- }
+ });
}
};
diff --git a/src/dorkbox/systemTray/swingUI/SwingMenuItemSeparator.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java
similarity index 97%
rename from src/dorkbox/systemTray/swingUI/SwingMenuItemSeparator.java
rename to src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java
index 330273e..ebf0d0a 100644
--- a/src/dorkbox/systemTray/swingUI/SwingMenuItemSeparator.java
+++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import javax.swing.JSeparator;
diff --git a/src/dorkbox/systemTray/swingUI/SwingMenuItemStatus.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java
similarity index 98%
rename from src/dorkbox/systemTray/swingUI/SwingMenuItemStatus.java
rename to src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java
index 8b76e32..e1bb46c 100644
--- a/src/dorkbox/systemTray/swingUI/SwingMenuItemStatus.java
+++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.awt.Font;
diff --git a/src/dorkbox/systemTray/swingUI/SwingUIFactory.java b/src/dorkbox/systemTray/ui/swing/SwingUIFactory.java
similarity index 98%
rename from src/dorkbox/systemTray/swingUI/SwingUIFactory.java
rename to src/dorkbox/systemTray/ui/swing/SwingUIFactory.java
index 5bbff7c..ec28c3c 100644
--- a/src/dorkbox/systemTray/swingUI/SwingUIFactory.java
+++ b/src/dorkbox/systemTray/ui/swing/SwingUIFactory.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.awt.Color;
diff --git a/src/dorkbox/systemTray/swingUI/TrayPopup.java b/src/dorkbox/systemTray/ui/swing/TrayPopup.java
similarity index 98%
rename from src/dorkbox/systemTray/swingUI/TrayPopup.java
rename to src/dorkbox/systemTray/ui/swing/TrayPopup.java
index e5b37a9..072478e 100644
--- a/src/dorkbox/systemTray/swingUI/TrayPopup.java
+++ b/src/dorkbox/systemTray/ui/swing/TrayPopup.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.awt.Dimension;
import java.awt.Frame;
@@ -89,14 +89,17 @@ class TrayPopup extends JPopupMenu {
hiddenDialog.setBounds(0,0,0,0);
addPopupMenuListener(new PopupMenuListener() {
+ @Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
+ @Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
hiddenDialog.setVisible(false);
hiddenDialog.toBack();
}
+ @Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
diff --git a/src/dorkbox/systemTray/swingUI/_SwingTray.java b/src/dorkbox/systemTray/ui/swing/_SwingTray.java
similarity index 88%
rename from src/dorkbox/systemTray/swingUI/_SwingTray.java
rename to src/dorkbox/systemTray/ui/swing/_SwingTray.java
index b0350bf..0255957 100644
--- a/src/dorkbox/systemTray/swingUI/_SwingTray.java
+++ b/src/dorkbox/systemTray/ui/swing/_SwingTray.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package dorkbox.systemTray.swingUI;
+package dorkbox.systemTray.ui.swing;
import java.awt.AWTException;
import java.awt.Image;
@@ -28,9 +28,9 @@ import javax.swing.JPopupMenu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Tray;
-import dorkbox.systemTray.jna.linux.GtkEventDispatch;
import dorkbox.util.OS;
import dorkbox.util.SwingUtil;
+import dorkbox.util.jna.linux.GtkEventDispatch;
/**
* Class for handling all system tray interaction, via Swing.
@@ -42,7 +42,7 @@ import dorkbox.util.SwingUtil;
*/
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
public final
-class _SwingTray extends Tray implements SwingUI {
+class _SwingTray extends Tray {
private volatile SystemTray tray;
private volatile TrayIcon trayIcon;
@@ -167,6 +167,31 @@ class _SwingTray extends Tray implements SwingUI {
// no op
}
+ @Override
+ public
+ void setTooltip(final MenuItem menuItem) {
+ final String text = menuItem.getTooltip();
+
+ if (tooltipText != null && tooltipText.equals(text) ||
+ tooltipText == null && text != null) {
+ return;
+ }
+
+ tooltipText = text;
+
+ SwingUtil.invokeLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ // don't want to matter which (setImage/setTooltip/setEnabled) is done first, and if the image/enabled is changed, we
+ // want to make sure keep the tooltip text the same as before.
+ if (trayIcon != null) {
+ trayIcon.setToolTip(text);
+ }
+ }
+ });
+ }
+
@Override
public
void remove() {
@@ -199,27 +224,6 @@ class _SwingTray extends Tray implements SwingUI {
bind(swingMenu, null, systemTray);
}
- @Override
- protected
- void setTooltip_(final String tooltipText) {
- if (this.tooltipText.equals(tooltipText)){
- return;
- }
- this.tooltipText = tooltipText;
-
- SwingUtil.invokeLater(new Runnable() {
- @Override
- public
- void run() {
- // don't want to matter which (setImage/setTooltip/setEnabled) is done first, and if the image/enabled is changed, we
- // want to make sure keep the tooltip text the same as before.
- if (trayIcon != null) {
- trayIcon.setToolTip(tooltipText);
- }
- }
- });
- }
-
@Override
public
boolean hasImage() {
diff --git a/src/dorkbox/systemTray/util/CssParser.java b/src/dorkbox/systemTray/util/CssParser.java
deleted file mode 100644
index 1fc8c06..0000000
--- a/src/dorkbox/systemTray/util/CssParser.java
+++ /dev/null
@@ -1,711 +0,0 @@
-package dorkbox.systemTray.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import dorkbox.util.FileUtil;
-import dorkbox.util.OS;
-
-/**
- * A simple, basic CSS parser
- */
-public
-class CssParser {
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_NODES = false;
- private static final boolean DEBUG_GETTING_ATTRIBUTE_FROM_NODES = false;
- private static final boolean DEBUG_VERBOSE = false;
-
- private static
- String trim(String s) {
- s = s.replaceAll("\n", "");
- s = s.replaceAll("\t", "");
- // shrink all whitespace more than 1 space wide.
- while (s.contains(" ")) {
- s = s.replaceAll(" ", " ");
- }
- return s.trim();
- }
-
- public static
- class Css {
- List colorDefinitions;
- List cssNodes;
-
- Css(final List colorDefinitions, final List cssNodes) {
- this.colorDefinitions = colorDefinitions;
- this.cssNodes = cssNodes;
- }
-
- @Override
- public
- String toString() {
- StringBuilder def = new StringBuilder();
- for (Entry attribute : colorDefinitions) {
- def.append(attribute.key)
- .append(" : ")
- .append(attribute.value)
- .append(OS.LINE_SEPARATOR);
- }
-
-
- StringBuilder nodes = new StringBuilder();
- for (CssNode node : cssNodes) {
- nodes.append(OS.LINE_SEPARATOR)
- .append(node.toString())
- .append(OS.LINE_SEPARATOR);
- }
-
- return nodes.toString() + "\n\n" + nodes.toString();
- }
-
- public
- String getColorDefinition(final String colorString) {
- for (Entry definition : colorDefinitions) {
- if (definition.key.equals(colorString)) {
- return definition.value;
- }
- }
-
- return null;
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- public static
- class CssNode {
- public String label;
- public List attributes;
-
- CssNode(final String label, final List attributes) {
- this.label = trim(label);
- this.attributes = attributes;
- }
-
- @Override
- public
- String toString() {
- StringBuilder builder = new StringBuilder();
- for (Entry attribute : attributes) {
- builder.append("\t")
- .append(attribute.key)
- .append(" : ")
- .append(attribute.value)
- .append(OS.LINE_SEPARATOR);
- }
-
- return label + "\n" + builder.toString();
- }
- }
-
-
- @SuppressWarnings("WeakerAccess")
- public static
- class Entry {
- public String key;
- public String value;
-
- Entry(final String key, final String value) {
- this.key = trim(key);
- this.value = trim(value);
- }
-
- @Override
- public
- String toString() {
- return key + " : " + value;
- }
-
- @SuppressWarnings("SimplifiableIfStatement")
- @Override
- public
- boolean equals(final Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- final Entry attribute = (Entry) o;
-
- if (key != null ? !key.equals(attribute.key) : attribute.key != null) {
- return false;
- }
- return value != null ? value.equals(attribute.value) : attribute.value == null;
- }
-
- @Override
- public
- int hashCode() {
- int result = key != null ? key.hashCode() : 0;
- result = 31 * result + (value != null ? value.hashCode() : 0);
- return result;
- }
- }
-
- /**
- * Gets the sections of text, of the specified CSS nodes.
- *
- * @param css the css
- * @param nodes the section nodes we are interested in (ie: .menuitem, *)
- * @param states the section state we are interested in (ie: focus, hover, active). Null (or empty list) means no state.
- */
- public static
- List getSections(Css css, String[] nodes, String[] states) {
- if (states == null) {
- states = new String[0];
- }
-
- List sections = new ArrayList(css.cssNodes.size());
-
- // our sections can ONLY contain what we are looking for, as a word.
- for (final CssNode section : css.cssNodes) {
- String label = section.label;
- boolean canSave = false;
-
- if (!section.attributes.isEmpty()) {
- main:
- for (String node : nodes) {
- if (label.equals(node)) {
- // exactly what our node is
- canSave = true;
- break;
- }
- if (label.length() > node.length() && label.startsWith(node)) {
- // a combination of our node + MAYBE some other node
- int index = node.length();
- label = trim(label.substring(index));
-
- if (label.charAt(0) == '>') {
- // if it's an override, we have to check what it overrides.
- label = label.substring(1);
- }
-
- // then, this MUST be one of our other nodes (that we are looking for, otherwise remove this section)
- for (String n : nodes) {
- //noinspection StringEquality
- if (n != node && label.startsWith(n)) {
- canSave = true;
- break main;
- }
- }
- }
- }
-
- if (canSave) {
- // if this section is for a state we DO NOT care about, remove it
- int stateIndex = label.lastIndexOf(':');
- if (stateIndex != -1) {
- String stateValue = label.substring(stateIndex + 1);
- boolean saveState = false;
- for (String state : states) {
- if (stateValue.equals(state)) {
- // this is a state we care about
- saveState = true;
- break;
- }
- }
-
- if (!saveState) {
- canSave = false;
- }
- }
- }
- }
-
- if (canSave) {
- sections.add(section);
- }
- }
-
-
- if (DEBUG_NODES) {
- for (CssNode section : sections) {
- System.err.println("--------------");
- System.err.println(section);
- System.err.println("--------------");
- }
- }
-
- return sections;
- }
-
- /**
- * find an attribute name from the list of sections. The incoming sections will all be related to one of the nodes, we prioritize
- * them on WHO has the attribute we are looking for.
- *
- * @param sections the css sections
- * @param attributeName the name of the attribute we are looking for.
- * @param equalsOrContained true if we want to EXACT match, false if the attribute key can contain what we are looking for.
- *
- * @return the attribute value, if found
- */
- @SuppressWarnings("Duplicates")
- public static
- List getAttributeFromSections(final List sections, final String attributeName, boolean equalsOrContained) {
-
- // a list of sections that contains the exact attribute we are looking for
- List sectionsWithAttribute = new ArrayList();
- for (CssNode cssNode : sections) {
- for (Entry attribute : cssNode.attributes) {
- if (equalsOrContained) {
- if (attribute.key.equals(attributeName)) {
- sectionsWithAttribute.add(new Entry(cssNode.label, attribute.value));
- }
- }
- else {
- if (attribute.key.contains(attributeName)) {
- sectionsWithAttribute.add(new Entry(cssNode.label, attribute.value));
- }
- }
- }
- }
-
- if (DEBUG_GETTING_ATTRIBUTE_FROM_NODES) {
- System.err.println("--------------");
- System.err.println("Cleaned Sections");
- System.err.println("--------------");
- for (Entry section : sectionsWithAttribute) {
- System.err.println("--------------");
- System.err.println(section);
- System.err.println("--------------");
- }
- }
-
- return sectionsWithAttribute;
- }
-
- public static
- void injectAdditionalCss(final File parent, final StringBuilder stringBuilder) {
- // not the BEST way to do this because duplicates are not merged at all.
-
- int start = 0;
- while (start != -1) {
- // now check if it says: @import url("gtk-main.css")
- start = stringBuilder.indexOf("@import url(", start);
-
- if (start != -1) {
- int end = stringBuilder.indexOf("\")", start);
- if (end != -1) {
- String url = stringBuilder.substring(start + 13, end);
- stringBuilder.delete(start, end + 2); // 2 is the size of ")
-
- if (DEBUG) {
- System.err.println("import url: " + url);
- }
- try {
- // now inject the new file where the import command was.
- File file = new File(parent, url);
- StringBuilder stringBuilder2 = new StringBuilder((int) (file.length()));
- FileUtil.read(file, stringBuilder2);
-
- removeComments(stringBuilder2);
-
- stringBuilder.insert(start, stringBuilder2);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
-
- @SuppressWarnings("Duplicates")
- public static
- void removeComments(final StringBuilder stringBuilder) {
- // remove block comments, /* .... */ This can span multiple lines
- int start = 0;
- while (start != -1) {
- // get the start of a comment
- start = stringBuilder.indexOf("/*", start);
-
- if (start != -1) {
- // get the end of a comment
- int end = stringBuilder.indexOf("*/", start);
- if (end != -1) {
- stringBuilder.delete(start, end + 2); // 2 is the size of */
-
- // sometimes when the comments are removed, there is a trailing newline. remove that too. Works for windows too
- if (stringBuilder.charAt(start) == '\n') {
- stringBuilder.delete(start, start + 1);
- }
- else {
- start++;
- }
- }
- }
- }
-
- // now remove comments that start with // (line MUST start with //)
- start = 0;
- while (start != -1) {
- // get the start of a comment
- start = stringBuilder.indexOf("//", start);
-
- if (start != -1) {
- // the comment is at the start of a line
- if (start == 0 || stringBuilder.charAt(start - 1) == '\n') {
- // get the end of the comment (the end of the line)
- int end = stringBuilder.indexOf("\n", start);
- if (end != -1) {
- stringBuilder.delete(start, end + 1); // 1 is the size of \n
- }
- }
-
- // sometimes when the comments are removed, there is a trailing newline. remove that too. Works for windows too
- if (stringBuilder.charAt(start) == '\n') {
- stringBuilder.delete(start, start + 1);
- }
- else if (start > 0) {
- start++;
- }
- }
- }
-
- // now remove comments that start with # (line MUST start with #)
- start = 0;
- while (start != -1) {
- // get the start of a comment
- start = stringBuilder.indexOf("#", start);
-
- if (start != -1) {
- // the comment is at the start of a line
- if (start == 0 || stringBuilder.charAt(start - 1) == '\n') {
- // get the end of the comment (the end of the line)
- int end = stringBuilder.indexOf("\n", start);
- if (end != -1) {
- stringBuilder.delete(start, end + 1); // 1 is the size of \n
- }
- }
-
- // sometimes when the comments are removed, there is a trailing newline. remove that too. Works for windows too
- if (stringBuilder.charAt(start) == '\n') {
- stringBuilder.delete(start, start + 1);
- }
- else if (start > 0) {
- start++;
- }
- }
- }
- }
-
- /**
- * @return the parsed out CSS, or NULL
- */
- public static
- Css parse(final String css) {
- if (css == null) {
- return null;
- }
-
- // extract the color definitions
- List colorDefinitions = getColorDefinition(css);
-
-
- int endOfColorDefinitions = css.indexOf("{");
- // find the start of the line.
- for (int lineStart = endOfColorDefinitions; lineStart > 0; lineStart--) {
- if (css.charAt(lineStart) == '\n') {
- endOfColorDefinitions = lineStart + 1;
- break;
- }
- }
-
-
- // collect a list of all of the sections that have what we are interested in.
- List sections = new ArrayList();
-
- int index = endOfColorDefinitions;
- int length = css.length();
-
- // now create a list of the CSS nodes
- do {
- int endOfNodeLabels = css.indexOf("{", index);
- if (endOfNodeLabels == -1) {
- break;
- }
-
- int endOfSection = css.indexOf("}", endOfNodeLabels + 1) + 1;
- int endOfSectionTest = css.indexOf("}", index) + 1;
-
-
- // this makes sure that weird parsing errors don't happen as a result of node keywords appearing in node sections
- if (endOfSection != endOfSectionTest) {
- // advance the index
- index = endOfSection;
- continue;
- }
-
- // find the start of the line.
- for (int lineStart = index; lineStart > 0; lineStart--) {
- if (css.charAt(lineStart) == '\n') {
- index = lineStart + 1;
- break;
- }
- }
-
- String nodeLabel = css.substring(index, endOfNodeLabels)
- .trim();
-
- List attributes = new ArrayList();
-
- // split the section into an arrayList, one per item. Split by attribute element
- String nodeSection = css.substring(endOfNodeLabels, endOfSection);
- int sectionStart = nodeSection.indexOf('{') + 1;
- int sectionEnd = nodeSection.indexOf('}');
-
- while (sectionStart != -1) {
- int end = nodeSection.indexOf(';', sectionStart);
- if (end != -1) {
- int separator = nodeSection.indexOf(':', sectionStart);
-
- // String.charAt() is a bit slow because of all the extra range checking it performs
- if (separator < end) {
- // the parenthesis must be balanced for the value
- short parenCount = 0;
-
- int j = separator;
- while (j < end) {
- j++;
-
- char c = nodeSection.charAt(j);
- if (c == '(') {
- parenCount++;
- }
- else if (c == ')') {
- parenCount--;
- }
- }
-
- j--;
- if (parenCount > 0) {
- do {
- // find the extended balancing paren
- if (nodeSection.charAt(j) == ')') {
- parenCount--;
- }
-
- j++;
- } while (parenCount > 0 && j < sectionEnd);
- }
-
- end = j + 1;
-
- String key = nodeSection.substring(sectionStart, separator);
- String value = nodeSection.substring(separator + 1, end);
- attributes.add(new Entry(key, value));
- }
- sectionStart = end + 1;
- }
- else {
- break;
- }
- }
-
- // if the label contains ',' this means that MORE than that one CssNode has the same attributes. We want to split it up and duplicate it.
- int multiIndex = nodeLabel.indexOf(',');
- if (multiIndex != -1) {
- multiIndex = 0;
- while (multiIndex != -1) {
- int multiEndIndex = nodeLabel.indexOf(',', multiIndex);
- if (multiEndIndex != -1) {
- String newLabel = nodeLabel.substring(multiIndex, multiEndIndex);
-
- sections.add(new CssNode(newLabel, attributes));
- multiIndex = multiEndIndex + 1;
- }
- else {
- // now add the last part of the label.
- String newLabel = nodeLabel.substring(multiIndex);
- sections.add(new CssNode(newLabel, attributes));
- multiIndex = -1;
- }
- }
-
- }
- else {
- // we are the only one with these attributes
- sections.add(new CssNode(nodeLabel, attributes));
- }
-
- // advance the index
- index = endOfSection;
- } while (index < length);
-
- // now merge all nodes that have the same labels.
- for (Iterator iterator = sections.iterator(); iterator.hasNext(); ) {
- final CssNode section = iterator.next();
-
- if (section != null) {
- String label = section.label;
-
- for (int i = 0; i < sections.size(); i++) {
- final CssNode section2 = sections.get(i);
- if (section != section2 && section2 != null && label.equals(section2.label)) {
- sections.set(i, null);
-
- // now merge both lists.
- final List attributes = section.attributes;
- for (int i1 = 0; i1 < attributes.size(); i1++) {
- final Entry attribute = attributes.get(i1);
- for (Iterator iterator2 = section2.attributes.iterator(); iterator2.hasNext(); ) {
- final Entry attribute2 = iterator2.next();
-
- if (attribute.equals(attribute2)) {
- iterator2.remove();
- break;
- }
- }
- }
-
- // now both lists are unique.
- section.attributes.addAll(section2.attributes);
- }
- }
- } else {
- // clean up the (possible) null entries.
- iterator.remove();
- }
- }
-
- // final cleanup loop for empty CSS sections
- for (Iterator iterator = sections.iterator(); iterator.hasNext(); ) {
- final CssNode section = iterator.next();
-
- for (Iterator iterator1 = section.attributes.iterator(); iterator1.hasNext(); ) {
- final Entry attribute = iterator1.next();
-
- if (attribute == null) {
- iterator1.remove();
- }
- }
-
- if (section.attributes.isEmpty()) {
- iterator.remove();
- }
- }
-
- return new Css(colorDefinitions, sections);
- }
-
-
- /**
- * Gets the color definitions (which exists at the beginnning of the CSS/gtkrc files) as a list of key/value attributes. The values
- * are also recursively resolved.
- */
- private static
- List getColorDefinition(final String css) {
- // since it's a color definition, it will start a very specific way. This will recursively get the defined colors.
- List defines = new ArrayList();
- {
- // have to setup the "define color" section
- String colorDefine = "@define-color";
- int length = colorDefine.length() + 1;
-
- int start = 0;
- int mid = 0;
- int end = 0;
-
- while (start < css.length()) {
- start = css.indexOf(colorDefine, start);
- if (start == -1) {
- break;
- }
-
- start += length;
- end = css.indexOf(';', start) + 1; // include the ;
- mid = css.indexOf(' ', start);
-
- if (mid > -1) {
- String label = css.substring(start, mid);
- String value = css.substring(mid + 1, end);
-
- // remove the trailing ;
- int endOfValue = value.length() - 1;
- if (value.charAt(endOfValue) == ';') {
- value = value.substring(0, endOfValue);
- }
-
- Entry attribute = new Entry(label, value);
- defines.add(attribute);
- }
-
- start = end + 1;
- }
- }
-
- // now to recursively figure out the color definitions
- boolean allClean = false;
- while (!allClean) {
- allClean = true;
-
- for (Entry d : defines) {
- String value = d.value;
- int i = value.indexOf('@');
- if (i > -1) {
- // where is the last letter?
- int lastLetter;
- for (lastLetter = i+1; lastLetter < value.length(); lastLetter++) {
- char c = value.charAt(lastLetter);
- if (!Character.isLetter(c) && c != '_') {
- allClean = false;
- break;
- }
- }
-
- String replacement = value.substring(i+1, lastLetter);
-
- // the target value for replacement will ALWAYS be earlier in the list.
- for (Entry d2 : defines) {
- if (d2.key.equals(replacement)) {
- int length = d2.value.length();
-
- // string.replace() goes bonkers on the heap... This keeps it under control
- StringBuilder builder = new StringBuilder(value);
- builder.insert(i, d2.value);
- builder.delete(i + length, lastLetter + length);
-
- d.value = builder.toString();
- break;
- }
- }
- }
- }
- }
-
- return defines;
- }
-
- /**
- * Select the most relevant CSS attribute based on the input cssNodes
- *
- * @param cssNodes the list of in-order cssNodes we care about
- * @param entries a list of key/value pairs, where the key is the CSS Node label, and the value is the attribute value
- *
- * @return the most relevant attribute or NULL
- */
- public static
- String selectMostRelevantAttribute(final String[] cssNodes, final List entries) {
- // we care about 'cssNodes' IN ORDER, so if the first one has what we are looking for, that is what we choose.
- for (String node : cssNodes) {
- for (Entry s : entries) {
- if (s.key.equals(node)) {
- return s.value;
- }
- }
-
- // check now if one of the children has it
- for (Entry s : entries) {
- if (s.key.contains(node)) {
- return s.value;
- }
- }
- }
-
- return null;
- }
-}
diff --git a/src/dorkbox/systemTray/util/EventDispatch.java b/src/dorkbox/systemTray/util/EventDispatch.java
new file mode 100644
index 0000000..9ae2818
--- /dev/null
+++ b/src/dorkbox/systemTray/util/EventDispatch.java
@@ -0,0 +1,39 @@
+package dorkbox.systemTray.util;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import dorkbox.util.NamedThreadFactory;
+
+/**
+ * Adds events to a single thread event dispatch, so that regardless of OS, all event callbacks happen on the same thread -- which is NOT
+ * the GTK/AWT/SWING event dispatch thread. There can be ODD peculiarities across on GTK with how AWT/SWING react with the GTK Event
+ * Dispatch Thread.
+ */
+public
+class EventDispatch {
+ private static ExecutorService eventDispatchExecutor = null;
+
+ /**
+ * Schedule an event to occur sometime in the future.
+ */
+ public static synchronized
+ void runLater(Runnable runnable) {
+ if (eventDispatchExecutor == null) {
+ eventDispatchExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("SystemTrayEventDispatch", false));
+ }
+
+ eventDispatchExecutor.execute(runnable);
+ }
+
+ /**
+ * Shutdown the event dispatch
+ */
+ public static synchronized
+ void shutdown() {
+ if (eventDispatchExecutor != null) {
+ eventDispatchExecutor.shutdownNow();
+ eventDispatchExecutor = null;
+ }
+ }
+}
diff --git a/src/dorkbox/systemTray/util/ImageResizeUtil.java b/src/dorkbox/systemTray/util/ImageResizeUtil.java
index bd7f718..307c849 100644
--- a/src/dorkbox/systemTray/util/ImageResizeUtil.java
+++ b/src/dorkbox/systemTray/util/ImageResizeUtil.java
@@ -220,33 +220,30 @@ class ImageResizeUtil {
// if it's already there, we have to delete it
newFile.delete();
- // resize the image, keep aspect ratio
Image image = ImageUtil.getImageImmediate(ImageIO.read(inputStream));
- int height = image.getHeight(null);
- int width = image.getWidth(null);
+ BufferedImage bufferedImage = ImageUtil.getBufferedImage(image);
+ // resize the image, keep aspect ratio
+ int width = bufferedImage.getWidth();
+ int height = bufferedImage.getHeight();
if (width > height) {
- // fit on the width
- image = image.getScaledInstance(size, -1, Image.SCALE_SMOOTH);
- } else {
- // fit on the height
- image = image.getScaledInstance(-1, size, Image.SCALE_SMOOTH);
+ bufferedImage = ImageUtil.resizeImage(bufferedImage, size, -1);
+ }
+ else {
+ bufferedImage = ImageUtil.resizeImage(bufferedImage, -1, size);
}
+ // make the image "square" so there is padding on the sides that are smaller
+ bufferedImage = ImageUtil.getSquareBufferedImage(bufferedImage);
+
// now write out the new one
- BufferedImage bufferedImage = ImageUtil.getSquareBufferedImage(image);
ImageIO.write(bufferedImage, "png", newFile); // made up extension
return newFile;
}
-
-
-
-
-
public static
File shouldResizeOrCache(final boolean isTrayImage, final File imageFile) {
if (imageFile == null) {
@@ -324,8 +321,8 @@ class ImageResizeUtil {
try {
final Image trayImage = ImageUtil.getImageImmediate(image);
-
BufferedImage bufferedImage = ImageUtil.getBufferedImage(trayImage);
+
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", os);
InputStream imageInputStream = new ByteArrayInputStream(os.toByteArray());
diff --git a/src/dorkbox/systemTray/util/JavaFX.java b/src/dorkbox/systemTray/util/JavaFX.java
deleted file mode 100644
index 9aeb79a..0000000
--- a/src/dorkbox/systemTray/util/JavaFX.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2016 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.util;
-
-
-import java.lang.reflect.Method;
-
-import dorkbox.systemTray.SystemTray;
-import dorkbox.util.OS;
-
-/**
- * Utility methods for JavaFX.
- *
- * We use reflection for these methods so that we can compile everything under Java 1.6 (which doesn't have JavaFX).
- */
-public
-class JavaFX {
-
- // Methods are cached for performance
- private static final Method dispatchMethod;
- private static final Method isEventThreadMethod;
- private static final Object isEventThreadObject;
-
- static {
- Method _isEventThreadMethod = null;
- Method _dispatchMethod = null;
- Object _isEventThreadObject = null;
-
- try {
- Class> clazz = Class.forName("javafx.application.Platform");
- _dispatchMethod = clazz.getMethod("runLater", Runnable.class);
-
- // JAVA 7
- // javafx.application.Platform.isFxApplicationThread();
-
- // JAVA 8
- // com.sun.javafx.tk.Toolkit.getToolkit().isFxUserThread();
- if (OS.javaVersion <= 7) {
- clazz = Class.forName("javafx.application.Platform");
- _isEventThreadMethod = clazz.getMethod("isFxApplicationThread");
- _isEventThreadObject = null;
- } else {
- clazz = Class.forName("com.sun.javafx.tk.Toolkit");
- _isEventThreadMethod = clazz.getMethod("getToolkit");
-
- _isEventThreadObject = _isEventThreadMethod.invoke(null);
- _isEventThreadMethod = _isEventThreadObject.getClass()
- .getMethod("isFxUserThread", (java.lang.Class>[])null);
- }
- } catch (Throwable e) {
- SystemTray.logger.error("Cannot initialize JavaFX", e);
- }
-
- dispatchMethod = _dispatchMethod;
- isEventThreadMethod = _isEventThreadMethod;
- isEventThreadObject = _isEventThreadObject;
- }
-
- public static
- void init() {
- if (dispatchMethod == null || isEventThreadMethod == null) {
- SystemTray.logger.error("Unable to initialize JavaFX! Please create an issue with your OS and Java " +
- "version so we may further investigate this issue.");
- }
- }
-
-
- public static
- void dispatch(final Runnable runnable) {
-// javafx.application.Platform.runLater(runnable);
-
- try {
- dispatchMethod.invoke(null, runnable);
- } catch (Throwable e) {
- SystemTray.logger.error("Unable to execute JavaFX runLater(). Please create an issue with your OS and Java " +
- "version so we may further investigate this issue.");
- }
- }
-
- public static
- boolean isEventThread() {
- // JAVA 7
- // javafx.application.Platform.isFxApplicationThread();
-
- // JAVA 8
- // com.sun.javafx.tk.Toolkit.getToolkit().isFxUserThread();
-
- try {
- if (OS.javaVersion <= 7) {
- return (Boolean) isEventThreadMethod.invoke(null);
- } else {
- return (Boolean) isEventThreadMethod.invoke(isEventThreadObject, (java.lang.Class>[])null);
- }
- } catch (Throwable e) {
- SystemTray.logger.error("Unable to check if JavaFX is in the event thread. Please create an issue with your OS and Java " +
- "version so we may further investigate this issue.");
- }
-
- return false;
- }
-
- public static
- void onShutdown(final Runnable runnable) {
- // com.sun.javafx.tk.Toolkit.getToolkit()
- // .addShutdownHook(runnable);
-
- try {
- Class> clazz = Class.forName("com.sun.javafx.tk.Toolkit");
- Method method = clazz.getMethod("getToolkit");
- Object o = method.invoke(null);
- Method m = o.getClass()
- .getMethod("addShutdownHook", Runnable.class);
- m.invoke(o, runnable);
- } catch (Throwable e) {
- if (SystemTray.DEBUG) {
- SystemTray.logger.error("Cannot initialize JavaFX", e);
- }
- SystemTray.logger.error("Unable to insert shutdown hook into JavaFX. Please create an issue with your OS and Java " +
- "version so we may further investigate this issue.");
- }
- }
-}
diff --git a/src/dorkbox/systemTray/util/LinuxSwingUI.java b/src/dorkbox/systemTray/util/LinuxSwingUI.java
index 94047ac..6e10387 100644
--- a/src/dorkbox/systemTray/util/LinuxSwingUI.java
+++ b/src/dorkbox/systemTray/util/LinuxSwingUI.java
@@ -31,7 +31,7 @@ import javax.swing.plaf.metal.MetalBorders;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
-import dorkbox.systemTray.swingUI.SwingUIFactory;
+import dorkbox.systemTray.ui.swing.SwingUIFactory;
import dorkbox.util.swing.DefaultMenuItemUI;
import dorkbox.util.swing.DefaultPopupMenuUI;
import dorkbox.util.swing.DefaultSeparatorUI;
@@ -56,7 +56,6 @@ class LinuxSwingUI implements SwingUIFactory {
public static class Metal_MenuItemBorder extends MetalBorders.MenuItemBorder {
private final int verticalPadding;
- public
Metal_MenuItemBorder(int verticalPadding) {
this.verticalPadding = verticalPadding;
}
@@ -69,6 +68,10 @@ class LinuxSwingUI implements SwingUIFactory {
}
}
+ private static PopupMenuUI popupMenuUI = null;
+ private static MenuItemUI menuItemUI = null;
+ private static SeparatorUI separatorUI = null;
+
/**
* Allows one to specify the Look & Feel of the menus (The main SystemTray and sub-menus)
*
@@ -134,7 +137,6 @@ class LinuxSwingUI implements SwingUIFactory {
return new DefaultSeparatorUI(jSeparator);
}
-
/**
* This saves a vector CheckMark to a correctly sized PNG file. The checkmark image will ALWAYS be centered in the targetImageSize
* (which is square)
diff --git a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java
index e67cef7..1ff4a8b 100644
--- a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java
+++ b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java
@@ -15,11 +15,8 @@
*/
package dorkbox.systemTray.util;
-import static com.sun.jna.platform.win32.WinDef.HDC;
-import static com.sun.jna.platform.win32.WinDef.POINT;
import static com.sun.jna.platform.win32.WinUser.SM_CYMENUCHECK;
-import static dorkbox.util.jna.windows.GDI32.GetDeviceCaps;
-import static dorkbox.util.jna.windows.GDI32.LOGPIXELSX;
+import static com.sun.jna.platform.win32.WinUser.SM_CYSMICON;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
@@ -28,22 +25,16 @@ import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.lang.reflect.Field;
-import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JMenuItem;
-import com.sun.jna.Pointer;
-import com.sun.jna.ptr.IntByReference;
-
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.Tray;
-import dorkbox.systemTray.jna.linux.GtkTheme;
-import dorkbox.systemTray.swingUI._SwingTray;
+import dorkbox.systemTray.ui.swing._SwingTray;
import dorkbox.util.OS;
-import dorkbox.util.OSUtil;
import dorkbox.util.SwingUtil;
-import dorkbox.util.jna.windows.ShCore;
+import dorkbox.util.jna.linux.GtkTheme;
import dorkbox.util.jna.windows.User32;
public
@@ -52,15 +43,9 @@ class SizeAndScalingUtil {
static int TRAY_SIZE = 0;
static int TRAY_MENU_SIZE = 0;
- static {
-// if (OSUtil.Windows.isWindows8_1_plus()) {
-// ShCore.SetProcessDpiAwareness(ProcessDpiAwareness.PROCESS_SYSTEM_DPI_AWARE);
-// }
- }
-
- private static
+ public static
double getMacOSScaleFactor() {
- // apple will ALWAYS return 2.0 on (apple) retina displays. If it's a non-standard, then who knows...
+ // apple will ALWAYS return 2.0 on (apple) retina displays. This is enforced by apple
// java6 way of getting it...
if (OS.javaVersion == 6) {
@@ -102,54 +87,11 @@ class SizeAndScalingUtil {
}
- /**
- * Number of pixels per logical inch along the screen width. In a system with multiple display monitors, this value is the
- * same for all monitors.
- */
public static
- int getWindowsLogicalDPI() {
- // get the logical resolution
- HDC screen = User32.IMPL.GetDC(null);
- int logical_dpiX = GetDeviceCaps(screen, LOGPIXELSX);
- User32.IMPL.ReleaseDC(null, screen);
-
- if (SystemTray.DEBUG) {
- SystemTray.logger.debug("Windows logical DPI: '{}'", logical_dpiX);
- }
-
- return logical_dpiX;
- }
-
- public static
- int getWindowsPrimaryMonitorHardwareDPI() {
- // WINDOWS 8.1+ ONLY! Parts of this API were added in Windows 8.1, so this will not work at all for < 8.1
- if (OSUtil.Windows.isWindows8_1_plus()) {
- // FROM: https://blogs.msdn.microsoft.com/oldnewthing/20070809-00/?p=25643
- // to get the **PRIMARY** monitor, pass in point 0,0
-
- IntByReference hardware_dpiX = new IntByReference();
- // get the primary monitor handle
- Pointer pointer = User32.IMPL.MonitorFromPoint(new POINT(0, 0), 1);// MONITOR_DEFAULTTOPRIMARY -> 1
-
- ShCore.GetDpiForMonitor(pointer, 1, hardware_dpiX, new IntByReference()); // don't care about y
-
- int value = hardware_dpiX.getValue();
-
- if (SystemTray.DEBUG) {
- SystemTray.logger.debug("Windows hardware DPI: '{}'", value);
- }
-
- return value;
- }
-
- return 0;
- }
-
- public static
- int getTrayImageSize(final Class extends Tray> trayType) {
+ int getTrayImageSize() {
if (TRAY_SIZE == 0) {
if (OS.isLinux()) {
- TRAY_SIZE = GtkTheme.getIndicatorSize(trayType);
+ TRAY_SIZE = GtkTheme.getIndicatorSize();
}
else if (OS.isMacOsX()) {
// these are the standard icon sizes. From what I can tell, they are Apple defined, and cannot be changed.
@@ -160,99 +102,8 @@ class SizeAndScalingUtil {
}
}
else if (OS.isWindows()) {
- int[] version = OSUtil.Windows.getVersion();
- if (SystemTray.DEBUG) {
- SystemTray.logger.debug("Windows version: '{}'", Arrays.toString(version));
- }
-
- // http://kynosarges.org/WindowsDpi.html
-
- // 96 DPI = 100% scaling
- // 120 DPI = 125% scaling
- // 144 DPI = 150% scaling
- // 192 DPI = 200% scaling
- final double defaultDPI = 96.0;
-
- // windows 8/8.1/10 are the only windows OSes to do scaling properly (XP/Vista/7 do DPI scaling, which is terrible anyways)
-
- // XP - 7.0 - only global DPI settings, no scaling
- // 8.0 - only global DPI settings + scaling
- // 8.1 - 10 - global + per-monitor DPI settings + scaling
-
- // get the logical resolution
- int windowsLogicalDPI = getWindowsLogicalDPI();
-
- if (!OSUtil.Windows.isWindows8_1_plus()) {
- // < Windows 8.1 doesn't do scaling + DPI changes, they just "magnify" (but not scale w/ DPI) the icon + change the font size.
- // 96 DPI = 16
- // 120 DPI = 20 (16 * 1.25)
- // 144 DPI = 24 (16 * 1.5)
- TRAY_SIZE = 16;
- return TRAY_SIZE;
- }
- else {
- // Windows 8.1+ does proper scaling, so an icon at a higher resolution is drawn, instead of drawing the "original"
- // resolution image and scaling it up to the new size
-
- // 96 DPI = 16
- // 120 DPI = 20 (16 * 1.25)
- // 144 DPI = 24 (16 * 1.5)
- TRAY_SIZE = (int) (16 * (windowsLogicalDPI / defaultDPI));
- return TRAY_SIZE;
- }
-
-// NOTE: can override DPI settings
-// * At a 100% scaling, the DPI is 96.
-//
-//
-// Integer winDPIScaling;
-// if (PlatformDetector.isWin7()) {
-// winDPIScaling = 1;
-// } else {
-// // Win 8 or later.
-// winDPIScaling = RegistryUtil.getRegistryIntValue(
-// RegistryUtil.HKEY_CURRENT_USER,
-// "Control Panel\\Desktop",
-// "Win8DpiScaling");
-// if(winDPIScaling == null){
-// winDPIScaling = 0;
-// }
-// }
-//
-// Integer desktopDPIOverride;
-// if (PlatformDetector.isWin7()) {
-// desktopDPIOverride = 0;
-// } else {
-// // Win 8 or later.
-// desktopDPIOverride = RegistryUtil.getRegistryIntValue(
-// RegistryUtil.HKEY_CURRENT_USER,
-// "Control Panel\\Desktop",
-// "DesktopDPIOverride");
-// if(desktopDPIOverride == null){
-// desktopDPIOverride = 0;
-// }
-//
-// }
-//
-//
-// if (winDPIScaling == 1 && desktopDPIOverride == 0){
-// // There is scaling, but on override (magnifying glass).
-// Integer logPixels = RegistryUtil.getRegistryIntValue(
-// RegistryUtil.HKEY_CURRENT_USER,
-// "Control Panel\\Desktop",
-// "LogPixels");
-//
-// if (logPixels != null && logPixels != WIN_DEFAULT_DPI){
-// this.scalingFactor = ((float)logPixels)/WIN_DEFAULT_DPI;
-// }
-// }
-
-
- // https://msdn.microsoft.com/en-us/library/bb773352(v=vs.85).aspx
- // provide both a 16x16 pixel icon and a 32x32 icon
-
- // https://msdn.microsoft.com/en-us/library/dn742495.aspx
- // Use an icon with 16x16, 20x20, and 24x24 pixel versions. The larger versions are used in high-dpi display mode
+ TRAY_SIZE = User32.User32.GetSystemMetrics(SM_CYSMICON);
+ return TRAY_SIZE;
} else {
// reasonable default
TRAY_SIZE = 32;
@@ -281,7 +132,6 @@ class SizeAndScalingUtil {
if (OS.isWindows()) {
// http://kynosarges.org/WindowsDpi.html
-
// image-size/menu-height
// 96 DPI = 100% actual size: 14/17
// 144 DPI = 150% actual size: 24/29
@@ -289,7 +139,7 @@ class SizeAndScalingUtil {
// gets the height of the default checkmark size, adjusted
// This is the closest image size we can get to the actual size programmatically. This is a LOT closer that checking the
// largest size a JMenu image can be before the menu size changes.
- TRAY_MENU_SIZE = User32.IMPL.GetSystemMetrics(SM_CYMENUCHECK) - 1;
+ TRAY_MENU_SIZE = User32.User32.GetSystemMetrics(SM_CYMENUCHECK) - 1;
// image-size/menu-height
// 96 DPI = 100% mark size: 14/20
diff --git a/src/dorkbox/systemTray/util/Swt.java b/src/dorkbox/systemTray/util/Swt.java
deleted file mode 100644
index 1f71429..0000000
--- a/src/dorkbox/systemTray/util/Swt.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2016 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.util;
-
-import static dorkbox.systemTray.SystemTray.logger;
-
-import org.eclipse.swt.widgets.Display;
-
-/**
- * Utility methods for SWT.
- *
- * SWT system tray types are just GTK trays.
- */
-public
-class Swt {
- private static final Display currentDisplay;
- private static final Thread currentDisplayThread;
-
- static {
- // we MUST save this, otherwise it is "null" when methods are run from the swing EDT.
- currentDisplay = Display.getCurrent();
-
- currentDisplayThread = currentDisplay.getThread();
- }
-
- public static
- void init() {
- if (currentDisplay == null) {
- logger.error("Unable to get the current display for SWT. Please create an issue with your OS and Java " +
- "version so we may further investigate this issue.");
- }
- }
-
- public static
- void dispatch(final Runnable runnable) {
- currentDisplay.syncExec(runnable);
- }
-
- public static
- boolean isEventThread() {
- return Thread.currentThread() == currentDisplayThread;
- }
-
- public static
- void onShutdown(final Runnable runnable) {
- // currentDisplay.getShells() can only happen inside the event thread!
- if (isEventThread()) {
- currentDisplay.getShells()[0].addListener(org.eclipse.swt.SWT.Close, new org.eclipse.swt.widgets.Listener() {
- @Override
- public
- void handleEvent(final org.eclipse.swt.widgets.Event event) {
- runnable.run();
- }
- });
- } else {
- dispatch(new Runnable() {
- @Override
- public
- void run() {
- currentDisplay.getShells()[0].addListener(org.eclipse.swt.SWT.Close, new org.eclipse.swt.widgets.Listener() {
- @Override
- public
- void handleEvent(final org.eclipse.swt.widgets.Event event) {
- runnable.run();
- }
- });
- }
- });
- }
- }
-}
diff --git a/src/dorkbox/systemTray/util/SystemTrayFixes.java b/src/dorkbox/systemTray/util/SystemTrayFixes.java
index 7628af0..fe1b69e 100644
--- a/src/dorkbox/systemTray/util/SystemTrayFixes.java
+++ b/src/dorkbox/systemTray/util/SystemTrayFixes.java
@@ -22,7 +22,7 @@ import java.awt.AWTException;
import java.util.Locale;
import dorkbox.systemTray.SystemTray;
-import dorkbox.util.BootStrapClassLoader;
+import dorkbox.util.ClassLoaderUtil;
import dorkbox.util.OS;
import javassist.ClassPool;
import javassist.CtBehavior;
@@ -106,7 +106,7 @@ class SystemTrayFixes {
.toLowerCase(Locale.US);
// spaces at the end to make sure we check for words
- return !(vendor.contains("sun ") || vendor.contains("oracle "));
+ return vendor.contains("sun ") || vendor.contains("oracle ");
}
@@ -231,8 +231,8 @@ class SystemTrayFixes {
}
// whoosh, past the classloader and directly into memory.
- BootStrapClassLoader.defineClass(trayBytes);
- BootStrapClassLoader.defineClass(trayIconBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(trayBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(trayIconBytes);
if (SystemTray.DEBUG) {
logger.debug("Successfully changed tray icon size to: {}", trayIconSize);
@@ -412,7 +412,7 @@ class SystemTrayFixes {
mouseEventBytes = trayClass.toBytecode();
// whoosh, past the classloader and directly into memory.
- BootStrapClassLoader.defineClass(mouseEventBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(mouseEventBytes);
if (SystemTray.DEBUG) {
logger.debug("Successfully changed mouse trigger for MacOSX");
@@ -691,11 +691,11 @@ class SystemTrayFixes {
}
// whoosh, past the classloader and directly into memory.
- BootStrapClassLoader.defineClass(runnableBytes);
- BootStrapClassLoader.defineClass(eFrameBytes);
- BootStrapClassLoader.defineClass(iconCanvasBytes);
- BootStrapClassLoader.defineClass(trayIconBytes);
- BootStrapClassLoader.defineClass(trayPeerBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(runnableBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(eFrameBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(iconCanvasBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(trayIconBytes);
+ ClassLoaderUtil.Bootstrap.defineClass(trayPeerBytes);
if (SystemTray.DEBUG) {
logger.debug("Successfully changed tray icon background color");
diff --git a/src/dorkbox/systemTray/util/WindowsSwingUI.java b/src/dorkbox/systemTray/util/WindowsSwingUI.java
index f9187ed..cde539d 100644
--- a/src/dorkbox/systemTray/util/WindowsSwingUI.java
+++ b/src/dorkbox/systemTray/util/WindowsSwingUI.java
@@ -16,8 +16,12 @@
package dorkbox.systemTray.util;
import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import javax.swing.Icon;
import javax.swing.JComponent;
+import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
@@ -25,9 +29,13 @@ import javax.swing.plaf.MenuItemUI;
import javax.swing.plaf.PopupMenuUI;
import javax.swing.plaf.SeparatorUI;
+import com.sun.java.swing.plaf.windows.WindowsMenuItemUI;
+import com.sun.java.swing.plaf.windows.WindowsMenuUI;
+
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
-import dorkbox.systemTray.swingUI.SwingUIFactory;
+import dorkbox.systemTray.ui.swing.SwingUIFactory;
+import dorkbox.util.OSUtil;
import dorkbox.util.swing.DefaultMenuItemUI;
import dorkbox.util.swing.DefaultPopupMenuUI;
import dorkbox.util.swing.DefaultSeparatorUI;
@@ -46,8 +54,10 @@ import dorkbox.util.swing.DefaultSeparatorUI;
* large
* myTextField.putClientProperty("JComponent.sizeVariant", "large");
*/
+@SuppressWarnings("Duplicates")
public
class WindowsSwingUI implements SwingUIFactory {
+ private static final boolean isWindowsXP = OSUtil.Windows.isWindowsXP();
/**
* Allows one to specify the Look & Feel of the menus (The main SystemTray and sub-menus)
@@ -67,7 +77,7 @@ class WindowsSwingUI implements SwingUIFactory {
super.installUI(c);
}
};
- }
+}
/**
* Allows one to specify the Look & Feel of a menu entry
@@ -80,13 +90,88 @@ class WindowsSwingUI implements SwingUIFactory {
@Override
public
MenuItemUI getItemUI(final JMenuItem jMenuItem, final Entry entry) {
- return new DefaultMenuItemUI(jMenuItem) {
- @Override
- public
- void installUI(final JComponent c) {
- super.installUI(c);
+ if (isWindowsXP) {
+ // fix for "Swing Menus - text/icon/checkmark alignment schemes severely broken"
+ // http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4199382
+ // basically, override everything to have a 'null' checkbox, so the graphics system thinks it's not there.
+ if (jMenuItem instanceof JMenu) {
+ return new WindowsMenuUI() {
+ @Override
+ public
+ void installUI(final JComponent c) {
+ super.installUI(c);
+ }
+
+ @Override
+ protected
+ void paintMenuItem(Graphics g,
+ JComponent c,
+ Icon checkIcon,
+ Icon arrowIcon,
+ Color background,
+ Color foreground,
+ int defaultTextIconGap) {
+ super.paintMenuItem(g, c, null, arrowIcon, background, foreground, defaultTextIconGap);
+ }
+
+
+ @Override
+ public Dimension getPreferredSize(JComponent c) {
+ return getPreferredMenuItemSize(c,
+ null,
+ arrowIcon,
+ defaultTextIconGap);
+ }
+
+ @Override
+ protected
+ Dimension getPreferredMenuItemSize(final JComponent c,
+ final Icon checkIcon,
+ final Icon arrowIcon,
+ final int defaultTextIconGap) {
+ return super.getPreferredMenuItemSize(c, null, arrowIcon, defaultTextIconGap);
+ }
+ };
+ } else {
+ return new WindowsMenuItemUI() {
+ @Override
+ public
+ void installUI(final JComponent c) {
+ super.installUI(c);
+ }
+
+ @Override
+ protected
+ void paintMenuItem(Graphics g,
+ JComponent c,
+ Icon checkIcon,
+ Icon arrowIcon,
+ Color background,
+ Color foreground,
+ int defaultTextIconGap) {
+ // we don't use checkboxes, we draw our own as an image. -OFFSET is to offset insanely large margins
+ super.paintMenuItem(g, c, null, arrowIcon, background, foreground, defaultTextIconGap);
+ }
+
+ @Override
+ public Dimension getPreferredSize(JComponent c) {
+ return getPreferredMenuItemSize(c,
+ null,
+ arrowIcon,
+ defaultTextIconGap);
+ }
+ };
}
- };
+ }
+ else {
+ return new DefaultMenuItemUI(jMenuItem) {
+ @Override
+ public
+ void installUI(final JComponent c) {
+ super.installUI(c);
+ }
+ };
+ }
}
/**
diff --git a/test/dorkbox/CustomSwingUI.java b/test/dorkbox/CustomSwingUI.java
index 1aaec98..ce4fbb2 100644
--- a/test/dorkbox/CustomSwingUI.java
+++ b/test/dorkbox/CustomSwingUI.java
@@ -27,7 +27,7 @@ import javax.swing.plaf.SeparatorUI;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
-import dorkbox.systemTray.swingUI.SwingUIFactory;
+import dorkbox.systemTray.ui.swing.SwingUIFactory;
import dorkbox.systemTray.util.HeavyCheckMark;
import dorkbox.util.swing.DefaultMenuItemUI;
import dorkbox.util.swing.DefaultPopupMenuUI;
diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java
index 914a97e..4ac7123 100644
--- a/test/dorkbox/TestTray.java
+++ b/test/dorkbox/TestTray.java
@@ -18,6 +18,7 @@ package dorkbox;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.IOException;
import java.net.URL;
import dorkbox.systemTray.Checkbox;
@@ -25,6 +26,9 @@ import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
+import dorkbox.util.CacheUtil;
+import dorkbox.util.Desktop;
+import dorkbox.util.OS;
/**
* Icons from 'SJJB Icons', public domain/CC0 icon set
@@ -56,8 +60,11 @@ class TestTray {
public
TestTray() {
-// SwingUtil.setLookAndFeel(null);
-// SystemTray.SWING_UI = new CustomSwingUI();
+ SystemTray.DEBUG = true; // for test apps, we always want to run in debug mode
+ CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production.
+
+ // SwingUtil.setLookAndFeel(null); // set Native L&F (this is the System L&F instead of CrossPlatform L&F)
+ // SystemTray.SWING_UI = new CustomSwingUI();
this.systemTray = SystemTray.get();
if (systemTray == null) {
@@ -97,12 +104,14 @@ class TestTray {
entry.setCallback(callbackGray);
entry.setImage(BLACK_MAIL);
entry.setText("Delete Mail");
+ entry.setTooltip(null); // remove the tooltip
// systemTray.remove(menuEntry);
}
});
greenEntry.setImage(GREEN_MAIL);
// case does not matter
greenEntry.setShortcut('G');
+ greenEntry.setTooltip("This means you have green mail!");
mainMenu.add(greenEntry);
@@ -118,10 +127,35 @@ class TestTray {
mainMenu.add(new Separator());
+ mainMenu.add(new MenuItem("About", new ActionListener() {
+ @Override
+ public
+ void actionPerformed(final ActionEvent e) {
+ try {
+ Desktop.browseURL("https://github.com/dorkbox/SystemTray");
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }));
+
+
+ mainMenu.add(new MenuItem("Temp Directory", new ActionListener() {
+ @Override
+ public
+ void actionPerformed(final ActionEvent e) {
+ try {
+ Desktop.browseDirectory(OS.TEMP_DIR.getAbsolutePath());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }));
+
Menu submenu = new Menu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
- mainMenu.add(submenu);
+
MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() {
@Override
@@ -149,7 +183,7 @@ class TestTray {
source.getParent().remove();
}
}));
-
+ mainMenu.add(submenu);
systemTray.getMenu().add(new MenuItem("Quit", new ActionListener() {
@Override
diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java
index d61fdc6..18b510f 100644
--- a/test/dorkbox/TestTrayJavaFX.java
+++ b/test/dorkbox/TestTrayJavaFX.java
@@ -17,6 +17,7 @@
package dorkbox;
import java.awt.event.ActionListener;
+import java.io.IOException;
import java.net.URL;
import dorkbox.systemTray.Checkbox;
@@ -24,7 +25,9 @@ import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
-import dorkbox.systemTray.util.JavaFX;
+import dorkbox.util.CacheUtil;
+import dorkbox.util.Desktop;
+import dorkbox.util.JavaFX;
import dorkbox.util.OS;
import javafx.application.Application;
import javafx.application.Platform;
@@ -68,6 +71,10 @@ class TestTrayJavaFX {
@Override
public
void start(final Stage stage) throws Exception {
+ if (testTrayJavaFX == null) {
+ testTrayJavaFX = new TestTrayJavaFX();
+ }
+
testTrayJavaFX.doJavaFxStuff(stage);
}
}
@@ -113,9 +120,11 @@ class TestTrayJavaFX {
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
+ SystemTray.DEBUG = true; // for test apps, we always want to run in debug mode
+ CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production.
-// SwingUtil.setLookAndFeel(null);
-// SystemTray.SWING_UI = new CustomSwingUI();
+ // SwingUtil.setLookAndFeel(null); // set Native L&F (this is the System L&F instead of CrossPlatform L&F)
+ // SystemTray.SWING_UI = new CustomSwingUI();
this.systemTray = SystemTray.get();
if (systemTray == null) {
@@ -155,12 +164,14 @@ class TestTrayJavaFX {
entry.setCallback(callbackGray);
entry.setImage(BLACK_MAIL);
entry.setText("Delete Mail");
+ entry.setTooltip(null); // remove the tooltip
// systemTray.remove(menuEntry);
}
});
greenEntry.setImage(GREEN_MAIL);
// case does not matter
greenEntry.setShortcut('G');
+ greenEntry.setTooltip("This means you have green mail!");
mainMenu.add(greenEntry);
@@ -176,10 +187,33 @@ class TestTrayJavaFX {
mainMenu.add(new Separator());
+ mainMenu.add(new MenuItem("About", new ActionListener() {
+ @Override
+ public
+ void actionPerformed(final java.awt.event.ActionEvent e) {
+ try {
+ Desktop.browseURL("https://github.com/dorkbox/SystemTray");
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }));
+
+ mainMenu.add(new MenuItem("Temp Directory", new ActionListener() {
+ @Override
+ public
+ void actionPerformed(final java.awt.event.ActionEvent e) {
+ try {
+ Desktop.browseDirectory(OS.TEMP_DIR.getAbsolutePath());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }));
Menu submenu = new Menu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
- mainMenu.add(submenu);
+
MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() {
@Override
@@ -207,7 +241,7 @@ class TestTrayJavaFX {
source.getParent().remove();
}
}));
-
+ mainMenu.add(submenu);
systemTray.getMenu().add(new MenuItem("Quit", new ActionListener() {
@Override
diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java
index a9cd626..1371480 100644
--- a/test/dorkbox/TestTraySwt.java
+++ b/test/dorkbox/TestTraySwt.java
@@ -18,6 +18,7 @@ package dorkbox;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.IOException;
import java.net.URL;
import org.eclipse.swt.SWT;
@@ -30,6 +31,9 @@ import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
+import dorkbox.util.CacheUtil;
+import dorkbox.util.Desktop;
+import dorkbox.util.OS;
/**
* Icons from 'SJJB Icons', public domain/CC0 icon set
@@ -72,8 +76,11 @@ class TestTraySwt {
helloWorldTest.setText("Hello World SWT ................. ");
helloWorldTest.pack();
-// SwingUtil.setLookAndFeel(null);
-// SystemTray.SWING_UI = new CustomSwingUI();
+ SystemTray.DEBUG = true; // for test apps, we always want to run in debug mode
+ CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production.
+
+ // SwingUtil.setLookAndFeel(null); // set Native L&F (this is the System L&F instead of CrossPlatform L&F)
+ // SystemTray.SWING_UI = new CustomSwingUI();
this.systemTray = SystemTray.get();
if (systemTray == null) {
@@ -113,12 +120,14 @@ class TestTraySwt {
entry.setCallback(callbackGray);
entry.setImage(BLACK_MAIL);
entry.setText("Delete Mail");
+ entry.setTooltip(null); // remove the tooltip
// systemTray.remove(menuEntry);
}
});
greenEntry.setImage(GREEN_MAIL);
// case does not matter
greenEntry.setShortcut('G');
+ greenEntry.setTooltip("This means you have green mail!");
mainMenu.add(greenEntry);
@@ -134,10 +143,33 @@ class TestTraySwt {
mainMenu.add(new Separator());
+ mainMenu.add(new MenuItem("About", new ActionListener() {
+ @Override
+ public
+ void actionPerformed(final ActionEvent e) {
+ try {
+ Desktop.browseURL("https://github.com/dorkbox/SystemTray");
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }));
+
+ mainMenu.add(new MenuItem("Temp Directory", new ActionListener() {
+ @Override
+ public
+ void actionPerformed(final ActionEvent e) {
+ try {
+ Desktop.browseDirectory(OS.TEMP_DIR.getAbsolutePath());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }));
Menu submenu = new Menu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
- mainMenu.add(submenu);
+
MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() {
@Override
@@ -174,6 +206,7 @@ class TestTraySwt {
systemTray.shutdown();
// necessary to shut down SWT
display.asyncExec(new Runnable() {
+ @Override
public void run() {
shell.dispose();
}
@@ -181,7 +214,7 @@ class TestTraySwt {
//System.exit(0); not necessary if all non-daemon threads have stopped.
}
})).setShortcut('q'); // case does not matter
-
+ mainMenu.add(submenu);
shell.pack();
shell.open();
diff --git a/test/example.c b/test/example.c
index d4d9295..8dc318a 100644
--- a/test/example.c
+++ b/test/example.c
@@ -3,11 +3,11 @@
#include
-// gcc example.c `pkg-config --cflags --libs gtk+-2.0 appindicator-0.1` -I/usr/include/libappindicator-0.1/ -o example
+// gcc example.c `pkg-config --cflags --libs gtk+-2.0 appindicator-0.1` -I/usr/include/libappindicator-0.1/ -o example && ./example
-// apt libgtk-3-dev install libappindicator3-dev
+// apt-get install libgtk-3-dev libappindicator3-dev
// NOTE: there will be warnings, but the file will build and run. NOTE: this will not run as root on ubuntu (no dbus connection back to the normal user)
-// gcc example.c `pkg-config --cflags --libs gtk+-3.0 appindicator3-0.1` -I/usr/include/libappindicator3-0.1/ -o example
+// gcc example.c `pkg-config --cflags --libs gtk+-3.0 appindicator3-0.1` -I/usr/include/libappindicator3-0.1/ -o example && ./example
static void activate_action (GtkAction *action);
@@ -278,6 +278,15 @@ int main (int argc, char **argv)
menuItem1 = gtk_image_menu_item_new_with_label("menu1");
+
+ // double check color info
+ GtkStyle *style;
+ style = gtk_rc_get_style(gtk_image_menu_item_new_with_mnemonic("xxx"));
+
+ GdkColor color = style->fg[GTK_STATE_NORMAL];
+
+ fprintf(stderr, "COLOR %s\n", gdk_color_to_string(&color));
+
// g_signal_connect(menuItem1, "button_press_event", G_CALLBACK (gtkCallback), NULL);
gtk_menu_shell_insert(GTK_MENU_SHELL(indicator_menu), menuItem1, 0);
gtk_widget_show(menuItem1);