From 62d04571839a9700cde8528b3b6781851281d392 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 12 Jul 2017 01:47:36 +0200 Subject: [PATCH 01/92] Added windows implementation as WIP/example with notes. --- .../nativeUI/_WindowsNativeTray.java | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 src/dorkbox/systemTray/nativeUI/_WindowsNativeTray.java diff --git a/src/dorkbox/systemTray/nativeUI/_WindowsNativeTray.java b/src/dorkbox/systemTray/nativeUI/_WindowsNativeTray.java new file mode 100644 index 0000000..f8a9336 --- /dev/null +++ b/src/dorkbox/systemTray/nativeUI/_WindowsNativeTray.java @@ -0,0 +1,272 @@ +/* + * 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.nativeUI; + +import static com.sun.jna.platform.win32.WinDef.HWND; +import static com.sun.jna.platform.win32.WinDef.LPARAM; +import static com.sun.jna.platform.win32.WinDef.POINT; +import static com.sun.jna.platform.win32.WinDef.WPARAM; +import static com.sun.jna.platform.win32.WinUser.WM_QUIT; +import static dorkbox.util.jna.windows.Shell32.NIM_ADD; +import static dorkbox.util.jna.windows.Shell32.NIM_DELETE; +import static dorkbox.util.jna.windows.Shell32.NIM_MODIFY; +import static dorkbox.util.jna.windows.Shell32.Shell_NotifyIcon; +import static dorkbox.util.jna.windows.User32.WM_LBUTTONUP; +import static dorkbox.util.jna.windows.User32.WM_RBUTTONUP; +import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_SHELLNOTIFY; +import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_TASKBARCREATED; + +import java.io.File; + +import javax.swing.ImageIcon; + +import dorkbox.systemTray.MenuItem; +import dorkbox.systemTray.SystemTray; +import dorkbox.systemTray.Tray; +import dorkbox.util.ImageUtil; +import dorkbox.util.jna.windows.HBITMAPWrap; +import dorkbox.util.jna.windows.HICONWrap; +import dorkbox.util.jna.windows.Kernel32; +import dorkbox.util.jna.windows.Listener; +import dorkbox.util.jna.windows.Shell32; +import dorkbox.util.jna.windows.User32; +import dorkbox.util.jna.windows.WindowsEventDispatch; +import dorkbox.util.jna.windows.structs.NOTIFYICONDATA; + + +/** + * Native implementation of a System tray on Windows. + */ +class _WindowsNativeTray extends Tray implements NativeUI { + private final Listener quitListener; + private final Listener menuListener; + + // is the system tray visible or not. + private volatile boolean visible = true; + + private volatile File imageFile; + private volatile HICONWrap imageIcon; + + private volatile String tooltipText = ""; + private final Listener showListener; + + private _WindowsNativeTray(final dorkbox.systemTray.SystemTray systemTray) { + super(); + + // we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization. + + // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks + // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). + // The menu is currently AWT to allow this class to compile + final AwtMenu windowsMenu = new AwtMenu(null) { + @Override + public + void setEnabled(final MenuItem menuItem) { + boolean enabled = menuItem.getEnabled(); + + if (visible && !enabled) { + // hide + hide(); + } + else if (!visible && enabled) { + // show + show(); + } + } + + @Override + public + void setImage(final MenuItem menuItem) { + imageFile = menuItem.getImage(); + + if (imageIcon != null) { + imageIcon.close(); + } + imageIcon = convertImage(imageFile); + + NOTIFYICONDATA nid = new NOTIFYICONDATA(); + nid.hWnd = WindowsEventDispatch.get(); + nid.setIcon(imageIcon); + + if (!Shell32.Shell_NotifyIcon(NIM_MODIFY, nid)) { + SystemTray.logger.error("Error setting the image for the tray. {}", Kernel32.getLastErrorMessage()); + } + } + + @Override + public + void setText(final MenuItem menuItem) { + // no op. + } + + @Override + public + void setShortcut(final MenuItem menuItem) { + // no op + } + + @Override + public + void remove() { + hide(); + + super.remove(); + + User32.IMPL.PostMessage(WindowsEventDispatch.get(), WM_QUIT, new WPARAM(0), new LPARAM(0)); + } + }; + + // will wait until it's started up. + WindowsEventDispatch.start(); + + HWND hWnd = WindowsEventDispatch.get(); + if (hWnd == null) { + throw new RuntimeException("The Windows System Tray is not supported! Please write an issue and include your OS type and " + + "configuration"); + } + + showListener = new Listener() { + @Override + public + void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) { + show(); + } + }; + quitListener = new Listener() { + @Override + public + void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) { + WindowsEventDispatch.stop(); + WindowsEventDispatch.removeListener(showListener); + WindowsEventDispatch.removeListener(quitListener); + WindowsEventDispatch.removeListener(menuListener); + } + }; + + + menuListener = new Listener() { + final POINT mousePosition = new POINT(); + + @Override + public + void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) { + int lp = lParam.intValue(); + switch (lp) { + case WM_LBUTTONUP: + if (User32.IMPL.GetCursorPos(mousePosition)) { + // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks + // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). + // The menu is currently AWT to allow this class to compile + // windowsMenu.showContextMenu(mousePosition); + } + break; + case WM_RBUTTONUP: + if (User32.IMPL.GetCursorPos(mousePosition)) { + // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks + // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). + // The menu is currently AWT to allow this class to compile + // windowsMenu.showContextMenu(mousePosition); + } + break; + } + } + }; + + WindowsEventDispatch.addListener(WM_TASKBARCREATED, showListener); + WindowsEventDispatch.addListener(WM_QUIT, quitListener); + WindowsEventDispatch.addListener(WM_SHELLNOTIFY, menuListener); + + show(); + + bind(windowsMenu, null, systemTray); + } + +// public synchronized void balloon (String title, String message, int millis) { +// balloonNotifyIconData.hWnd = this.windowNotifyIconData.hWnd; +// balloonNotifyIconData.uID = this.windowNotifyIconData.uID; +// balloonNotifyIconData.setBalloon(title, message, millis, NIIF_NONE); +// Shell_NotifyIcon(NIM_MODIFY, balloonNotifyIconData); +// } + + + private void hide() { + if (imageIcon != null) { + imageIcon.close(); + imageIcon = null; + } + + NOTIFYICONDATA nid = new NOTIFYICONDATA(); + nid.hWnd = WindowsEventDispatch.get(); + + if (!Shell32.Shell_NotifyIcon(NIM_DELETE, nid)) { + SystemTray.logger.error("Error hiding tray. {}", Kernel32.getLastErrorMessage()); + } + visible = false; + } + + private void show() { + if (imageIcon != null) { + imageIcon.close(); + } + imageIcon = convertImage(imageFile); + + NOTIFYICONDATA nid = new NOTIFYICONDATA(); + nid.hWnd = WindowsEventDispatch.get(); + nid.setTooltip(tooltipText); + nid.setIcon(imageIcon); + nid.setCallback(WM_SHELLNOTIFY); + + if (!Shell_NotifyIcon(NIM_ADD, nid)) { + SystemTray.logger.error("Error showing tray. {}", Kernel32.getLastErrorMessage()); + } + visible = true; + } + + @Override + protected + void setTooltip_(final String tooltipText) { + if (this.tooltipText.equals(tooltipText)){ + return; + } + this.tooltipText = tooltipText; + + NOTIFYICONDATA nid = new NOTIFYICONDATA(); + nid.hWnd = WindowsEventDispatch.get(); + nid.setTooltip(tooltipText); + + Shell_NotifyIcon(NIM_MODIFY, nid); + } + + @Override + public + boolean hasImage() { + return imageFile != null; + } + + private static + HICONWrap convertImage(final File imageFile) { + if (imageFile != null) { + ImageIcon imageIcon = new ImageIcon(imageFile.getAbsolutePath()); + // fully loads the image and returns when it's done loading the image + imageIcon = new ImageIcon(imageIcon.getImage()); + + HBITMAPWrap hbitmapTrayIcon = new HBITMAPWrap(ImageUtil.getBufferedImage(imageIcon)); + return new HICONWrap(hbitmapTrayIcon); + } + + return null; + } +} From 80597bd0aa2a65dd50a24ff6488f1fca06db18d3 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 12 Jul 2017 01:59:06 +0200 Subject: [PATCH 02/92] Refactored UI class packages, now that there are no "native" vs "swing" UI implementations anymore. --- src/dorkbox/systemTray/SystemTray.java | 10 +- src/dorkbox/systemTray/nativeUI/NativeUI.java | 30 ----- src/dorkbox/systemTray/swingUI/SwingUI.java | 29 ---- .../{nativeUI => ui}/_WindowsNativeTray.java | 124 +++++++++--------- .../{nativeUI => ui/awt}/AwtMenu.java | 2 +- .../{nativeUI => ui/awt}/AwtMenuItem.java | 2 +- .../awt}/AwtMenuItemCheckbox.java | 2 +- .../awt}/AwtMenuItemSeparator.java | 2 +- .../awt}/AwtMenuItemStatus.java | 2 +- .../{nativeUI => ui/awt}/_AwtTray.java | 4 +- .../{nativeUI => ui/gtk}/GtkBaseMenuItem.java | 2 +- .../{nativeUI => ui/gtk}/GtkMenu.java | 2 +- .../{nativeUI => ui/gtk}/GtkMenuItem.java | 2 +- .../gtk}/GtkMenuItemCheckbox.java | 2 +- .../gtk}/GtkMenuItemSeparator.java | 2 +- .../gtk}/GtkMenuItemStatus.java | 2 +- .../gtk}/_AppIndicatorNativeTray.java | 4 +- .../gtk}/_GtkStatusIconNativeTray.java | 4 +- .../{swingUI => ui/swing}/SwingMenu.java | 2 +- .../{swingUI => ui/swing}/SwingMenuItem.java | 2 +- .../swing}/SwingMenuItemCheckbox.java | 2 +- .../swing}/SwingMenuItemSeparator.java | 2 +- .../swing}/SwingMenuItemStatus.java | 2 +- .../{swingUI => ui/swing}/SwingUIFactory.java | 2 +- .../{swingUI => ui/swing}/TrayPopup.java | 5 +- .../{swingUI => ui/swing}/_SwingTray.java | 4 +- src/dorkbox/systemTray/util/LinuxSwingUI.java | 2 +- .../systemTray/util/SizeAndScalingUtil.java | 2 +- .../systemTray/util/WindowsSwingUI.java | 2 +- 29 files changed, 97 insertions(+), 157 deletions(-) delete mode 100644 src/dorkbox/systemTray/nativeUI/NativeUI.java delete mode 100644 src/dorkbox/systemTray/swingUI/SwingUI.java rename src/dorkbox/systemTray/{nativeUI => ui}/_WindowsNativeTray.java (78%) rename src/dorkbox/systemTray/{nativeUI => ui/awt}/AwtMenu.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/awt}/AwtMenuItem.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/awt}/AwtMenuItemCheckbox.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/awt}/AwtMenuItemSeparator.java (97%) rename src/dorkbox/systemTray/{nativeUI => ui/awt}/AwtMenuItemStatus.java (98%) rename src/dorkbox/systemTray/{nativeUI => ui/awt}/_AwtTray.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/GtkBaseMenuItem.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/GtkMenu.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/GtkMenuItem.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/GtkMenuItemCheckbox.java (99%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/GtkMenuItemSeparator.java (98%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/GtkMenuItemStatus.java (98%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/_AppIndicatorNativeTray.java (98%) rename src/dorkbox/systemTray/{nativeUI => ui/gtk}/_GtkStatusIconNativeTray.java (98%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/SwingMenu.java (99%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/SwingMenuItem.java (99%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/SwingMenuItemCheckbox.java (99%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/SwingMenuItemSeparator.java (97%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/SwingMenuItemStatus.java (98%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/SwingUIFactory.java (98%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/TrayPopup.java (98%) rename src/dorkbox/systemTray/{swingUI => ui/swing}/_SwingTray.java (98%) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 8098349..3cbf995 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -41,11 +41,11 @@ import org.slf4j.LoggerFactory; import dorkbox.systemTray.jna.linux.AppIndicator; import dorkbox.systemTray.jna.linux.Gtk; import dorkbox.systemTray.jna.linux.GtkEventDispatch; -import dorkbox.systemTray.nativeUI._AppIndicatorNativeTray; -import dorkbox.systemTray.nativeUI._AwtTray; -import dorkbox.systemTray.nativeUI._GtkStatusIconNativeTray; -import dorkbox.systemTray.swingUI.SwingUIFactory; -import dorkbox.systemTray.swingUI._SwingTray; +import dorkbox.systemTray.ui.awt._AwtTray; +import dorkbox.systemTray.ui.gtk._AppIndicatorNativeTray; +import dorkbox.systemTray.ui.gtk._GtkStatusIconNativeTray; +import dorkbox.systemTray.ui.swing.SwingUIFactory; +import dorkbox.systemTray.ui.swing._SwingTray; import dorkbox.systemTray.util.ImageResizeUtil; import dorkbox.systemTray.util.JavaFX; import dorkbox.systemTray.util.LinuxSwingUI; 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/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/_WindowsNativeTray.java b/src/dorkbox/systemTray/ui/_WindowsNativeTray.java similarity index 78% rename from src/dorkbox/systemTray/nativeUI/_WindowsNativeTray.java rename to src/dorkbox/systemTray/ui/_WindowsNativeTray.java index f8a9336..43dfb59 100644 --- a/src/dorkbox/systemTray/nativeUI/_WindowsNativeTray.java +++ b/src/dorkbox/systemTray/ui/_WindowsNativeTray.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; import static com.sun.jna.platform.win32.WinDef.HWND; import static com.sun.jna.platform.win32.WinDef.LPARAM; @@ -33,7 +33,6 @@ import java.io.File; import javax.swing.ImageIcon; -import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.Tray; import dorkbox.util.ImageUtil; @@ -50,7 +49,7 @@ import dorkbox.util.jna.windows.structs.NOTIFYICONDATA; /** * Native implementation of a System tray on Windows. */ -class _WindowsNativeTray extends Tray implements NativeUI { +class _WindowsNativeTray extends Tray { private final Listener quitListener; private final Listener menuListener; @@ -70,64 +69,63 @@ class _WindowsNativeTray extends Tray implements NativeUI { // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). - // The menu is currently AWT to allow this class to compile - final AwtMenu windowsMenu = new AwtMenu(null) { - @Override - public - void setEnabled(final MenuItem menuItem) { - boolean enabled = menuItem.getEnabled(); - - if (visible && !enabled) { - // hide - hide(); - } - else if (!visible && enabled) { - // show - show(); - } - } - - @Override - public - void setImage(final MenuItem menuItem) { - imageFile = menuItem.getImage(); - - if (imageIcon != null) { - imageIcon.close(); - } - imageIcon = convertImage(imageFile); - - NOTIFYICONDATA nid = new NOTIFYICONDATA(); - nid.hWnd = WindowsEventDispatch.get(); - nid.setIcon(imageIcon); - - if (!Shell32.Shell_NotifyIcon(NIM_MODIFY, nid)) { - SystemTray.logger.error("Error setting the image for the tray. {}", Kernel32.getLastErrorMessage()); - } - } - - @Override - public - void setText(final MenuItem menuItem) { - // no op. - } - - @Override - public - void setShortcut(final MenuItem menuItem) { - // no op - } - - @Override - public - void remove() { - hide(); - - super.remove(); - - User32.IMPL.PostMessage(WindowsEventDispatch.get(), WM_QUIT, new WPARAM(0), new LPARAM(0)); - } - }; +// final WindowsMenu windowsMenu = new WindowsMenu() { +// @Override +// public +// void setEnabled(final MenuItem menuItem) { +// boolean enabled = menuItem.getEnabled(); +// +// if (visible && !enabled) { +// // hide +// hide(); +// } +// else if (!visible && enabled) { +// // show +// show(); +// } +// } +// +// @Override +// public +// void setImage(final MenuItem menuItem) { +// imageFile = menuItem.getImage(); +// +// if (imageIcon != null) { +// imageIcon.close(); +// } +// imageIcon = convertImage(imageFile); +// +// NOTIFYICONDATA nid = new NOTIFYICONDATA(); +// nid.hWnd = WindowsEventDispatch.get(); +// nid.setIcon(imageIcon); +// +// if (!Shell32.Shell_NotifyIcon(NIM_MODIFY, nid)) { +// SystemTray.logger.error("Error setting the image for the tray. {}", Kernel32.getLastErrorMessage()); +// } +// } +// +// @Override +// public +// void setText(final MenuItem menuItem) { +// // no op. +// } +// +// @Override +// public +// void setShortcut(final MenuItem menuItem) { +// // no op +// } +// +// @Override +// public +// void remove() { +// hide(); +// +// super.remove(); +// +// User32.IMPL.PostMessage(WindowsEventDispatch.get(), WM_QUIT, new WPARAM(0), new LPARAM(0)); +// } +// }; // will wait until it's started up. WindowsEventDispatch.start(); @@ -169,7 +167,6 @@ class _WindowsNativeTray extends Tray implements NativeUI { if (User32.IMPL.GetCursorPos(mousePosition)) { // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). - // The menu is currently AWT to allow this class to compile // windowsMenu.showContextMenu(mousePosition); } break; @@ -177,7 +174,6 @@ class _WindowsNativeTray extends Tray implements NativeUI { if (User32.IMPL.GetCursorPos(mousePosition)) { // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). - // The menu is currently AWT to allow this class to compile // windowsMenu.showContextMenu(mousePosition); } break; @@ -191,7 +187,7 @@ class _WindowsNativeTray extends Tray implements NativeUI { show(); - bind(windowsMenu, null, systemTray); +// bind(windowsMenu, null, systemTray); } // public synchronized void balloon (String title, String message, int millis) { diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenu.java b/src/dorkbox/systemTray/ui/awt/AwtMenu.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/AwtMenu.java rename to src/dorkbox/systemTray/ui/awt/AwtMenu.java index 55e5e22..d9e112a 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; diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenuItem.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/AwtMenuItem.java rename to src/dorkbox/systemTray/ui/awt/AwtMenuItem.java index b4385fc..99018c7 100644 --- a/src/dorkbox/systemTray/nativeUI/AwtMenuItem.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.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; import java.awt.event.ActionEvent; diff --git a/src/dorkbox/systemTray/nativeUI/AwtMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/AwtMenuItemCheckbox.java rename to src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java index 1d14512..fac468c 100644 --- a/src/dorkbox/systemTray/nativeUI/AwtMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.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; import java.awt.event.ActionEvent; 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 99% rename from src/dorkbox/systemTray/nativeUI/_AwtTray.java rename to src/dorkbox/systemTray/ui/awt/_AwtTray.java index ff7dabb..673e8dd 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; diff --git a/src/dorkbox/systemTray/nativeUI/GtkBaseMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/GtkBaseMenuItem.java rename to src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java index 7403c0d..71fc91a 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkBaseMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.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; diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/GtkMenu.java rename to src/dorkbox/systemTray/ui/gtk/GtkMenu.java index f911061..6f917fc 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.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.util.ArrayList; import java.util.List; diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/GtkMenuItem.java rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index 70110a1..15b894e 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.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.awt.event.ActionListener; diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java similarity index 99% rename from src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java index 1a130c9..e0fd951 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.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.awt.Color; import java.awt.Rectangle; diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemSeparator.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java similarity index 98% rename from src/dorkbox/systemTray/nativeUI/GtkMenuItemSeparator.java rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java index 1cde789..1ba1670 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.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 dorkbox.systemTray.jna.linux.Gtk; import dorkbox.systemTray.jna.linux.GtkEventDispatch; diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemStatus.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java similarity index 98% rename from src/dorkbox/systemTray/nativeUI/GtkMenuItemStatus.java rename to src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java index a85241d..d0ee8a0 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.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 dorkbox.systemTray.Status; import dorkbox.systemTray.jna.linux.Gtk; diff --git a/src/dorkbox/systemTray/nativeUI/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java similarity index 98% rename from src/dorkbox/systemTray/nativeUI/_AppIndicatorNativeTray.java rename to src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java index abf6d07..d54cecb 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; @@ -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; diff --git a/src/dorkbox/systemTray/nativeUI/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java similarity index 98% rename from src/dorkbox/systemTray/nativeUI/_GtkStatusIconNativeTray.java rename to src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java index 994da4b..04663e2 100644 --- a/src/dorkbox/systemTray/nativeUI/_GtkStatusIconNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.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; @@ -37,7 +37,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 diff --git a/src/dorkbox/systemTray/swingUI/SwingMenu.java b/src/dorkbox/systemTray/ui/swing/SwingMenu.java similarity index 99% rename from src/dorkbox/systemTray/swingUI/SwingMenu.java rename to src/dorkbox/systemTray/ui/swing/SwingMenu.java index a234ec0..6d4ece1 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; diff --git a/src/dorkbox/systemTray/swingUI/SwingMenuItem.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java similarity index 99% rename from src/dorkbox/systemTray/swingUI/SwingMenuItem.java rename to src/dorkbox/systemTray/ui/swing/SwingMenuItem.java index 75b9f8b..eac0de2 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; diff --git a/src/dorkbox/systemTray/swingUI/SwingMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java similarity index 99% rename from src/dorkbox/systemTray/swingUI/SwingMenuItemCheckbox.java rename to src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java index 848264f..c2dd068 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; 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 98% rename from src/dorkbox/systemTray/swingUI/_SwingTray.java rename to src/dorkbox/systemTray/ui/swing/_SwingTray.java index b0350bf..85b6700 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; @@ -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; diff --git a/src/dorkbox/systemTray/util/LinuxSwingUI.java b/src/dorkbox/systemTray/util/LinuxSwingUI.java index 94047ac..d8cb24f 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; diff --git a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java index e67cef7..45fcc2f 100644 --- a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java +++ b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java @@ -39,7 +39,7 @@ 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; diff --git a/src/dorkbox/systemTray/util/WindowsSwingUI.java b/src/dorkbox/systemTray/util/WindowsSwingUI.java index f9187ed..988e844 100644 --- a/src/dorkbox/systemTray/util/WindowsSwingUI.java +++ b/src/dorkbox/systemTray/util/WindowsSwingUI.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.util.swing.DefaultMenuItemUI; import dorkbox.util.swing.DefaultPopupMenuUI; import dorkbox.util.swing.DefaultSeparatorUI; From 7a4601c8ddbfe5cc7de5094b807c049e9c644b79 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 12 Jul 2017 02:25:32 +0200 Subject: [PATCH 03/92] Refactored UI class packages, now that there are no "native" vs "swing" UI implementations anymore. --- test/dorkbox/CustomSwingUI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From b49688aac751eabe67d1476e6807e7095ef7ce52 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 12 Jul 2017 02:28:02 +0200 Subject: [PATCH 04/92] Example code cleanup --- test/dorkbox/TestTray.java | 6 +++++- test/dorkbox/TestTrayJavaFX.java | 5 ++++- test/dorkbox/TestTraySwt.java | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 914a97e..c5c969a 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -25,6 +25,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; +import dorkbox.util.CacheUtil; +import dorkbox.util.SwingUtil; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -55,8 +57,10 @@ class TestTray { private ActionListener callbackGray; public + TestTray() { -// SwingUtil.setLookAndFeel(null); + CacheUtil.clear(); // for test apps, make sure the cache is always reset + 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(); diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index d61fdc6..fd0bc62 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -25,7 +25,9 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.util.JavaFX; +import dorkbox.util.CacheUtil; import dorkbox.util.OS; +import dorkbox.util.SwingUtil; import javafx.application.Application; import javafx.application.Platform; import javafx.event.ActionEvent; @@ -114,7 +116,8 @@ class TestTrayJavaFX { primaryStage.show(); -// SwingUtil.setLookAndFeel(null); + CacheUtil.clear(); // for test apps, make sure the cache is always reset + SwingUtil.setLookAndFeel(null); // SystemTray.SWING_UI = new CustomSwingUI(); this.systemTray = SystemTray.get(); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index a9cd626..ba59c47 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -30,6 +30,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; +import dorkbox.util.CacheUtil; +import dorkbox.util.SwingUtil; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -72,7 +74,8 @@ class TestTraySwt { helloWorldTest.setText("Hello World SWT ................. "); helloWorldTest.pack(); -// SwingUtil.setLookAndFeel(null); + CacheUtil.clear(); // for test apps, make sure the cache is always reset + SwingUtil.setLookAndFeel(null); // SystemTray.SWING_UI = new CustomSwingUI(); this.systemTray = SystemTray.get(); @@ -174,6 +177,7 @@ class TestTraySwt { systemTray.shutdown(); // necessary to shut down SWT display.asyncExec(new Runnable() { + @Override public void run() { shell.dispose(); } From 2ca5cdf622019f3cf11e89f53e4ee266f390ee48 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 12 Jul 2017 02:29:02 +0200 Subject: [PATCH 05/92] Windows logic now uses native methods to get the size of the tray image or system menu image heights. --- .../systemTray/util/SizeAndScalingUtil.java | 158 +----------------- 1 file changed, 4 insertions(+), 154 deletions(-) diff --git a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java index 45fcc2f..598c924 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.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.windows.User32; public @@ -52,13 +43,7 @@ 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... @@ -102,49 +87,6 @@ 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 trayType) { if (TRAY_SIZE == 0) { @@ -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.IMPL.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 From baaaa72079f6804f8dcbadcf15364a57b615806c Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:08:05 +0200 Subject: [PATCH 06/92] Fixed color struct, so now color queries match what C returned --- src/dorkbox/systemTray/jna/linux/Gtk.java | 2 +- src/dorkbox/systemTray/jna/linux/Gtk2.java | 20 +- src/dorkbox/systemTray/jna/linux/Gtk3.java | 114 --- .../systemTray/jna/linux/GtkTheme.java | 918 ++---------------- .../jna/linux/structs/GParamSpecStruct.java | 48 - .../jna/linux/structs/GdkColor.java | 2 +- .../jna/linux/structs/GdkRGBAColor.java | 68 -- .../jna/linux/structs/GtkStyle.java | 28 +- test/example.c | 13 +- 9 files changed, 142 insertions(+), 1071 deletions(-) delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GParamSpecStruct.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GdkRGBAColor.java diff --git a/src/dorkbox/systemTray/jna/linux/Gtk.java b/src/dorkbox/systemTray/jna/linux/Gtk.java index f4257ea..05faefc 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk.java @@ -541,7 +541,7 @@ class Gtk { * 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) { + GtkStyle gtk_rc_get_style(Pointer widget) { return Gtk2.gtk_rc_get_style(widget); } diff --git a/src/dorkbox/systemTray/jna/linux/Gtk2.java b/src/dorkbox/systemTray/jna/linux/Gtk2.java index e6e7db6..3d6005e 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk2.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk2.java @@ -325,7 +325,7 @@ class Gtk2 { * 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); + GtkStyle 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 @@ -365,14 +365,28 @@ class Gtk2 { * 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); + void pango_layout_get_pixel_extents(Pointer layout, Pointer ink_rect, Pointer logical_rect); + + /** + * Creates the GDK (windowing system) resources associated with a widget. For example, widget->window will be created when a widget + * is realized. Normally realization happens implicitly; if you show a widget and all its parent containers, then the widget will + * be realized and mapped automatically. + * + * Realizing a widget requires all the widget’s parent widgets to be realized; calling gtk_widget_realize() realizes the widget’s + * parents in addition to widget itself. If a widget is not yet inside a toplevel window when you realize it, bad things will happen. + * + * This function is primarily used in widget implementations, and isn’t very useful otherwise. Many times when you think you might + * need it, a better approach is to connect to a signal that will be called after the widget is realized automatically, such as + * “draw”. Or simply g_signal_connect() to the “realize” signal. + */ + public static native + void gtk_widget_realize(Pointer widget); /** * Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen. diff --git a/src/dorkbox/systemTray/jna/linux/Gtk3.java b/src/dorkbox/systemTray/jna/linux/Gtk3.java index 66e544b..404c5e8 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk3.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk3.java @@ -36,120 +36,6 @@ class Gtk3 { 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). diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java index afdd638..daf40ab 100644 --- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java +++ b/src/dorkbox/systemTray/jna/linux/GtkTheme.java @@ -15,15 +15,11 @@ */ 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; @@ -34,16 +30,9 @@ 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; @@ -63,68 +52,40 @@ import dorkbox.util.process.ShellProcessBuilder; @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", "*"}; + private static final boolean DEBUG = true; 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(); + // the following method requires an offscreen widget to get the size of text (for the checkmark size) via pango + // don't forget to destroy everything! + Pointer menu = null; + Pointer item = null; - // we use the size of "X" as the checkmark - Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text); + try { + menu = Gtk.gtk_menu_new(); + item = Gtk.gtk_image_menu_item_new_with_mnemonic(text); - Gtk.gtk_container_add(offscreen, item); + Gtk.gtk_container_add(menu, 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); + Gtk2.gtk_widget_realize(menu); + Gtk2.gtk_widget_realize(item); + Gtk2.gtk_widget_show_all(menu); - // ink pixel size is how much exact space it takes on the screen - PangoRectangle ink = new PangoRectangle(); + // get the text widget (GtkAccelLabel/GtkLabel) from inside the GtkMenuItem + Pointer textLabel = Gtk.gtk_bin_get_child(item); + Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel); - Gtk.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null); - ink.read(); + // ink pixel size is how much exact space it takes on the screen + PangoRectangle ink = new PangoRectangle(); - Rectangle size = new Rectangle(ink.width, ink.height); + Gtk.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null); + ink.read(); - 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 new Rectangle(ink.width, ink.height); + } finally { + Gtk.gtk_widget_destroy(item); + Gtk.gtk_widget_destroy(menu); + } } /** @@ -172,7 +133,7 @@ class GtkTheme { * - GtkStatusIndicator: ?? */ public static - int getIndicatorSize(final Class trayType) { + int getIndicatorSize() { // Linux is similar enough, that it just uses this method // https://wiki.archlinux.org/index.php/HiDPI @@ -181,6 +142,8 @@ class GtkTheme { final AtomicReference screenScale = new AtomicReference(); final AtomicInteger screenDPI = new AtomicInteger(); + screenScale.set(0D); + screenDPI.set(0); GtkEventDispatch.dispatchAndWait(new Runnable() { @Override @@ -196,7 +159,8 @@ class GtkTheme { if (Gtk.isGtk3) { Pointer window = Gtk.gdk_get_default_root_window(); if (window != null) { - screenScale.set((double) Gtk3.gdk_window_get_scale_factor(window)); + double scale = Gtk3.gdk_window_get_scale_factor(window); + screenScale.set(scale); } } } @@ -267,48 +231,47 @@ class GtkTheme { 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); + // sometimes the scaling-factor is set. If we have gsettings, great! otherwise try KDE + 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(); + // 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); + 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()); + 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 + // 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 + 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) { } + } catch (Throwable ignore) { } - else if (OSUtil.DesktopEnv.isKDE()) { + + if (OSUtil.DesktopEnv.isKDE()) { // check the custom KDE override file try { File customSettings = new File("/usr/bin/startkde-custom"); @@ -364,7 +327,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac if (mainFile.canRead()) { List lines = FileUtil.readLines(mainFile); boolean found = false; - int index = 0; + int index; for (final String line : lines) { if (line.contains("")) { found = true; @@ -416,7 +379,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac } } else { - if (OSUtil.Linux.isUbuntu() && env == OSUtil.DesktopEnv.Env.Unity) { + if (OSUtil.Linux.isUbuntu() && env == OSUtil.DesktopEnv.Env.Unity || env == OSUtil.DesktopEnv.Env.Unity7) { // 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... @@ -511,145 +474,42 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac */ 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; + Color c = null; + + // the following method requires an offscreen widget to get the style information from. + // don't forget to destroy everything! + Pointer menu = null; + Pointer item = null; + + try { + menu = Gtk.gtk_menu_new(); + item = Gtk.gtk_image_menu_item_new_with_mnemonic("a"); + + Gtk.gtk_container_add(menu, item); + + Gtk2.gtk_widget_realize(menu); + Gtk2.gtk_widget_realize(item); + Gtk2.gtk_widget_show_all(menu); + + + GtkStyle style = Gtk.gtk_rc_get_style(item); + style.read(); + + // this is the same color chromium uses (fg) + // https://chromium.googlesource.com/chromium/src/+/b3ca230ddd7d1238ee96ed26ea23e369f10dd655/chrome/browser/ui/libgtk2ui/gtk2_ui.cc#873 + c = style.fg[Gtk.State.NORMAL].getColor(); - // 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); + } finally { Gtk.gtk_widget_destroy(item); - return; + Gtk.gtk_widget_destroy(menu); } - - - - 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); } }); @@ -669,604 +529,4 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac // 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/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/GdkColor.java b/src/dorkbox/systemTray/jna/linux/structs/GdkColor.java index c22a18a..000e356 100644 --- a/src/dorkbox/systemTray/jna/linux/structs/GdkColor.java +++ b/src/dorkbox/systemTray/jna/linux/structs/GdkColor.java @@ -42,7 +42,7 @@ class GdkColor extends Structure { public short blue; /** - * Convert to positive int (value between 0 and 65535, these are 16 bits per pixel) that is from 0-255 + * Convert from positive int (value between 0 and 65535, these are 16 bits per pixel) to values from 0-255 */ private static int convert(int inputColor) { return (inputColor & 0x0000FFFF >> 8) & 0xFF; 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/GtkStyle.java b/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java index b0b99c6..70e8420 100644 --- a/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java +++ b/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java @@ -46,18 +46,22 @@ class GtkStyle extends Structure { */ public static - class ByReference extends GtkStyle implements Structure.ByReference {} + class ByReference extends GtkStyle implements Structure.ByReference { + } public - class ByValue extends GtkStyle implements Structure.ByValue {} + class ByValue extends GtkStyle implements Structure.ByValue { + } - // only list public fields + // required, even though it's "private" in the corresponding C code. OTHERWISE the memory offsets are INCORRECT. + public GObjectStruct parent_instance; /** 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]; @@ -69,7 +73,10 @@ class GtkStyle extends Structure { /** 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 */ + + /** Halfway between text/base */ + public GdkColor text_aa[] = new GdkColor[5]; + public GdkColor black; public GdkColor white; public Pointer /*PangoFontDescription*/ font_desc; @@ -77,10 +84,21 @@ class GtkStyle extends Structure { public int ythickness; public Pointer /*cairo_pattern_t*/ background[] = new Pointer[5]; + public + void debug(final int gtkState) { + System.err.println("base " + base[gtkState].getColor()); + System.err.println("text " + text[gtkState].getColor()); + System.err.println("text_aa " + text_aa[gtkState].getColor()); + System.err.println("bg " + bg[gtkState].getColor()); + System.err.println("fg " + fg[gtkState].getColor()); + } + + @Override protected List getFieldOrder() { - return Arrays.asList("fg", + return Arrays.asList("parent_instance", + "fg", "bg", "light", "dark", diff --git a/test/example.c b/test/example.c index d4d9295..3b01c42 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 // 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); From 30832aef9ea932b9a80a305dda53a3d65bd2c891 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:09:16 +0200 Subject: [PATCH 07/92] Code polish, removed CSS parser which is no longer needed. --- src/dorkbox/systemTray/util/CssParser.java | 711 ------------------ .../systemTray/util/SizeAndScalingUtil.java | 6 +- 2 files changed, 3 insertions(+), 714 deletions(-) delete mode 100644 src/dorkbox/systemTray/util/CssParser.java 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/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java index 598c924..1898014 100644 --- a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java +++ b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java @@ -45,7 +45,7 @@ class SizeAndScalingUtil { 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) { @@ -88,10 +88,10 @@ class SizeAndScalingUtil { public static - int getTrayImageSize(final Class 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. From 1e0531e74060f795f70a1bdd8cbe386185c848b1 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:14:35 +0200 Subject: [PATCH 08/92] Turned off debug --- src/dorkbox/systemTray/jna/linux/GtkTheme.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java index daf40ab..55090ed 100644 --- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java +++ b/src/dorkbox/systemTray/jna/linux/GtkTheme.java @@ -52,7 +52,7 @@ import dorkbox.util.process.ShellProcessBuilder; @SuppressWarnings({"deprecation", "WeakerAccess"}) public class GtkTheme { - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; public static Rectangle getPixelTextHeight(String text) { @@ -497,7 +497,6 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac Gtk2.gtk_widget_realize(item); Gtk2.gtk_widget_show_all(menu); - GtkStyle style = Gtk.gtk_rc_get_style(item); style.read(); From 99ec7e73a5449781bfeecb1c0c03b3d202e32745 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:17:34 +0200 Subject: [PATCH 09/92] Code polish --- src/dorkbox/systemTray/jna/linux/Gobject.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dorkbox/systemTray/jna/linux/Gobject.java b/src/dorkbox/systemTray/jna/linux/Gobject.java index 5d0ed43..d4236af 100644 --- a/src/dorkbox/systemTray/jna/linux/Gobject.java +++ b/src/dorkbox/systemTray/jna/linux/Gobject.java @@ -62,16 +62,14 @@ class Gobject { 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 + * 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. */ 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); - } From 14f5db0b57e8bda6c19c78cbe4b0dc1cf1513a36 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:19:18 +0200 Subject: [PATCH 10/92] Comment polish --- src/dorkbox/systemTray/jna/linux/GtkTheme.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java index 55090ed..181e355 100644 --- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java +++ b/src/dorkbox/systemTray/jna/linux/GtkTheme.java @@ -40,14 +40,7 @@ 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!!! + * Class to contain all of the various methods needed to get information set by a GTK theme. */ @SuppressWarnings({"deprecation", "WeakerAccess"}) public From c9f6b3f044f8416ceb168e588dd963aa1b119291 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:45:15 +0200 Subject: [PATCH 11/92] Added detection for Ubuntu 17.04. Of note, MIR/Wayland is not supported by java (Java uses the XServer). --- src/dorkbox/systemTray/SystemTray.java | 52 +++++++++++++++----------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 3cbf995..fc08d8d 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -318,6 +318,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 Unity 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/ @@ -621,31 +625,35 @@ class SystemTray { // fix various incompatibilities 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 (de == OSUtil.DesktopEnv.Env.Unity || de == OSUtil.DesktopEnv.Env.Unity7 && 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 (isTrayType(trayType, TrayType.AppIndicator) && OSUtil.Linux.isRoot()) { @@ -759,7 +767,7 @@ 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); @@ -1141,7 +1149,7 @@ class SystemTray { */ public int getTrayImageSize() { - return SizeAndScalingUtil.getTrayImageSize(systemTrayMenu.getClass()); + return SizeAndScalingUtil.getTrayImageSize(); } From 9fcbfd0e3cee6e4dffe7dc9e774bfed36904f2dd Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 00:46:12 +0200 Subject: [PATCH 12/92] Code polish for tests --- test/dorkbox/TestTray.java | 6 +++--- test/dorkbox/TestTrayJavaFX.java | 7 ++++--- test/dorkbox/TestTraySwt.java | 7 ++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index c5c969a..74173bd 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -57,11 +57,11 @@ class TestTray { private ActionListener callbackGray; public - TestTray() { - CacheUtil.clear(); // for test apps, make sure the cache is always reset + 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(); + // SystemTray.SWING_UI = new CustomSwingUI(); this.systemTray = SystemTray.get(); if (systemTray == null) { diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index fd0bc62..e4ca988 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -116,9 +116,10 @@ class TestTrayJavaFX { primaryStage.show(); - CacheUtil.clear(); // for test apps, make sure the cache is always reset - SwingUtil.setLookAndFeel(null); -// SystemTray.SWING_UI = new CustomSwingUI(); + 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) { diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index ba59c47..e4f14b5 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -74,9 +74,10 @@ class TestTraySwt { helloWorldTest.setText("Hello World SWT ................. "); helloWorldTest.pack(); - CacheUtil.clear(); // for test apps, make sure the cache is always reset - SwingUtil.setLookAndFeel(null); -// SystemTray.SWING_UI = new CustomSwingUI(); + 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) { From 7b172f8ed8d86dcb519947a82f25195b1ddf3009 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 01:29:03 +0200 Subject: [PATCH 13/92] Code polish for accessing User32 --- src/dorkbox/systemTray/ui/_WindowsNativeTray.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dorkbox/systemTray/ui/_WindowsNativeTray.java b/src/dorkbox/systemTray/ui/_WindowsNativeTray.java index 43dfb59..c617acd 100644 --- a/src/dorkbox/systemTray/ui/_WindowsNativeTray.java +++ b/src/dorkbox/systemTray/ui/_WindowsNativeTray.java @@ -24,6 +24,7 @@ import static dorkbox.util.jna.windows.Shell32.NIM_ADD; import static dorkbox.util.jna.windows.Shell32.NIM_DELETE; import static dorkbox.util.jna.windows.Shell32.NIM_MODIFY; import static dorkbox.util.jna.windows.Shell32.Shell_NotifyIcon; +import static dorkbox.util.jna.windows.User32.User32; import static dorkbox.util.jna.windows.User32.WM_LBUTTONUP; import static dorkbox.util.jna.windows.User32.WM_RBUTTONUP; import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_SHELLNOTIFY; @@ -41,7 +42,6 @@ import dorkbox.util.jna.windows.HICONWrap; import dorkbox.util.jna.windows.Kernel32; import dorkbox.util.jna.windows.Listener; import dorkbox.util.jna.windows.Shell32; -import dorkbox.util.jna.windows.User32; import dorkbox.util.jna.windows.WindowsEventDispatch; import dorkbox.util.jna.windows.structs.NOTIFYICONDATA; @@ -164,14 +164,14 @@ class _WindowsNativeTray extends Tray { int lp = lParam.intValue(); switch (lp) { case WM_LBUTTONUP: - if (User32.IMPL.GetCursorPos(mousePosition)) { + if (User32.GetCursorPos(mousePosition)) { // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). // windowsMenu.showContextMenu(mousePosition); } break; case WM_RBUTTONUP: - if (User32.IMPL.GetCursorPos(mousePosition)) { + if (User32.GetCursorPos(mousePosition)) { // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). // windowsMenu.showContextMenu(mousePosition); From b42da8863374569c69bf27e4e1ff553c51d2013f Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 01:29:44 +0200 Subject: [PATCH 14/92] Code polish for accessing GTK and it's different versions --- src/dorkbox/systemTray/jna/linux/Gtk.java | 445 ++++------------- src/dorkbox/systemTray/jna/linux/Gtk2.java | 465 +++++------------- src/dorkbox/systemTray/jna/linux/Gtk3.java | 230 ++++++++- .../jna/linux/GtkEventDispatch.java | 9 +- .../systemTray/jna/linux/GtkLoader.java | 210 ++++++++ .../systemTray/jna/linux/GtkState.java | 23 + .../systemTray/jna/linux/GtkTheme.java | 53 +- .../systemTray/ui/gtk/GtkBaseMenuItem.java | 23 +- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 37 +- .../systemTray/ui/gtk/GtkMenuItem.java | 31 +- .../ui/gtk/GtkMenuItemCheckbox.java | 35 +- .../ui/gtk/GtkMenuItemSeparator.java | 7 +- .../systemTray/ui/gtk/GtkMenuItemStatus.java | 13 +- .../ui/gtk/_GtkStatusIconNativeTray.java | 25 +- .../systemTray/util/SizeAndScalingUtil.java | 4 +- 15 files changed, 772 insertions(+), 838 deletions(-) create mode 100644 src/dorkbox/systemTray/jna/linux/GtkLoader.java create mode 100644 src/dorkbox/systemTray/jna/linux/GtkState.java diff --git a/src/dorkbox/systemTray/jna/linux/Gtk.java b/src/dorkbox/systemTray/jna/linux/Gtk.java index 05faefc..03cbbf0 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk.java @@ -15,16 +15,10 @@ */ 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 @@ -33,7 +27,7 @@ import dorkbox.util.jna.JnaHelper; */ @SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed", "WeakerAccess"}) public -class Gtk { +interface 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 @@ -41,231 +35,73 @@ class 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; - } + // make specific note of GTK2 vs GTK3 APIs + Gtk Gtk2 = GtkLoader.isGtk2 ? new Gtk2() : new Gtk3(); + Gtk3 Gtk3 = GtkLoader.isGtk2 ? null : (Gtk3) Gtk2; - 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) - } + boolean isGtk2 = GtkLoader.isGtk2; + boolean isGtk3 = GtkLoader.isGtk3; + boolean isLoaded = GtkLoader.isLoaded; + Function gtk_status_icon_position_menu = GtkLoader.gtk_status_icon_position_menu; - // 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; + int FALSE = 0; + int TRUE = 1; + int MAJOR = GtkLoader.MAJOR; + int MINOR = GtkLoader.MINOR; + int MICRO = GtkLoader.MICRO; - 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. + /** + * 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 { - boolean shouldUseGtk2 = SystemTray.FORCE_GTK2; - boolean _isGtk2 = false; - boolean _isLoaded = false; - boolean _alreadyRunningGTK = false; - int major = 0; - int minor = 0; - int micro = 0; + int gdk_threads_add_idle_full(int priority, FuncCallback function, Pointer data, Pointer notify); - boolean shouldLoadGtk = !(OS.isWindows() || OS.isMacOsX()); - if (!shouldLoadGtk) { - _isLoaded = true; - } + /** + * 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. + */ + boolean gtk_init_check(int argc); - // 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... + /** + * 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. + */ + void gtk_main(); - // in some cases, we ALWAYS want to try GTK2 first - String gtk2LibName = "gtk-x11-2.0"; - String gtk3LibName = "libgtk-3.so.0"; + /** + * aks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already running + */ + 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! + */ + void gtk_main_quit(); - 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(); - } + Pointer 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); - } + void gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu); /** * Creates a new GtkSeparatorMenuItem. */ - public static - Pointer gtk_separator_menu_item_new() { - return Gtk2.gtk_separator_menu_item_new(); - } + 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 @@ -273,18 +109,12 @@ class Gtk { *

* 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); - } + Pointer gtk_image_new_from_file(String 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); - } + 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 @@ -295,15 +125,9 @@ class Gtk { * 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); - } + Pointer gtk_image_menu_item_new_with_mnemonic(String label); - public static - Pointer gtk_check_menu_item_new_with_mnemonic(String label) { - return Gtk2.gtk_check_menu_item_new_with_mnemonic(label); - } + 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 @@ -311,10 +135,7 @@ class Gtk { *

* 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); - } + 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. @@ -322,10 +143,7 @@ class Gtk { *

* 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); - } + void gtk_image_menu_item_set_always_show_image(Pointer menu_item, boolean forceShow); /** * Creates an empty status icon object. @@ -333,20 +151,14 @@ class Gtk { * 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(); - } + 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 - Pointer gdk_get_default_root_window() { - return Gtk2.gdk_get_default_root_window(); - } + Pointer gdk_get_default_root_window(); /** * Gets the default screen for the default display. (See gdk_display_get_default()). @@ -355,10 +167,7 @@ class Gtk { * * @since 2.2 */ - public static - Pointer gdk_screen_get_default() { - return Gtk2.gdk_screen_get_default(); - } + Pointer gdk_screen_get_default(); /** * Gets the resolution for font handling on the screen; see gdk_screen_set_resolution() for full details. @@ -372,10 +181,7 @@ class Gtk { * * @since Since: 2.10 */ - public static - double gdk_screen_get_resolution(Pointer screen) { - return Gtk2.gdk_screen_get_resolution(screen); - } + double gdk_screen_get_resolution(Pointer screen); /** * Makes status_icon display the file filename . See gtk_status_icon_new_from_file() for details. @@ -383,10 +189,7 @@ class Gtk { * 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); - } + void gtk_status_icon_set_from_file(Pointer widget, String label); /** * Shows or hides a status icon. @@ -394,10 +197,7 @@ class Gtk { * 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); - } + void gtk_status_icon_set_visible(Pointer widget, boolean visible); /** @@ -409,10 +209,7 @@ class Gtk { * 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); - } + 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 @@ -421,10 +218,7 @@ class Gtk { * 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); - } + 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 @@ -433,10 +227,7 @@ class Gtk { * 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); - } + void gtk_status_icon_set_name(Pointer widget, String name); /** * Displays a menu and makes it available for selection. @@ -444,43 +235,28 @@ class Gtk { * 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); - } + 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 - void gtk_menu_item_set_label(Pointer menu_item, String label) { - Gtk2.gtk_menu_item_set_label(menu_item, label); - } + 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 - void gtk_menu_shell_append(Pointer menu_shell, Pointer child) { - Gtk2.gtk_menu_shell_append(menu_shell, child); - } + 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 - void gtk_widget_set_sensitive(Pointer widget, boolean sensitive) { - Gtk2.gtk_widget_set_sensitive(widget, sensitive); - } + 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 - void gtk_widget_show_all(Pointer widget) { - Gtk2.gtk_widget_show_all(widget); - } + 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 @@ -490,10 +266,7 @@ class Gtk { * 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); - } + void gtk_container_remove(Pointer parentWidget, Pointer widget); /** * Destroys a widget. @@ -513,48 +286,20 @@ class Gtk { *

* 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); - } + void gtk_widget_destroy(Pointer 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); - } + Pointer gtk_settings_get_for_screen(Pointer screen); /** * 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 - GtkStyle 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); - } + GtkStyle gtk_rc_get_style(Pointer widget); /** * Adds widget to container . Typically used for simple containers such as GtkWindow, GtkFrame, or GtkButton; for more complicated @@ -562,18 +307,12 @@ class Gtk { * 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); - } + void gtk_container_add(Pointer offscreen, Pointer 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); - } + 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 @@ -581,10 +320,7 @@ class Gtk { * * 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); - } + 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 @@ -595,20 +331,28 @@ class Gtk { * @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); - } + void pango_layout_get_pixel_extents(Pointer layout, Pointer ink_rect, Pointer logical_rect); + + /** + * Creates the GDK (windowing system) resources associated with a widget. For example, widget->window will be created when a widget + * is realized. Normally realization happens implicitly; if you show a widget and all its parent containers, then the widget will + * be realized and mapped automatically. + * + * Realizing a widget requires all the widget’s parent widgets to be realized; calling gtk_widget_realize() realizes the widget’s + * parents in addition to widget itself. If a widget is not yet inside a toplevel window when you realize it, bad things will happen. + * + * This function is primarily used in widget implementations, and isn’t very useful otherwise. Many times when you think you might + * need it, a better approach is to connect to a signal that will be called after the widget is realized automatically, such as + * “draw”. Or simply g_signal_connect() to the “realize” signal. + */ + void gtk_widget_realize(Pointer widget); /** * 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(); - } + Pointer gtk_offscreen_window_new(); /** * This function is typically used when implementing a GtkContainer subclass. Obtains the preferred size of a widget. The @@ -620,15 +364,7 @@ class Gtk { * * 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); - } - } + 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 @@ -639,9 +375,6 @@ class Gtk { * * @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); - } + Pointer gtk_image_menu_item_new_from_stock(String stock_id, Pointer accel_group); } diff --git a/src/dorkbox/systemTray/jna/linux/Gtk2.java b/src/dorkbox/systemTray/jna/linux/Gtk2.java index 3d6005e..8c623a2 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk2.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk2.java @@ -22,403 +22,176 @@ 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 { +class Gtk2 implements Gtk { // objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk + @Override + public native + int gdk_threads_add_idle_full(final int priority, final FuncCallback function, final Pointer data, final Pointer notify); - /** - * 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); + @Override + public native + boolean gtk_init_check(final int argc); - /** - * 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 + @Override + public 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 + @Override + public 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 + @Override + public native void gtk_main_quit(); - - - - /** - * Creates a new GtkMenu - */ - public static native + @Override + public 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); + @Override + public native + void gtk_menu_item_set_submenu(final Pointer menuEntry, final Pointer menu); - /** - * Creates a new GtkSeparatorMenuItem. - */ - public static native + @Override + public 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); + @Override + public native + Pointer gtk_image_new_from_file(final 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); + @Override + public native + void gtk_check_menu_item_set_active(final Pointer check_menu_item, final 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); + @Override + public native + Pointer gtk_image_menu_item_new_with_mnemonic(final String label); - public static native - Pointer gtk_check_menu_item_new_with_mnemonic(String label); + @Override + public native + Pointer gtk_check_menu_item_new_with_mnemonic(final 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); + @Override + public native + void gtk_image_menu_item_set_image(final Pointer image_menu_item, final 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); + @Override + public native + void gtk_image_menu_item_set_always_show_image(final Pointer menu_item, final 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 + @Override + public 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 + @Override + public 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 + @Override + public 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); + @Override + public native + double gdk_screen_get_resolution(final 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); + @Override + public native + void gtk_status_icon_set_from_file(final Pointer widget, final 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); + @Override + public native + void gtk_status_icon_set_visible(final Pointer widget, final boolean visible); + @Override + public native + void gtk_status_icon_set_tooltip_text(final Pointer widget, final String tooltipText); - /** - * 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); + @Override + public native + void gtk_status_icon_set_title(final Pointer widget, final String titleText); - /** - * 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); + @Override + public native + void gtk_status_icon_set_name(final Pointer widget, final String name); - /** - * 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); + @Override + public native + void gtk_menu_popup(final Pointer menu, + final Pointer widget, + final Pointer bla, + final Function func, + final Pointer data, + final int button, + final int time); - /** - * 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); + @Override + public native + void gtk_menu_item_set_label(final Pointer menu_item, final String label); - /** - * Sets text on the menu_item label - */ - public static native - void gtk_menu_item_set_label(Pointer menu_item, String label); + @Override + public native + void gtk_menu_shell_append(final Pointer menu_shell, final Pointer child); - /** - * 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); + @Override + public native + void gtk_widget_set_sensitive(final Pointer widget, final boolean sensitive); - /** - * 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); + @Override + public native + void gtk_widget_show_all(final Pointer widget); - /** - * Recursively shows a widget, and any child widgets (if the widget is a container) - */ - public static native - void gtk_widget_show_all(Pointer widget); + @Override + public native + void gtk_container_remove(final Pointer parentWidget, final 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); + @Override + public native + void gtk_widget_destroy(final 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); + @Override + public native + Pointer gtk_settings_get_for_screen(final Pointer screen); - /** - * Gets the GtkSettings object for screen , creating it if necessary. - * - * @since 2.2 - */ - public static native - Pointer gtk_settings_get_for_screen(Pointer screen); + @Override + public native + GtkStyle gtk_rc_get_style(final Pointer widget); - /** - * Simply an accessor function that returns @widget->style. - */ - public static native - GtkStyle.ByReference gtk_widget_get_style(Pointer widget); + @Override + public native + void gtk_container_add(final Pointer offscreen, final 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 - GtkStyle gtk_rc_get_style(Pointer widget); + @Override + public native + Pointer gtk_bin_get_child(final Pointer bin); - /** - * 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); + @Override + public native + Pointer gtk_label_get_layout(final Pointer label); - /** - * 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); + @Override + public native + void pango_layout_get_pixel_extents(final Pointer layout, final Pointer ink_rect, final Pointer logical_rect); - /** - * Get's the child from a GTK Bin object - */ - public static native - Pointer gtk_bin_get_child(Pointer bin); + @Override + public native + void gtk_widget_realize(final Pointer widget); - /** - * 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); + @Override + public native + Pointer gtk_offscreen_window_new(); - /** - * 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 the GDK (windowing system) resources associated with a widget. For example, widget->window will be created when a widget - * is realized. Normally realization happens implicitly; if you show a widget and all its parent containers, then the widget will - * be realized and mapped automatically. - * - * Realizing a widget requires all the widget’s parent widgets to be realized; calling gtk_widget_realize() realizes the widget’s - * parents in addition to widget itself. If a widget is not yet inside a toplevel window when you realize it, bad things will happen. - * - * This function is primarily used in widget implementations, and isn’t very useful otherwise. Many times when you think you might - * need it, a better approach is to connect to a signal that will be called after the widget is realized automatically, such as - * “draw”. Or simply g_signal_connect() to the “realize” signal. - */ - public static native - void gtk_widget_realize(Pointer widget); - - /** - * 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 + @Override + public 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); + @Override + public native + Pointer gtk_image_menu_item_new_from_stock(final String stock_id, final Pointer accel_group); } diff --git a/src/dorkbox/systemTray/jna/linux/Gtk3.java b/src/dorkbox/systemTray/jna/linux/Gtk3.java index 404c5e8..d59c4f6 100644 --- a/src/dorkbox/systemTray/jna/linux/Gtk3.java +++ b/src/dorkbox/systemTray/jna/linux/Gtk3.java @@ -15,43 +15,36 @@ */ 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+ 3. *

* Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md */ public -class Gtk3 { +class Gtk3 implements 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 - public static native - int gtk_get_major_version(); - - public static native - int gtk_get_minor_version(); - - public static native - int gtk_get_micro_version(); - /** - * 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. + * 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(). * - * @return the scale factor + * 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. * - * @since 3.10 + * Also remember that the size request is not necessarily the size a widget will actually be allocated. */ - public static native - int gdk_window_get_scale_factor(Pointer window); + @Override + public + void gtk_widget_size_request(final Pointer widget, final Pointer requisition) { + this.gtk_widget_get_preferred_size(widget, requisition, null); + } /** * Retrieves the minimum and natural size of a widget, taking into account the widget’s preference for height-for-width management. @@ -68,6 +61,197 @@ class Gtk3 { * @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 + public native void gtk_widget_get_preferred_size(final Pointer widget, final Pointer minimum_size, final Pointer natural_size); + + public native + int gtk_get_major_version(); + + public native + int gtk_get_minor_version(); + + public native + int gtk_get_micro_version(); + + /** + * 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 native + int gdk_window_get_scale_factor(Pointer window); + + + + + @Override + public native + int gdk_threads_add_idle_full(final int priority, final FuncCallback function, final Pointer data, final Pointer notify); + + @Override + public native + boolean gtk_init_check(final int argc); + + @Override + public native + void gtk_main(); + + @Override + public native + int gtk_main_level(); + + @Override + public native + void gtk_main_quit(); + + @Override + public native + Pointer gtk_menu_new(); + + @Override + public native + void gtk_menu_item_set_submenu(final Pointer menuEntry, final Pointer menu); + + @Override + public native + Pointer gtk_separator_menu_item_new(); + + @Override + public native + Pointer gtk_image_new_from_file(final String iconPath); + + @Override + public native + void gtk_check_menu_item_set_active(final Pointer check_menu_item, final boolean isChecked); + + @Override + public native + Pointer gtk_image_menu_item_new_with_mnemonic(final String label); + + @Override + public native + Pointer gtk_check_menu_item_new_with_mnemonic(final String label); + + @Override + public native + void gtk_image_menu_item_set_image(final Pointer image_menu_item, final Pointer image); + + @Override + public native + void gtk_image_menu_item_set_always_show_image(final Pointer menu_item, final boolean forceShow); + + @Override + public native + Pointer gtk_status_icon_new(); + + @Override + public native + Pointer gdk_get_default_root_window(); + + @Override + public native + Pointer gdk_screen_get_default(); + + @Override + public native + double gdk_screen_get_resolution(final Pointer screen); + + @Override + public native + void gtk_status_icon_set_from_file(final Pointer widget, final String label); + + @Override + public native + void gtk_status_icon_set_visible(final Pointer widget, final boolean visible); + + @Override + public native + void gtk_status_icon_set_tooltip_text(final Pointer widget, final String tooltipText); + + @Override + public native + void gtk_status_icon_set_title(final Pointer widget, final String titleText); + + @Override + public native + void gtk_status_icon_set_name(final Pointer widget, final String name); + + @Override + public native + void gtk_menu_popup(final Pointer menu, + final Pointer widget, + final Pointer bla, + final Function func, + final Pointer data, + final int button, + final int time); + + @Override + public native + void gtk_menu_item_set_label(final Pointer menu_item, final String label); + + @Override + public native + void gtk_menu_shell_append(final Pointer menu_shell, final Pointer child); + + @Override + public native + void gtk_widget_set_sensitive(final Pointer widget, final boolean sensitive); + + @Override + public native + void gtk_widget_show_all(final Pointer widget); + + @Override + public native + void gtk_container_remove(final Pointer parentWidget, final Pointer widget); + + @Override + public native + void gtk_widget_destroy(final Pointer widget); + + @Override + public native + Pointer gtk_settings_get_for_screen(final Pointer screen); + + @Override + public native + GtkStyle gtk_rc_get_style(final Pointer widget); + + @Override + public native + void gtk_container_add(final Pointer offscreen, final Pointer widget); + + @Override + public native + Pointer gtk_bin_get_child(final Pointer bin); + + @Override + public native + Pointer gtk_label_get_layout(final Pointer label); + + @Override + public native + void pango_layout_get_pixel_extents(final Pointer layout, final Pointer ink_rect, final Pointer logical_rect); + + @Override + public native + void gtk_widget_realize(final Pointer widget); + + @Override + public native + Pointer gtk_offscreen_window_new(); + + @Override + public native + Pointer gtk_image_menu_item_new_from_stock(final String stock_id, final Pointer accel_group); } diff --git a/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java b/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java index e728d27..fd1de0d 100644 --- a/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java +++ b/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java @@ -16,6 +16,7 @@ package dorkbox.systemTray.jna.linux; import static dorkbox.systemTray.SystemTray.logger; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -65,7 +66,7 @@ class GtkEventDispatch { // startup the GTK GUI event loop. There can be multiple/nested loops. - if (!Gtk.alreadyRunningGTK) { + if (!GtkLoader.alreadyRunningGTK) { // If JavaFX/SWT is used, this is UNNECESSARY (we can detect if the GTK main_loop is running) gtkUpdateThread = new Thread() { @@ -207,7 +208,7 @@ class GtkEventDispatch { */ public static void dispatch(final Runnable runnable) { - if (Gtk.alreadyRunningGTK) { + if (GtkLoader.alreadyRunningGTK) { if (SystemTray.isJavaFxLoaded) { // JavaFX only if (JavaFX.isEventThread()) { @@ -254,7 +255,7 @@ class GtkEventDispatch { isDispatch.set(false); } - return Gtk.FALSE; // don't want to call this again + return Gtk2.FALSE; // don't want to call this again } }; @@ -274,7 +275,7 @@ class GtkEventDispatch { public void run() { // If JavaFX/SWT is used, this is UNNECESSARY (and will break SWT/JavaFX shutdown) - if (!Gtk.alreadyRunningGTK) { + if (!GtkLoader.alreadyRunningGTK) { Gtk2.gtk_main_quit(); } diff --git a/src/dorkbox/systemTray/jna/linux/GtkLoader.java b/src/dorkbox/systemTray/jna/linux/GtkLoader.java new file mode 100644 index 0000000..b1b8b69 --- /dev/null +++ b/src/dorkbox/systemTray/jna/linux/GtkLoader.java @@ -0,0 +1,210 @@ +/* + * 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 static dorkbox.systemTray.jna.linux.Gtk.Gtk2; + +import com.sun.jna.Function; +import com.sun.jna.NativeLibrary; + +import dorkbox.systemTray.SystemTray; +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"}) +class GtkLoader { + // 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 + + // NOTE: AppIndicator uses this info to figure out WHAT VERSION OF appindicator to use: GTK2 -> appindicator1, GTK3 -> appindicator3 + static final boolean isGtk2; + static final boolean isGtk3; + static final boolean isLoaded; + + + static final boolean alreadyRunningGTK; + + static Function gtk_status_icon_position_menu = null; + + static final int MAJOR; + static final int MINOR; + 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; + Gtk gtk = new Gtk2(); + + // 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 = gtk.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. + JnaHelper.register(gtk3LibName, Gtk3.class); + gtk_status_icon_position_menu = Function.getFunction(gtk3LibName, "gtk_status_icon_position_menu"); + Gtk3 gtk = new Gtk3(); + + // 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 = gtk.gtk_main_level() != 0; + _isLoaded = true; + + major = gtk.gtk_get_major_version(); + minor = gtk.gtk_get_minor_version(); + micro = gtk.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."); + } + } + } +} + diff --git a/src/dorkbox/systemTray/jna/linux/GtkState.java b/src/dorkbox/systemTray/jna/linux/GtkState.java new file mode 100644 index 0000000..49a39af --- /dev/null +++ b/src/dorkbox/systemTray/jna/linux/GtkState.java @@ -0,0 +1,23 @@ +package dorkbox.systemTray.jna.linux; + +/** + * + */ +@SuppressWarnings({"unused", "PointlessBitwiseExpression"}) +public +class GtkState { + 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; +} diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java index 181e355..5e085fe 100644 --- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java +++ b/src/dorkbox/systemTray/jna/linux/GtkTheme.java @@ -15,6 +15,9 @@ */ package dorkbox.systemTray.jna.linux; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk3; + import java.awt.Color; import java.awt.Rectangle; import java.awt.Toolkit; @@ -55,29 +58,29 @@ class GtkTheme { Pointer item = null; try { - menu = Gtk.gtk_menu_new(); - item = Gtk.gtk_image_menu_item_new_with_mnemonic(text); + menu = Gtk2.gtk_menu_new(); + item = Gtk2.gtk_image_menu_item_new_with_mnemonic(text); - Gtk.gtk_container_add(menu, item); + Gtk2.gtk_container_add(menu, item); Gtk2.gtk_widget_realize(menu); Gtk2.gtk_widget_realize(item); Gtk2.gtk_widget_show_all(menu); // get the text widget (GtkAccelLabel/GtkLabel) from inside the GtkMenuItem - Pointer textLabel = Gtk.gtk_bin_get_child(item); - Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel); + Pointer textLabel = Gtk2.gtk_bin_get_child(item); + Pointer pangoLayout = Gtk2.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); + Gtk2.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null); ink.read(); return new Rectangle(ink.width, ink.height); } finally { - Gtk.gtk_widget_destroy(item); - Gtk.gtk_widget_destroy(menu); + Gtk2.gtk_widget_destroy(item); + Gtk2.gtk_widget_destroy(menu); } } @@ -92,19 +95,19 @@ class GtkTheme { @Override public void run() { - Pointer offscreen = Gtk.gtk_offscreen_window_new(); + Pointer offscreen = Gtk2.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); + Pointer item = Gtk2.gtk_image_menu_item_new_from_stock("gtk-paste", null); - Gtk.gtk_container_add(offscreen, item); + Gtk2.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()); + Gtk2.gtk_widget_size_request(imageWidget, gtkRequisition.getPointer()); gtkRequisition.read(); imageHeight.set(gtkRequisition.height); @@ -143,14 +146,14 @@ class GtkTheme { public void run() { // screen DPI - Pointer screen = Gtk.gdk_screen_get_default(); + Pointer screen = Gtk2.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)); + screenDPI.set((int) Gtk2.gdk_screen_get_resolution(screen)); } - if (Gtk.isGtk3) { - Pointer window = Gtk.gdk_get_default_root_window(); + if (Gtk2.isGtk3) { + Pointer window = Gtk2.gdk_get_default_root_window(); if (window != null) { double scale = Gtk3.gdk_window_get_scale_factor(window); screenScale.set(scale); @@ -411,11 +414,11 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac @Override public void run() { - Pointer screen = Gtk.gdk_screen_get_default(); + Pointer screen = Gtk2.gdk_screen_get_default(); Pointer settings = null; if (screen != null) { - settings = Gtk.gtk_settings_get_for_screen(screen); + settings = Gtk2.gtk_settings_get_for_screen(screen); } if (settings != null) { @@ -481,26 +484,26 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac Pointer item = null; try { - menu = Gtk.gtk_menu_new(); - item = Gtk.gtk_image_menu_item_new_with_mnemonic("a"); + menu = Gtk2.gtk_menu_new(); + item = Gtk2.gtk_image_menu_item_new_with_mnemonic("a"); - Gtk.gtk_container_add(menu, item); + Gtk2.gtk_container_add(menu, item); Gtk2.gtk_widget_realize(menu); Gtk2.gtk_widget_realize(item); Gtk2.gtk_widget_show_all(menu); - GtkStyle style = Gtk.gtk_rc_get_style(item); + GtkStyle style = Gtk2.gtk_rc_get_style(item); style.read(); // this is the same color chromium uses (fg) // https://chromium.googlesource.com/chromium/src/+/b3ca230ddd7d1238ee96ed26ea23e369f10dd655/chrome/browser/ui/libgtk2ui/gtk2_ui.cc#873 - c = style.fg[Gtk.State.NORMAL].getColor(); + c = style.fg[GtkState.NORMAL].getColor(); color.set(c); } finally { - Gtk.gtk_widget_destroy(item); - Gtk.gtk_widget_destroy(menu); + Gtk2.gtk_widget_destroy(item); + Gtk2.gtk_widget_destroy(menu); } } }); diff --git a/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java index 71fc91a..e090551 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java @@ -15,12 +15,13 @@ */ package dorkbox.systemTray.ui.gtk; +import static dorkbox.systemTray.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; @@ -64,20 +65,20 @@ class GtkBaseMenuItem implements EntryPeer { } 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; - Gtk.gtk_widget_show_all(_native); + Gtk2.gtk_widget_show_all(_native); } if (everyoneElseHasImages) { - spacerImage = Gtk.gtk_image_new_from_file(transparentIcon.getAbsolutePath()); - Gtk.gtk_image_menu_item_set_image(_native, spacerImage); + 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 - 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); } // some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator. @@ -85,7 +86,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 +96,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 +109,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/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index 6f917fc..cffdbbb 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -15,6 +15,8 @@ */ package dorkbox.systemTray.ui.gtk; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -27,7 +29,6 @@ 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; @@ -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,7 +159,7 @@ 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) + Gtk2.gtk_widget_show_all(_nativeMenu); // necessary to guarantee widget is visible (doesn't always show_all for all children) onMenuAdded(_nativeMenu); } @@ -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); @@ -256,21 +257,21 @@ 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 +285,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 +324,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); } }); } @@ -379,7 +380,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/ui/gtk/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index 15b894e..9f8a2c3 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java @@ -15,6 +15,8 @@ */ package dorkbox.systemTray.ui.gtk; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; + import java.awt.event.ActionListener; import com.sun.jna.Pointer; @@ -23,7 +25,6 @@ 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; @@ -44,7 +45,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); @@ -66,7 +67,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { } } - return Gtk.TRUE; + return Gtk2.TRUE; } // NOTE: XFCE used to use appindicator3, which DOES NOT support images in the menu. This change was reverted. @@ -83,21 +84,21 @@ 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); + 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); } }); } @@ -109,7 +110,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,8 +147,8 @@ 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); } }); } @@ -174,13 +175,13 @@ 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 + Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it GtkMenuItem.super.remove(); menuItemForActionCallback = null; 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/ui/gtk/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java index e0fd951..6244cd3 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java @@ -15,6 +15,8 @@ */ package dorkbox.systemTray.ui.gtk; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; + import java.awt.Color; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -26,7 +28,6 @@ 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; @@ -91,7 +92,7 @@ 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); @@ -117,7 +118,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); } } @@ -131,7 +132,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall GtkEventDispatch.proxyClick(null, callback); } - return Gtk.TRUE; + return Gtk2.TRUE; } @Override @@ -153,7 +154,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 +186,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); } }); } @@ -240,7 +241,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 +253,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 +289,12 @@ 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 + Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it GtkMenuItemCheckbox.super.remove(); 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/ui/gtk/GtkMenuItemSeparator.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java index 1ba1670..3ae11e0 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java @@ -15,7 +15,8 @@ */ package dorkbox.systemTray.ui.gtk; -import dorkbox.systemTray.jna.linux.Gtk; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; + import dorkbox.systemTray.jna.linux.GtkEventDispatch; import dorkbox.systemTray.peer.EntryPeer; @@ -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/ui/gtk/GtkMenuItemStatus.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java index d0ee8a0..d815278 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java @@ -15,8 +15,9 @@ */ package dorkbox.systemTray.ui.gtk; +import static dorkbox.systemTray.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; @@ -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,7 +65,7 @@ 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 + Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it GtkMenuItemStatus.super.remove(); diff --git a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java index 04663e2..f502cdd 100644 --- a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java @@ -15,6 +15,8 @@ */ package dorkbox.systemTray.ui.gtk; +import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; + import java.io.File; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,7 +28,6 @@ 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; @@ -76,11 +77,11 @@ class _GtkStatusIconNativeTray extends Tray { 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 +100,11 @@ class _GtkStatusIconNativeTray extends Tray { @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); } } }); @@ -131,7 +132,7 @@ class _GtkStatusIconNativeTray extends Tray { 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 +153,7 @@ class _GtkStatusIconNativeTray extends Tray { @Override public void run() { - trayIcon = Gtk.gtk_status_icon_new(); + trayIcon = Gtk2.gtk_status_icon_new(); gtkCallback = new GEventCallback() { @Override @@ -161,8 +162,8 @@ class _GtkStatusIconNativeTray extends Tray { // 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 +183,7 @@ class _GtkStatusIconNativeTray extends Tray { // 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 @@ -193,7 +194,7 @@ class _GtkStatusIconNativeTray extends Tray { // 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); + Gtk2.gtk_status_icon_set_name(trayIcon, Extension.DEFAULT_NAME); } } }); @@ -216,7 +217,7 @@ class _GtkStatusIconNativeTray extends Tray { @Override public void run() { - Gtk.gtk_status_icon_set_tooltip_text(trayIcon, tooltipText); + Gtk2.gtk_status_icon_set_tooltip_text(trayIcon, tooltipText); } }); } diff --git a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java index 1898014..676b4c1 100644 --- a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java +++ b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java @@ -102,7 +102,7 @@ class SizeAndScalingUtil { } } else if (OS.isWindows()) { - TRAY_SIZE = User32.IMPL.GetSystemMetrics(SM_CYSMICON); + TRAY_SIZE = User32.User32.GetSystemMetrics(SM_CYSMICON); return TRAY_SIZE; } else { // reasonable default @@ -139,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 From 72b3f20d30a6e0b3889d7d84319c080a9c4609be Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 18:34:09 +0200 Subject: [PATCH 15/92] Null pointer fix when executing the javaFX application handler directly. --- test/dorkbox/TestTrayJavaFX.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index e4ca988..2f59643 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -24,10 +24,10 @@ 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.Cache; +import dorkbox.util.JavaFX; import dorkbox.util.OS; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; import javafx.application.Application; import javafx.application.Platform; import javafx.event.ActionEvent; @@ -70,6 +70,10 @@ class TestTrayJavaFX { @Override public void start(final Stage stage) throws Exception { + if (testTrayJavaFX == null) { + testTrayJavaFX = new TestTrayJavaFX(); + } + testTrayJavaFX.doJavaFxStuff(stage); } } @@ -116,9 +120,9 @@ class TestTrayJavaFX { primaryStage.show(); - CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. + Cache.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) + Swing.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(); From 3d652e2d14a23405d17359e1f2508dcfb8ddb7c2 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 18:34:55 +0200 Subject: [PATCH 16/92] Changed Util class names --- test/dorkbox/TestTray.java | 8 ++++---- test/dorkbox/TestTraySwt.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 74173bd..1544efa 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -25,8 +25,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; -import dorkbox.util.CacheUtil; -import dorkbox.util.SwingUtil; +import dorkbox.util.Cache; +import dorkbox.util.Swing; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -58,9 +58,9 @@ class TestTray { public TestTray() { - CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. + Cache.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) + Swing.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(); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index e4f14b5..0f08b09 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -30,8 +30,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; -import dorkbox.util.CacheUtil; -import dorkbox.util.SwingUtil; +import dorkbox.util.Cache; +import dorkbox.util.Swing; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -74,9 +74,9 @@ class TestTraySwt { helloWorldTest.setText("Hello World SWT ................. "); helloWorldTest.pack(); - CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. + Cache.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) + Swing.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(); From 839d264d31079ed4b5d2f07f7bc59899e499350c Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 18:35:38 +0200 Subject: [PATCH 17/92] Changed Util class names --- src/dorkbox/systemTray/Checkbox.java | 4 +- src/dorkbox/systemTray/MenuItem.java | 4 +- src/dorkbox/systemTray/SystemTray.java | 123 ++++++---------- src/dorkbox/systemTray/ui/awt/AwtMenu.java | 14 +- .../systemTray/ui/awt/AwtMenuItem.java | 12 +- .../ui/awt/AwtMenuItemCheckbox.java | 14 +- .../ui/awt/AwtMenuItemSeparator.java | 4 +- .../systemTray/ui/awt/AwtMenuItemStatus.java | 6 +- src/dorkbox/systemTray/ui/awt/_AwtTray.java | 10 +- .../systemTray/ui/gtk/GtkBaseMenuItem.java | 6 +- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 4 +- .../systemTray/ui/gtk/GtkMenuItem.java | 8 +- .../ui/gtk/GtkMenuItemCheckbox.java | 20 ++- .../ui/gtk/GtkMenuItemSeparator.java | 4 +- .../systemTray/ui/gtk/GtkMenuItemStatus.java | 4 +- .../ui/gtk/_AppIndicatorNativeTray.java | 8 +- .../ui/gtk/_GtkStatusIconNativeTray.java | 13 +- .../systemTray/ui/swing/SwingMenu.java | 16 +-- .../systemTray/ui/swing/SwingMenuItem.java | 14 +- .../ui/swing/SwingMenuItemCheckbox.java | 16 +-- .../ui/swing/SwingMenuItemSeparator.java | 4 +- .../ui/swing/SwingMenuItemStatus.java | 6 +- .../systemTray/ui/swing/_SwingTray.java | 12 +- .../systemTray/util/HeavyCheckMark.java | 4 +- .../systemTray/util/ImageResizeUtil.java | 28 ++-- src/dorkbox/systemTray/util/JavaFX.java | 135 ------------------ .../systemTray/util/SizeAndScalingUtil.java | 8 +- src/dorkbox/systemTray/util/Swt.java | 84 ----------- 28 files changed, 167 insertions(+), 418 deletions(-) delete mode 100644 src/dorkbox/systemTray/util/JavaFX.java delete mode 100644 src/dorkbox/systemTray/util/Swt.java diff --git a/src/dorkbox/systemTray/Checkbox.java b/src/dorkbox/systemTray/Checkbox.java index b366366..14b5b47 100644 --- a/src/dorkbox/systemTray/Checkbox.java +++ b/src/dorkbox/systemTray/Checkbox.java @@ -18,7 +18,7 @@ package dorkbox.systemTray; import java.awt.event.ActionListener; import dorkbox.systemTray.peer.CheckboxPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; /** * This represents a common menu-checkbox entry, that is cross platform in nature @@ -189,7 +189,7 @@ class Checkbox extends Entry { */ public void setShortcut(final int key) { - this.mnemonicKey = SwingUtil.getFromVirtualKey(key); + this.mnemonicKey = Swing.getFromVirtualKey(key); if (peer != null) { ((CheckboxPeer) peer).setShortcut(this); diff --git a/src/dorkbox/systemTray/MenuItem.java b/src/dorkbox/systemTray/MenuItem.java index a0bc7be..1ca6caa 100644 --- a/src/dorkbox/systemTray/MenuItem.java +++ b/src/dorkbox/systemTray/MenuItem.java @@ -25,7 +25,7 @@ import javax.imageio.stream.ImageInputStream; import dorkbox.systemTray.peer.MenuItemPeer; import dorkbox.systemTray.util.ImageResizeUtil; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; /** * This represents a common menu-entry, that is cross platform in nature @@ -336,7 +336,7 @@ class MenuItem extends Entry { */ public void setShortcut(final int key) { - this.mnemonicKey = SwingUtil.getFromVirtualKey(key); + this.mnemonicKey = Swing.getFromVirtualKey(key); if (peer != null) { ((MenuItemPeer) peer).setShortcut(this); diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index fc08d8d..efc5795 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -38,27 +38,27 @@ import javax.swing.UIManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dorkbox.systemTray.jna.linux.AppIndicator; -import dorkbox.systemTray.jna.linux.Gtk; -import dorkbox.systemTray.jna.linux.GtkEventDispatch; import dorkbox.systemTray.ui.awt._AwtTray; import dorkbox.systemTray.ui.gtk._AppIndicatorNativeTray; import dorkbox.systemTray.ui.gtk._GtkStatusIconNativeTray; import dorkbox.systemTray.ui.swing.SwingUIFactory; import dorkbox.systemTray.ui.swing._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.Cache; +import dorkbox.util.Framework; 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 dorkbox.util.Swing; +import dorkbox.util.jna.linux.AppIndicator; +import dorkbox.util.jna.linux.Gtk; +import dorkbox.util.jna.linux.GtkEventDispatch; import sun.security.action.GetPropertyAction; @@ -140,35 +140,6 @@ class SystemTray { 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 tray, final TrayType trayType) { @@ -319,7 +290,7 @@ class SystemTray { return selectTypeQuietly(TrayType.AppIndicator); } case Unity7: { - // Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell". + // Ubuntu Unity7 is a weird combination. It's "Gnome", but it's not "Gnome Shell". return selectTypeQuietly(TrayType.AppIndicator); } case XFCE: { @@ -439,7 +410,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 (Framework.isJavaFxLoaded && 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" + @@ -457,7 +428,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 (Framework.isSwtLoaded && 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."); @@ -485,7 +456,7 @@ class SystemTray { // 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) { + if (!FORCE_GTK2 && !Framework.isJavaFxLoaded && !Framework.isSwtLoaded) { String currentUI = UIManager.getLookAndFeel() .getClass() @@ -522,20 +493,11 @@ class SystemTray { } } } - else if (isSwtLoaded) { + else if (Framework.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"); - // 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 (isSwt_GTK3 && FORCE_GTK2) { + if (Framework.isSwtGtk3 && 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;`"); @@ -543,21 +505,21 @@ class SystemTray { systemTrayMenu = null; systemTray = null; return; - } else if (!isSwt_GTK3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { + } else if (!Framework.isSwtGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { // we must use GTK2, because SWT is GTK2 FORCE_GTK2 = true; logger.warn("Forcing GTK2 because SWT is GTK2"); } } - else if (isJavaFxLoaded) { + else if (Framework.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 (Framework.isJavaFxGtk3 && FORCE_GTK2) { // if we are java9, then we can change it -- otherwise we cannot. if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) { FORCE_GTK2 = false; @@ -574,7 +536,7 @@ class SystemTray { systemTray = null; return; } - } else if (!isJava_GTK3_Possible && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { + } else if (!Framework.isJavaFxGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { // we must use GTK2, because JavaFX is GTK2 FORCE_GTK2 = true; @@ -596,8 +558,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? {}", Framework.isJavaFxLoaded); + logger.debug("Is SWT detected? {}", Framework.isSwtLoaded); logger.debug("Java Swing L&F: {}", UIManager.getLookAndFeel().getID()); if (FORCE_TRAY_TYPE == TrayType.AutoDetect) { logger.debug("Auto-detecting tray type"); @@ -683,13 +645,27 @@ class SystemTray { // - appIndicator/gtk require strings (which is the path) // - swing version loads as an image (which can be stream or path, we use path) - CacheUtil.tempDir = "SystemTrayImages"; + Cache.tempDir = "SystemTrayImages"; + + // This will initialize javaFX/SWT event dispatch methods + Framework.initDispatch(); + 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) { + // linux/unix need access to GTK, so load it up before the tray is loaded! + GtkEventDispatch.startGui(FORCE_GTK2, DEBUG); + GtkEventDispatch.waitForEventsToComplete(); + + 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); + } + // NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3. // appindicator3 doesn't support menu icons via GTK2!! if (!Gtk.isLoaded) { @@ -739,24 +715,9 @@ class SystemTray { - 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 && Swing.isDefaultLookAndFeel()) { if (isNix) { SystemTray.SWING_UI = new LinuxSwingUI(); } @@ -792,7 +753,7 @@ class SystemTray { - if ((isJavaFxLoaded || isSwtLoaded) && SwingUtilities.isEventDispatchThread()) { + if ((Framework.isJavaFxLoaded || Framework.isSwtLoaded) && 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."); @@ -807,7 +768,7 @@ class SystemTray { // linux + GTK/AppIndicator menus must not start on the EDT! // AWT/Swing must be constructed on the EDT however... - if (isJavaFxLoaded || isSwtLoaded || + if (Framework.isJavaFxLoaded || Framework.isSwtLoaded || (isNix && (isTrayType(trayType, TrayType.GtkStatusIcon) || isTrayType(trayType, TrayType.AppIndicator))) ) { try { @@ -821,7 +782,7 @@ class SystemTray { // have to construct swing stuff inside the swing EDT final Class finalTrayType = trayType; - SwingUtil.invokeAndWait(new Runnable() { + Swing.invokeAndWait(new Runnable() { @Override public void run() { @@ -850,7 +811,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 (Framework.isJavaFxLoaded) { // 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() { @@ -863,10 +824,10 @@ class SystemTray { } }); } - else if (isSwtLoaded) { + else if (Framework.isSwtLoaded) { // 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() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenu.java b/src/dorkbox/systemTray/ui/awt/AwtMenu.java index d9e112a..a07ff83 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenu.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenu.java @@ -26,7 +26,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.Status; import dorkbox.systemTray.peer.MenuPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; // this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both @SuppressWarnings("ForLoopReplaceableByForEach") @@ -53,7 +53,7 @@ class AwtMenu implements MenuPeer { public void add(final Menu parentMenu, final Entry entry, final int index) { // must always be called on the EDT - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -92,7 +92,7 @@ class AwtMenu implements MenuPeer { @Override public void setEnabled(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -105,7 +105,7 @@ class AwtMenu implements MenuPeer { @Override public void setText(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -125,9 +125,9 @@ class AwtMenu implements MenuPeer { public void setShortcut(final MenuItem menuItem) { // yikes... - final int vKey = SwingUtil.getVirtualKey(menuItem.getShortcut()); + final int vKey = Swing.getVirtualKey(menuItem.getShortcut()); - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -139,7 +139,7 @@ class AwtMenu implements MenuPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java index 99018c7..ef634ad 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java @@ -21,7 +21,7 @@ import java.awt.event.ActionListener; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.MenuItemPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class AwtMenuItem implements MenuItemPeer { @@ -45,7 +45,7 @@ class AwtMenuItem implements MenuItemPeer { @Override public void setEnabled(final dorkbox.systemTray.MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -57,7 +57,7 @@ class AwtMenuItem implements MenuItemPeer { @Override public void setText(final dorkbox.systemTray.MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -103,9 +103,9 @@ class AwtMenuItem implements MenuItemPeer { void setShortcut(final dorkbox.systemTray.MenuItem menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = SwingUtil.getVirtualKey(shortcut); + final int vKey = Swing.getVirtualKey(shortcut); - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -118,7 +118,7 @@ class AwtMenuItem implements MenuItemPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java index fac468c..17750a5 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java @@ -22,7 +22,7 @@ import java.awt.event.ActionListener; import dorkbox.systemTray.Checkbox; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.CheckboxPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class AwtMenuItemCheckbox implements CheckboxPeer { @@ -42,7 +42,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { @Override public void setEnabled(final Checkbox menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -54,7 +54,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { @Override public void setText(final Checkbox menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -102,9 +102,9 @@ class AwtMenuItemCheckbox implements CheckboxPeer { void setShortcut(final Checkbox menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = SwingUtil.getVirtualKey(shortcut); + final int vKey = Swing.getVirtualKey(shortcut); - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -122,7 +122,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { if (checked != this.isChecked) { this.isChecked = checked; - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -136,7 +136,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java index 459ccf0..7a9c385 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java @@ -17,7 +17,7 @@ package dorkbox.systemTray.ui.awt; import dorkbox.systemTray.peer.EntryPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class AwtMenuItemSeparator implements EntryPeer { @@ -34,7 +34,7 @@ class AwtMenuItemSeparator implements EntryPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java index e3ee146..c5310ee 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java @@ -22,7 +22,7 @@ import java.awt.MenuItem; import dorkbox.systemTray.Status; import dorkbox.systemTray.peer.StatusPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class AwtMenuItemStatus implements StatusPeer { @@ -39,7 +39,7 @@ class AwtMenuItemStatus implements StatusPeer { @Override public void setText(final Status menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -63,7 +63,7 @@ class AwtMenuItemStatus implements StatusPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/_AwtTray.java b/src/dorkbox/systemTray/ui/awt/_AwtTray.java index 673e8dd..fa0b52e 100644 --- a/src/dorkbox/systemTray/ui/awt/_AwtTray.java +++ b/src/dorkbox/systemTray/ui/awt/_AwtTray.java @@ -27,7 +27,7 @@ import javax.swing.ImageIcon; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Tray; import dorkbox.util.OS; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; /** * Class for handling all system tray interaction, via AWT. Pretty much EXCLUSIVELY for on MacOS, because that is the only time this @@ -69,7 +69,7 @@ class _AwtTray extends Tray { @Override public void setEnabled(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -136,7 +136,7 @@ class _AwtTray extends Tray { return; } - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -185,7 +185,7 @@ class _AwtTray extends Tray { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -217,7 +217,7 @@ class _AwtTray extends Tray { } this.tooltipText = tooltipText; - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java index e090551..9ef764c 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java @@ -15,16 +15,16 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +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.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 { diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index cffdbbb..9098756 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -15,7 +15,7 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.util.jna.linux.Gtk.Gtk2; import java.util.ArrayList; import java.util.List; @@ -29,8 +29,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.Status; -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 { diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index 9f8a2c3..8e76ec9 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java @@ -15,7 +15,7 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.util.jna.linux.Gtk.Gtk2; import java.awt.event.ActionListener; @@ -23,10 +23,10 @@ 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.GtkEventDispatch; import dorkbox.systemTray.peer.MenuItemPeer; +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; diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java index 6244cd3..1ec98e1 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java @@ -15,7 +15,7 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.util.jna.linux.Gtk.Gtk2; import java.awt.Color; import java.awt.Rectangle; @@ -26,14 +26,14 @@ 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.GtkEventDispatch; -import dorkbox.systemTray.jna.linux.GtkTheme; import dorkbox.systemTray.peer.CheckboxPeer; 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 { @@ -52,7 +52,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) { @@ -99,7 +99,13 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall 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"); diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java index 3ae11e0..104a9aa 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java @@ -15,10 +15,10 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.util.jna.linux.Gtk.Gtk2; -import dorkbox.systemTray.jna.linux.GtkEventDispatch; import dorkbox.systemTray.peer.EntryPeer; +import dorkbox.util.jna.linux.GtkEventDispatch; class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer { diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java index d815278..be359a4 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java @@ -15,11 +15,11 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.util.jna.linux.Gtk.Gtk2; import dorkbox.systemTray.Status; -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 diff --git a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java index d54cecb..920a15f 100644 --- a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java @@ -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. diff --git a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java index f502cdd..e9c4bea 100644 --- a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java @@ -15,7 +15,7 @@ */ package dorkbox.systemTray.ui.gtk; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk2; +import static dorkbox.util.jna.linux.Gtk.Gtk2; import java.io.File; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,10 +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.GtkEventDispatch; -import dorkbox.systemTray.jna.linux.structs.GdkEventButton; +import dorkbox.util.Framework; +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. @@ -193,7 +194,7 @@ class _GtkStatusIconNativeTray extends Tray { // 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) { + if (Framework.isJavaFxLoaded || Tray.usingGnome) { Gtk2.gtk_status_icon_set_name(trayIcon, Extension.DEFAULT_NAME); } } diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenu.java b/src/dorkbox/systemTray/ui/swing/SwingMenu.java index 6d4ece1..e1242d9 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenu.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenu.java @@ -30,7 +30,7 @@ import dorkbox.systemTray.Separator; import dorkbox.systemTray.Status; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.MenuPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; // this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both (and duplicate code) @SuppressWarnings("ForLoopReplaceableByForEach") @@ -69,7 +69,7 @@ class SwingMenu implements MenuPeer { public void add(final Menu parentMenu, final Entry entry, final int index) { // must always be called on the EDT - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -107,7 +107,7 @@ class SwingMenu implements MenuPeer { @Override public void setImage(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -127,7 +127,7 @@ class SwingMenu implements MenuPeer { @Override public void setEnabled(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -141,7 +141,7 @@ class SwingMenu implements MenuPeer { @Override public void setText(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -162,9 +162,9 @@ class SwingMenu implements MenuPeer { void setShortcut(final MenuItem menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = SwingUtil.getVirtualKey(shortcut); + final int vKey = Swing.getVirtualKey(shortcut); - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -179,7 +179,7 @@ class SwingMenu implements MenuPeer { @Override public synchronized void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java index eac0de2..90ff945 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java @@ -28,7 +28,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.MenuItemPeer; import dorkbox.systemTray.util.ImageResizeUtil; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class SwingMenuItem implements MenuItemPeer { @@ -78,7 +78,7 @@ class SwingMenuItem implements MenuItemPeer { @Override public void setImage(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -97,7 +97,7 @@ class SwingMenuItem implements MenuItemPeer { @Override public void setEnabled(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -109,7 +109,7 @@ class SwingMenuItem implements MenuItemPeer { @Override public void setText(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -155,9 +155,9 @@ class SwingMenuItem implements MenuItemPeer { void setShortcut(final MenuItem menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = SwingUtil.getVirtualKey(shortcut); + final int vKey = Swing.getVirtualKey(shortcut); - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -170,7 +170,7 @@ class SwingMenuItem implements MenuItemPeer { public void remove() { //noinspection Duplicates - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java index c2dd068..fb5afa9 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java @@ -26,8 +26,8 @@ import dorkbox.systemTray.Entry; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.CheckboxPeer; import dorkbox.systemTray.util.HeavyCheckMark; -import dorkbox.util.FontUtil; -import dorkbox.util.SwingUtil; +import dorkbox.util.Font; +import dorkbox.util.Swing; class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { @@ -50,7 +50,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { } // Having the checkmark size the same size as the letter X is a reasonably nice size. - int size = FontUtil.getFontHeight(jMenuItem.getFont(), "X"); + int size = Font.getFontHeight(jMenuItem.getFont(), "X"); // this is the largest size of an image used in a JMenuItem, before the size of the JMenuItem is forced to be larger int menuImageSize = SystemTray.get() @@ -73,7 +73,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { @Override public void setEnabled(final Checkbox menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -85,7 +85,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { @Override public void setText(final Checkbox menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -133,9 +133,9 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { void setShortcut(final Checkbox menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = SwingUtil.getVirtualKey(shortcut); + final int vKey = Swing.getVirtualKey(shortcut); - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -153,7 +153,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { if (checked != this.isChecked) { this.isChecked = checked; - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java index ebf0d0a..be9571c 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java @@ -19,7 +19,7 @@ import javax.swing.JSeparator; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.EntryPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class SwingMenuItemSeparator implements EntryPeer { @@ -40,7 +40,7 @@ class SwingMenuItemSeparator implements EntryPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java index e1bb46c..c655909 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java @@ -24,7 +24,7 @@ import dorkbox.systemTray.Entry; import dorkbox.systemTray.Status; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.StatusPeer; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; class SwingMenuItemStatus implements StatusPeer { @@ -54,7 +54,7 @@ class SwingMenuItemStatus implements StatusPeer { @Override public void setText(final Status menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -66,7 +66,7 @@ class SwingMenuItemStatus implements StatusPeer { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/_SwingTray.java b/src/dorkbox/systemTray/ui/swing/_SwingTray.java index 85b6700..3ae2e2d 100644 --- a/src/dorkbox/systemTray/ui/swing/_SwingTray.java +++ b/src/dorkbox/systemTray/ui/swing/_SwingTray.java @@ -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.Swing; +import dorkbox.util.jna.linux.GtkEventDispatch; /** * Class for handling all system tray interaction, via Swing. @@ -66,7 +66,7 @@ class _SwingTray extends Tray { @Override public void setEnabled(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -104,7 +104,7 @@ class _SwingTray extends Tray { return; } - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -170,7 +170,7 @@ class _SwingTray extends Tray { @Override public void remove() { - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { @@ -207,7 +207,7 @@ class _SwingTray extends Tray { } this.tooltipText = tooltipText; - SwingUtil.invokeLater(new Runnable() { + Swing.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/util/HeavyCheckMark.java b/src/dorkbox/systemTray/util/HeavyCheckMark.java index 2fad45b..536d298 100644 --- a/src/dorkbox/systemTray/util/HeavyCheckMark.java +++ b/src/dorkbox/systemTray/util/HeavyCheckMark.java @@ -11,7 +11,7 @@ import java.io.File; import javax.imageio.ImageIO; import dorkbox.systemTray.SystemTray; -import dorkbox.util.CacheUtil; +import dorkbox.util.Cache; public class HeavyCheckMark { private static final double SVG_ORIG_SIZE_Y = 222.0D; @@ -51,7 +51,7 @@ public class HeavyCheckMark { targetImageSize = checkMarkSize; } - final File newFile = CacheUtil.create(name); + final File newFile = Cache.create(name); if (newFile.canRead() || newFile.length() == 0) { try { BufferedImage img = HeavyCheckMark.draw(color, checkMarkSize, targetImageSize); diff --git a/src/dorkbox/systemTray/util/ImageResizeUtil.java b/src/dorkbox/systemTray/util/ImageResizeUtil.java index bd7f718..b650a16 100644 --- a/src/dorkbox/systemTray/util/ImageResizeUtil.java +++ b/src/dorkbox/systemTray/util/ImageResizeUtil.java @@ -30,7 +30,7 @@ import javax.imageio.ImageIO; import javax.imageio.stream.ImageInputStream; import dorkbox.systemTray.SystemTray; -import dorkbox.util.CacheUtil; +import dorkbox.util.Cache; import dorkbox.util.IO; import dorkbox.util.ImageUtil; @@ -47,7 +47,7 @@ class ImageResizeUtil { File getTransparentImage(final int imageSize) { // NOTE: this does not need to be called on the EDT try { - final File newFile = CacheUtil.create(imageSize + "_empty.png"); + final File newFile = Cache.create(imageSize + "_empty.png"); return ImageUtil.createImage(imageSize, newFile, null); } catch (IOException e) { throw new RuntimeException("Unable to generate transparent image! Something is severely wrong!"); @@ -69,12 +69,12 @@ class ImageResizeUtil { imageStream.mark(0); // check if we already have this file information saved to disk, based on size + hash of data - final String cacheName = size + "_" + CacheUtil.createNameAsHash(imageStream); + final String cacheName = size + "_" + Cache.createNameAsHash(imageStream); ((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException // if we already have this fileName, reuse it - final File check = CacheUtil.check(cacheName); + final File check = Cache.check(cacheName); if (check != null) { return check; } @@ -83,7 +83,7 @@ class ImageResizeUtil { File resizedFile = resizeFileNoCheck(size, imageStream); // now cache that file - return CacheUtil.save(cacheName, resizedFile); + return Cache.save(cacheName, resizedFile); } catch (Exception e) { // this must be thrown throw new RuntimeException("Serious problems! Unable to extract error image, this should NEVER happen!", e); @@ -130,12 +130,12 @@ class ImageResizeUtil { imageStream.mark(0); // check if we already have this file information saved to disk, based on size + hash of data - cacheName = size + "_" + CacheUtil.createNameAsHash(imageStream); + cacheName = size + "_" + Cache.createNameAsHash(imageStream); ((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException // if we already have this fileName, reuse it - final File check = CacheUtil.check(cacheName); + final File check = Cache.check(cacheName); if (check != null) { return check; } @@ -165,7 +165,7 @@ class ImageResizeUtil { // now cache that file try { - return CacheUtil.save(cacheName, resizedFile); + return Cache.save(cacheName, resizedFile); } catch (Exception e) { // have to serve up the error image instead. SystemTray.logger.error("Error caching image. Using error icon instead", e); @@ -181,7 +181,7 @@ class ImageResizeUtil { } else { // no resize necessary, just cache as is. try { - return CacheUtil.save(cacheName, imageStream); + return Cache.save(cacheName, imageStream); } catch (Exception e) { // have to serve up the error image instead. SystemTray.logger.error("Error caching image. Using error icon instead", e); @@ -216,7 +216,7 @@ class ImageResizeUtil { File resizeFileNoCheck(final int size, InputStream inputStream) throws IOException { // have to resize the file (and return the new path) - File newFile = CacheUtil.create("temp_resize.png"); + File newFile = Cache.create("temp_resize.png"); // if it's already there, we have to delete it newFile.delete(); @@ -288,7 +288,7 @@ class ImageResizeUtil { return file; } else { - return CacheUtil.save(imageUrl); + return Cache.save(imageUrl); } } catch (Exception e) { // have to serve up the error image instead. @@ -307,7 +307,7 @@ class ImageResizeUtil { return ImageResizeUtil.resizeAndCache(getSize(isTrayImage), imageStream); } else { try { - return CacheUtil.save(imageStream); + return Cache.save(imageStream); } catch (IOException e) { SystemTray.logger.error("Error checking cache for information. Using error icon instead", e); return getErrorImage(0); @@ -337,7 +337,7 @@ class ImageResizeUtil { return file; } else { - File file = CacheUtil.save(imageInputStream); + File file = Cache.save(imageInputStream); imageInputStream.close(); // BAOS doesn't do anything, but here for completeness + documentation return file; @@ -362,7 +362,7 @@ class ImageResizeUtil { if (SystemTray.AUTO_SIZE) { return resizeAndCache(getSize(isTrayImage), fileStream); } else { - return CacheUtil.save(fileStream); + return Cache.save(fileStream); } } catch (Exception e) { // have to serve up the error image instead. 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/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java index 676b4c1..a4509f0 100644 --- a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java +++ b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java @@ -31,10 +31,10 @@ import javax.swing.JMenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.Tray; -import dorkbox.systemTray.jna.linux.GtkTheme; import dorkbox.systemTray.ui.swing._SwingTray; import dorkbox.util.OS; -import dorkbox.util.SwingUtil; +import dorkbox.util.Swing; +import dorkbox.util.jna.linux.GtkTheme; import dorkbox.util.jna.windows.User32; public @@ -147,7 +147,7 @@ class SizeAndScalingUtil { } else { final AtomicInteger iconSize = new AtomicInteger(); - SwingUtil.invokeAndWaitQuietly(new Runnable() { + Swing.invokeAndWaitQuietly(new Runnable() { @Override public void run() { @@ -159,7 +159,7 @@ class SizeAndScalingUtil { } // this is the largest size of an image used in a JMenuItem, before the size of the JMenuItem is forced to be larger - int height = SwingUtil.getLargestIconHeightForButton(jMenuItem); + int height = Swing.getLargestIconHeightForButton(jMenuItem); iconSize.set(height); } }); 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(); - } - }); - } - }); - } - } -} From 7ac866fac3ded22c565936d0fdc4966ca0a234b7 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jul 2017 18:36:59 +0200 Subject: [PATCH 18/92] Moved JNA to Utility module for use by other applications --- .../systemTray/jna/linux/AppIndicator.java | 198 ------- .../systemTray/jna/linux/FuncCallback.java | 29 - .../systemTray/jna/linux/GCallback.java | 30 - .../systemTray/jna/linux/GEventCallback.java | 28 - src/dorkbox/systemTray/jna/linux/Glib.java | 72 --- src/dorkbox/systemTray/jna/linux/Gobject.java | 75 --- src/dorkbox/systemTray/jna/linux/Gtk.java | 380 ------------- src/dorkbox/systemTray/jna/linux/Gtk2.java | 197 ------- src/dorkbox/systemTray/jna/linux/Gtk3.java | 257 --------- .../jna/linux/GtkEventDispatch.java | 352 ------------ .../systemTray/jna/linux/GtkLoader.java | 210 ------- .../systemTray/jna/linux/GtkState.java | 23 - .../systemTray/jna/linux/GtkTheme.java | 527 ------------------ .../structs/AppIndicatorInstanceStruct.java | 37 -- .../jna/linux/structs/GObjectStruct.java | 46 -- .../linux/structs/GTypeInstanceStruct.java | 44 -- .../jna/linux/structs/GdkColor.java | 88 --- .../jna/linux/structs/GdkEventButton.java | 47 -- .../jna/linux/structs/GtkRequisition.java | 37 -- .../jna/linux/structs/GtkStyle.java | 116 ---- .../jna/linux/structs/PangoRectangle.java | 39 -- 21 files changed, 2832 deletions(-) delete mode 100644 src/dorkbox/systemTray/jna/linux/AppIndicator.java delete mode 100644 src/dorkbox/systemTray/jna/linux/FuncCallback.java delete mode 100644 src/dorkbox/systemTray/jna/linux/GCallback.java delete mode 100644 src/dorkbox/systemTray/jna/linux/GEventCallback.java delete mode 100644 src/dorkbox/systemTray/jna/linux/Glib.java delete mode 100644 src/dorkbox/systemTray/jna/linux/Gobject.java delete mode 100644 src/dorkbox/systemTray/jna/linux/Gtk.java delete mode 100644 src/dorkbox/systemTray/jna/linux/Gtk2.java delete mode 100644 src/dorkbox/systemTray/jna/linux/Gtk3.java delete mode 100644 src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java delete mode 100644 src/dorkbox/systemTray/jna/linux/GtkLoader.java delete mode 100644 src/dorkbox/systemTray/jna/linux/GtkState.java delete mode 100644 src/dorkbox/systemTray/jna/linux/GtkTheme.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/AppIndicatorInstanceStruct.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GObjectStruct.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GTypeInstanceStruct.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GdkColor.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GdkEventButton.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GtkRequisition.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java delete mode 100644 src/dorkbox/systemTray/jna/linux/structs/PangoRectangle.java 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/GCallback.java b/src/dorkbox/systemTray/jna/linux/GCallback.java deleted file mode 100644 index 53344e0..0000000 --- a/src/dorkbox/systemTray/jna/linux/GCallback.java +++ /dev/null @@ -1,30 +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 -public -interface GCallback extends Callback { - /** - * @return Gtk.TRUE if we handled this event - */ - int callback(Pointer instance, 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 d4236af..0000000 --- a/src/dorkbox/systemTray/jna/linux/Gobject.java +++ /dev/null @@ -1,75 +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. - */ - 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 03cbbf0..0000000 --- a/src/dorkbox/systemTray/jna/linux/Gtk.java +++ /dev/null @@ -1,380 +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.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 - */ -@SuppressWarnings({"Duplicates", "SameParameterValue", "DeprecatedIsStillUsed", "WeakerAccess"}) -public -interface 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 - - - // make specific note of GTK2 vs GTK3 APIs - Gtk Gtk2 = GtkLoader.isGtk2 ? new Gtk2() : new Gtk3(); - Gtk3 Gtk3 = GtkLoader.isGtk2 ? null : (Gtk3) Gtk2; - - boolean isGtk2 = GtkLoader.isGtk2; - boolean isGtk3 = GtkLoader.isGtk3; - boolean isLoaded = GtkLoader.isLoaded; - - Function gtk_status_icon_position_menu = GtkLoader.gtk_status_icon_position_menu; - - int FALSE = 0; - int TRUE = 1; - - int MAJOR = GtkLoader.MAJOR; - int MINOR = GtkLoader.MINOR; - int MICRO = GtkLoader.MICRO; - - /** - * 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. - */ - 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. - */ - 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. - */ - void gtk_main(); - - /** - * aks for the current nesting level of the main loop. Useful to determine (at startup) if GTK is already running - */ - 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! - */ - void gtk_main_quit(); - - - - /** - * Creates a new GtkMenu - */ - Pointer gtk_menu_new(); - - /** - * Sets or replaces the menu item’s submenu, or removes it when a NULL submenu is passed. - */ - void gtk_menu_item_set_submenu(Pointer menuEntry, Pointer menu); - - /** - * Creates a new GtkSeparatorMenuItem. - */ - 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. - */ - Pointer gtk_image_new_from_file(String iconPath); - - /** - * Sets the active state of the menu item’s check box. - */ - 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. - */ - Pointer gtk_image_menu_item_new_with_mnemonic(String label); - - 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. - */ - 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. - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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. - */ - 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. - */ - void gtk_widget_set_sensitive(Pointer widget, boolean sensitive); - - /** - * Recursively shows a widget, and any child widgets (if the widget is a container) - */ - 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. - */ - 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. - */ - void gtk_widget_destroy(Pointer widget); - - /** - * Gets the GtkSettings object for screen , creating it if necessary. - * - * @since 2.2 - */ - Pointer gtk_settings_get_for_screen(Pointer screen); - - /** - * 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.) - */ - GtkStyle gtk_rc_get_style(Pointer widget); - - /** - * 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. - */ - void gtk_container_add(Pointer offscreen, Pointer widget); - - /** - * Get's the child from a GTK Bin object - */ - 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. - */ - 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. - */ - void pango_layout_get_pixel_extents(Pointer layout, Pointer ink_rect, Pointer logical_rect); - - /** - * Creates the GDK (windowing system) resources associated with a widget. For example, widget->window will be created when a widget - * is realized. Normally realization happens implicitly; if you show a widget and all its parent containers, then the widget will - * be realized and mapped automatically. - * - * Realizing a widget requires all the widget’s parent widgets to be realized; calling gtk_widget_realize() realizes the widget’s - * parents in addition to widget itself. If a widget is not yet inside a toplevel window when you realize it, bad things will happen. - * - * This function is primarily used in widget implementations, and isn’t very useful otherwise. Many times when you think you might - * need it, a better approach is to connect to a signal that will be called after the widget is realized automatically, such as - * “draw”. Or simply g_signal_connect() to the “realize” signal. - */ - void gtk_widget_realize(Pointer widget); - - /** - * Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen. - * - * @since 2.20 - */ - 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. - */ - 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. - */ - Pointer gtk_image_menu_item_new_from_stock(String stock_id, Pointer 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 8c623a2..0000000 --- a/src/dorkbox/systemTray/jna/linux/Gtk2.java +++ /dev/null @@ -1,197 +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 implements Gtk { - // objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk - - @Override - public native - int gdk_threads_add_idle_full(final int priority, final FuncCallback function, final Pointer data, final Pointer notify); - - @Override - public native - boolean gtk_init_check(final int argc); - - @Override - public native - void gtk_main(); - - @Override - public native - int gtk_main_level(); - - @Override - public native - void gtk_main_quit(); - - @Override - public native - Pointer gtk_menu_new(); - - @Override - public native - void gtk_menu_item_set_submenu(final Pointer menuEntry, final Pointer menu); - - @Override - public native - Pointer gtk_separator_menu_item_new(); - - @Override - public native - Pointer gtk_image_new_from_file(final String iconPath); - - @Override - public native - void gtk_check_menu_item_set_active(final Pointer check_menu_item, final boolean isChecked); - - @Override - public native - Pointer gtk_image_menu_item_new_with_mnemonic(final String label); - - @Override - public native - Pointer gtk_check_menu_item_new_with_mnemonic(final String label); - - @Override - public native - void gtk_image_menu_item_set_image(final Pointer image_menu_item, final Pointer image); - - @Override - public native - void gtk_image_menu_item_set_always_show_image(final Pointer menu_item, final boolean forceShow); - - @Override - public native - Pointer gtk_status_icon_new(); - - @Override - public native - Pointer gdk_get_default_root_window(); - - @Override - public native - Pointer gdk_screen_get_default(); - - @Override - public native - double gdk_screen_get_resolution(final Pointer screen); - - @Override - public native - void gtk_status_icon_set_from_file(final Pointer widget, final String label); - - @Override - public native - void gtk_status_icon_set_visible(final Pointer widget, final boolean visible); - - @Override - public native - void gtk_status_icon_set_tooltip_text(final Pointer widget, final String tooltipText); - - @Override - public native - void gtk_status_icon_set_title(final Pointer widget, final String titleText); - - @Override - public native - void gtk_status_icon_set_name(final Pointer widget, final String name); - - @Override - public native - void gtk_menu_popup(final Pointer menu, - final Pointer widget, - final Pointer bla, - final Function func, - final Pointer data, - final int button, - final int time); - - @Override - public native - void gtk_menu_item_set_label(final Pointer menu_item, final String label); - - @Override - public native - void gtk_menu_shell_append(final Pointer menu_shell, final Pointer child); - - @Override - public native - void gtk_widget_set_sensitive(final Pointer widget, final boolean sensitive); - - @Override - public native - void gtk_widget_show_all(final Pointer widget); - - @Override - public native - void gtk_container_remove(final Pointer parentWidget, final Pointer widget); - - @Override - public native - void gtk_widget_destroy(final Pointer widget); - - @Override - public native - Pointer gtk_settings_get_for_screen(final Pointer screen); - - @Override - public native - GtkStyle gtk_rc_get_style(final Pointer widget); - - @Override - public native - void gtk_container_add(final Pointer offscreen, final Pointer widget); - - @Override - public native - Pointer gtk_bin_get_child(final Pointer bin); - - @Override - public native - Pointer gtk_label_get_layout(final Pointer label); - - @Override - public native - void pango_layout_get_pixel_extents(final Pointer layout, final Pointer ink_rect, final Pointer logical_rect); - - @Override - public native - void gtk_widget_realize(final Pointer widget); - - @Override - public native - Pointer gtk_offscreen_window_new(); - - @Override - public native - void gtk_widget_size_request(final Pointer widget, final Pointer requisition); - - @Override - public native - Pointer gtk_image_menu_item_new_from_stock(final String stock_id, final 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 d59c4f6..0000000 --- a/src/dorkbox/systemTray/jna/linux/Gtk3.java +++ /dev/null @@ -1,257 +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+ 3. - *

- * Direct-mapping, See: https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md - */ -public -class Gtk3 implements 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 - - /** - * 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. - */ - @Override - public - void gtk_widget_size_request(final Pointer widget, final Pointer requisition) { - this.gtk_widget_get_preferred_size(widget, requisition, null); - } - - /** - * 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 native - void gtk_widget_get_preferred_size(final Pointer widget, final Pointer minimum_size, final Pointer natural_size); - - public native - int gtk_get_major_version(); - - public native - int gtk_get_minor_version(); - - public native - int gtk_get_micro_version(); - - /** - * 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 native - int gdk_window_get_scale_factor(Pointer window); - - - - - @Override - public native - int gdk_threads_add_idle_full(final int priority, final FuncCallback function, final Pointer data, final Pointer notify); - - @Override - public native - boolean gtk_init_check(final int argc); - - @Override - public native - void gtk_main(); - - @Override - public native - int gtk_main_level(); - - @Override - public native - void gtk_main_quit(); - - @Override - public native - Pointer gtk_menu_new(); - - @Override - public native - void gtk_menu_item_set_submenu(final Pointer menuEntry, final Pointer menu); - - @Override - public native - Pointer gtk_separator_menu_item_new(); - - @Override - public native - Pointer gtk_image_new_from_file(final String iconPath); - - @Override - public native - void gtk_check_menu_item_set_active(final Pointer check_menu_item, final boolean isChecked); - - @Override - public native - Pointer gtk_image_menu_item_new_with_mnemonic(final String label); - - @Override - public native - Pointer gtk_check_menu_item_new_with_mnemonic(final String label); - - @Override - public native - void gtk_image_menu_item_set_image(final Pointer image_menu_item, final Pointer image); - - @Override - public native - void gtk_image_menu_item_set_always_show_image(final Pointer menu_item, final boolean forceShow); - - @Override - public native - Pointer gtk_status_icon_new(); - - @Override - public native - Pointer gdk_get_default_root_window(); - - @Override - public native - Pointer gdk_screen_get_default(); - - @Override - public native - double gdk_screen_get_resolution(final Pointer screen); - - @Override - public native - void gtk_status_icon_set_from_file(final Pointer widget, final String label); - - @Override - public native - void gtk_status_icon_set_visible(final Pointer widget, final boolean visible); - - @Override - public native - void gtk_status_icon_set_tooltip_text(final Pointer widget, final String tooltipText); - - @Override - public native - void gtk_status_icon_set_title(final Pointer widget, final String titleText); - - @Override - public native - void gtk_status_icon_set_name(final Pointer widget, final String name); - - @Override - public native - void gtk_menu_popup(final Pointer menu, - final Pointer widget, - final Pointer bla, - final Function func, - final Pointer data, - final int button, - final int time); - - @Override - public native - void gtk_menu_item_set_label(final Pointer menu_item, final String label); - - @Override - public native - void gtk_menu_shell_append(final Pointer menu_shell, final Pointer child); - - @Override - public native - void gtk_widget_set_sensitive(final Pointer widget, final boolean sensitive); - - @Override - public native - void gtk_widget_show_all(final Pointer widget); - - @Override - public native - void gtk_container_remove(final Pointer parentWidget, final Pointer widget); - - @Override - public native - void gtk_widget_destroy(final Pointer widget); - - @Override - public native - Pointer gtk_settings_get_for_screen(final Pointer screen); - - @Override - public native - GtkStyle gtk_rc_get_style(final Pointer widget); - - @Override - public native - void gtk_container_add(final Pointer offscreen, final Pointer widget); - - @Override - public native - Pointer gtk_bin_get_child(final Pointer bin); - - @Override - public native - Pointer gtk_label_get_layout(final Pointer label); - - @Override - public native - void pango_layout_get_pixel_extents(final Pointer layout, final Pointer ink_rect, final Pointer logical_rect); - - @Override - public native - void gtk_widget_realize(final Pointer widget); - - @Override - public native - Pointer gtk_offscreen_window_new(); - - @Override - public native - Pointer gtk_image_menu_item_new_from_stock(final String stock_id, final Pointer accel_group); -} diff --git a/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java b/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java deleted file mode 100644 index fd1de0d..0000000 --- a/src/dorkbox/systemTray/jna/linux/GtkEventDispatch.java +++ /dev/null @@ -1,352 +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 static dorkbox.systemTray.jna.linux.Gtk.Gtk2; - -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 (!GtkLoader.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 (GtkLoader.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 Gtk2.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 (!GtkLoader.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/GtkLoader.java b/src/dorkbox/systemTray/jna/linux/GtkLoader.java deleted file mode 100644 index b1b8b69..0000000 --- a/src/dorkbox/systemTray/jna/linux/GtkLoader.java +++ /dev/null @@ -1,210 +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 static dorkbox.systemTray.jna.linux.Gtk.Gtk2; - -import com.sun.jna.Function; -import com.sun.jna.NativeLibrary; - -import dorkbox.systemTray.SystemTray; -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"}) -class GtkLoader { - // 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 - - // NOTE: AppIndicator uses this info to figure out WHAT VERSION OF appindicator to use: GTK2 -> appindicator1, GTK3 -> appindicator3 - static final boolean isGtk2; - static final boolean isGtk3; - static final boolean isLoaded; - - - static final boolean alreadyRunningGTK; - - static Function gtk_status_icon_position_menu = null; - - static final int MAJOR; - static final int MINOR; - 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; - Gtk gtk = new Gtk2(); - - // 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 = gtk.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. - JnaHelper.register(gtk3LibName, Gtk3.class); - gtk_status_icon_position_menu = Function.getFunction(gtk3LibName, "gtk_status_icon_position_menu"); - Gtk3 gtk = new Gtk3(); - - // 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 = gtk.gtk_main_level() != 0; - _isLoaded = true; - - major = gtk.gtk_get_major_version(); - minor = gtk.gtk_get_minor_version(); - micro = gtk.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."); - } - } - } -} - diff --git a/src/dorkbox/systemTray/jna/linux/GtkState.java b/src/dorkbox/systemTray/jna/linux/GtkState.java deleted file mode 100644 index 49a39af..0000000 --- a/src/dorkbox/systemTray/jna/linux/GtkState.java +++ /dev/null @@ -1,23 +0,0 @@ -package dorkbox.systemTray.jna.linux; - -/** - * - */ -@SuppressWarnings({"unused", "PointlessBitwiseExpression"}) -public -class GtkState { - 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; -} diff --git a/src/dorkbox/systemTray/jna/linux/GtkTheme.java b/src/dorkbox/systemTray/jna/linux/GtkTheme.java deleted file mode 100644 index 5e085fe..0000000 --- a/src/dorkbox/systemTray/jna/linux/GtkTheme.java +++ /dev/null @@ -1,527 +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.jna.linux.Gtk.Gtk2; -import static dorkbox.systemTray.jna.linux.Gtk.Gtk3; - -import java.awt.Color; -import java.awt.Rectangle; -import java.awt.Toolkit; -import java.io.ByteArrayOutputStream; -import java.io.File; -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.jna.linux.structs.GtkRequisition; -import dorkbox.systemTray.jna.linux.structs.GtkStyle; -import dorkbox.systemTray.jna.linux.structs.PangoRectangle; -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 various methods needed to get information set by a GTK theme. - */ -@SuppressWarnings({"deprecation", "WeakerAccess"}) -public -class GtkTheme { - private static final boolean DEBUG = false; - - public static - Rectangle getPixelTextHeight(String text) { - // the following method requires an offscreen widget to get the size of text (for the checkmark size) via pango - // don't forget to destroy everything! - Pointer menu = null; - Pointer item = null; - - try { - menu = Gtk2.gtk_menu_new(); - item = Gtk2.gtk_image_menu_item_new_with_mnemonic(text); - - Gtk2.gtk_container_add(menu, item); - - Gtk2.gtk_widget_realize(menu); - Gtk2.gtk_widget_realize(item); - Gtk2.gtk_widget_show_all(menu); - - // get the text widget (GtkAccelLabel/GtkLabel) from inside the GtkMenuItem - Pointer textLabel = Gtk2.gtk_bin_get_child(item); - Pointer pangoLayout = Gtk2.gtk_label_get_layout(textLabel); - - // ink pixel size is how much exact space it takes on the screen - PangoRectangle ink = new PangoRectangle(); - - Gtk2.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null); - ink.read(); - - return new Rectangle(ink.width, ink.height); - } finally { - Gtk2.gtk_widget_destroy(item); - Gtk2.gtk_widget_destroy(menu); - } - } - - /** - * @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 = Gtk2.gtk_offscreen_window_new(); - - // get the default icon size for the "paste" icon. - Pointer item = Gtk2.gtk_image_menu_item_new_from_stock("gtk-paste", null); - - Gtk2.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(); - Gtk2.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() { - // 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(); - screenScale.set(0D); - screenDPI.set(0); - - GtkEventDispatch.dispatchAndWait(new Runnable() { - @Override - public - void run() { - // screen DPI - Pointer screen = Gtk2.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) Gtk2.gdk_screen_get_resolution(screen)); - } - - if (Gtk2.isGtk3) { - Pointer window = Gtk2.gdk_get_default_root_window(); - if (window != null) { - double scale = Gtk3.gdk_window_get_scale_factor(window); - screenScale.set(scale); - } - } - } - }); - - // 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 we have gsettings, great! otherwise try KDE - 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) { - } - - 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; - 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 || env == OSUtil.DesktopEnv.Env.Unity7) { - // 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 = Gtk2.gdk_screen_get_default(); - Pointer settings = null; - - if (screen != null) { - settings = Gtk2.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() { - final AtomicReference color = new AtomicReference(null); - GtkEventDispatch.dispatchAndWait(new Runnable() { - @SuppressWarnings("UnusedAssignment") - @Override - public - void run() { - Color c = null; - - // the following method requires an offscreen widget to get the style information from. - // don't forget to destroy everything! - Pointer menu = null; - Pointer item = null; - - try { - menu = Gtk2.gtk_menu_new(); - item = Gtk2.gtk_image_menu_item_new_with_mnemonic("a"); - - Gtk2.gtk_container_add(menu, item); - - Gtk2.gtk_widget_realize(menu); - Gtk2.gtk_widget_realize(item); - Gtk2.gtk_widget_show_all(menu); - - GtkStyle style = Gtk2.gtk_rc_get_style(item); - style.read(); - - // this is the same color chromium uses (fg) - // https://chromium.googlesource.com/chromium/src/+/b3ca230ddd7d1238ee96ed26ea23e369f10dd655/chrome/browser/ui/libgtk2ui/gtk2_ui.cc#873 - c = style.fg[GtkState.NORMAL].getColor(); - - color.set(c); - } finally { - Gtk2.gtk_widget_destroy(item); - Gtk2.gtk_widget_destroy(menu); - } - } - }); - - - 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; - } -} 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/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 000e356..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 from positive int (value between 0 and 65535, these are 16 bits per pixel) to values 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/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 70e8420..0000000 --- a/src/dorkbox/systemTray/jna/linux/structs/GtkStyle.java +++ /dev/null @@ -1,116 +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 { - } - - // required, even though it's "private" in the corresponding C code. OTHERWISE the memory offsets are INCORRECT. - public GObjectStruct parent_instance; - - /** 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]; - - /** Halfway between text/base */ - public GdkColor text_aa[] = new GdkColor[5]; - - 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]; - - public - void debug(final int gtkState) { - System.err.println("base " + base[gtkState].getColor()); - System.err.println("text " + text[gtkState].getColor()); - System.err.println("text_aa " + text_aa[gtkState].getColor()); - System.err.println("bg " + bg[gtkState].getColor()); - System.err.println("fg " + fg[gtkState].getColor()); - } - - - @Override - protected - List getFieldOrder() { - return Arrays.asList("parent_instance", - "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"); - } -} From f584ca4549e28441a24caead8d4e7874fc7a30fd Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 15 Jul 2017 18:13:43 +0200 Subject: [PATCH 19/92] Polished JavaFX and SWT dispatch access to remove hard dependency on JavaFX and SWT. Now uses JNA to create the classes for SWT access + a mix of reflection. JavaFX uses reflection. --- src/dorkbox/systemTray/SystemTray.java | 45 +++++++++---------- .../ui/gtk/_GtkStatusIconNativeTray.java | 4 +- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index efc5795..c3336dd 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -48,14 +48,14 @@ import dorkbox.systemTray.util.LinuxSwingUI; import dorkbox.systemTray.util.SizeAndScalingUtil; import dorkbox.systemTray.util.SystemTrayFixes; import dorkbox.systemTray.util.WindowsSwingUI; -import dorkbox.util.Cache; -import dorkbox.util.Framework; +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.Swing; +import dorkbox.util.SwingUtil; +import dorkbox.util.Swt; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gtk; import dorkbox.util.jna.linux.GtkEventDispatch; @@ -410,7 +410,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 (Framework.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" + @@ -428,7 +428,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 (Framework.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."); @@ -456,7 +456,7 @@ class SystemTray { // 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 && !Framework.isJavaFxLoaded && !Framework.isSwtLoaded) { + if (!FORCE_GTK2 && !JavaFX.isLoaded && !Swt.isLoaded) { String currentUI = UIManager.getLookAndFeel() .getClass() @@ -493,11 +493,11 @@ class SystemTray { } } } - else if (Framework.isSwtLoaded) { + 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 (Framework.isSwtGtk3 && FORCE_GTK2) { + if (Swt.isGtk3 && 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;`"); @@ -505,21 +505,21 @@ class SystemTray { systemTrayMenu = null; systemTray = null; return; - } else if (!Framework.isSwtGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { + } else if (!Swt.isGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { // we must use GTK2, because SWT is GTK2 FORCE_GTK2 = true; logger.warn("Forcing GTK2 because SWT is GTK2"); } } - else if (Framework.isJavaFxLoaded) { + else 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 (Framework.isJavaFxGtk3 && FORCE_GTK2) { + if (JavaFX.isGtk3 && FORCE_GTK2) { // if we are java9, then we can change it -- otherwise we cannot. if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) { FORCE_GTK2 = false; @@ -536,7 +536,7 @@ class SystemTray { systemTray = null; return; } - } else if (!Framework.isJavaFxGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { + } else if (!JavaFX.isGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { // we must use GTK2, because JavaFX is GTK2 FORCE_GTK2 = true; @@ -558,8 +558,8 @@ class SystemTray { logger.debug("Is Auto sizing tray/menu? {}", AUTO_SIZE); - logger.debug("Is JavaFX detected? {}", Framework.isJavaFxLoaded); - logger.debug("Is SWT detected? {}", Framework.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"); @@ -645,10 +645,7 @@ class SystemTray { // - appIndicator/gtk require strings (which is the path) // - swing version loads as an image (which can be stream or path, we use path) - Cache.tempDir = "SystemTrayImages"; - - // This will initialize javaFX/SWT event dispatch methods - Framework.initDispatch(); + CacheUtil.tempDir = "SystemTrayImages"; try { @@ -717,7 +714,7 @@ class SystemTray { // have to make adjustments BEFORE the tray/menu image size calculations - if (AUTO_FIX_INCONSISTENCIES && isTrayType(trayType, TrayType.Swing) && SystemTray.SWING_UI == null && Swing.isDefaultLookAndFeel()) { + if (AUTO_FIX_INCONSISTENCIES && isTrayType(trayType, TrayType.Swing) && SystemTray.SWING_UI == null && SwingUtil.isDefaultLookAndFeel()) { if (isNix) { SystemTray.SWING_UI = new LinuxSwingUI(); } @@ -753,7 +750,7 @@ class SystemTray { - if ((Framework.isJavaFxLoaded || Framework.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."); @@ -768,7 +765,7 @@ class SystemTray { // linux + GTK/AppIndicator menus must not start on the EDT! // AWT/Swing must be constructed on the EDT however... - if (Framework.isJavaFxLoaded || Framework.isSwtLoaded || + if (JavaFX.isLoaded || Swt.isLoaded || (isNix && (isTrayType(trayType, TrayType.GtkStatusIcon) || isTrayType(trayType, TrayType.AppIndicator))) ) { try { @@ -782,7 +779,7 @@ class SystemTray { // have to construct swing stuff inside the swing EDT final Class finalTrayType = trayType; - Swing.invokeAndWait(new Runnable() { + SwingUtil.invokeAndWait(new Runnable() { @Override public void run() { @@ -811,7 +808,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 (Framework.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() { @@ -824,7 +821,7 @@ class SystemTray { } }); } - else if (Framework.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 dorkbox.util.Swt.onShutdown(new Runnable() { diff --git a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java index e9c4bea..c35342d 100644 --- a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java @@ -26,7 +26,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.Tray; import dorkbox.systemTray.gnomeShell.Extension; -import dorkbox.util.Framework; +import dorkbox.util.JavaFX; import dorkbox.util.jna.linux.GEventCallback; import dorkbox.util.jna.linux.Gobject; import dorkbox.util.jna.linux.GtkEventDispatch; @@ -194,7 +194,7 @@ class _GtkStatusIconNativeTray extends Tray { // 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 (Framework.isJavaFxLoaded || Tray.usingGnome) { + if (JavaFX.isLoaded || Tray.usingGnome) { Gtk2.gtk_status_icon_set_name(trayIcon, Extension.DEFAULT_NAME); } } From fa3afc7c7444b3f6ba78ae41a0697746d4ebba2f Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 15 Jul 2017 18:25:10 +0200 Subject: [PATCH 20/92] Moved Classloader utility methods into their own class. --- .../systemTray/util/SystemTrayFixes.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dorkbox/systemTray/util/SystemTrayFixes.java b/src/dorkbox/systemTray/util/SystemTrayFixes.java index 7628af0..26566bc 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; @@ -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"); From 68c97956742e3a42afa9b94a6b39f481b01ca021 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 15 Jul 2017 18:25:46 +0200 Subject: [PATCH 21/92] (reverted) Changed Util class names --- src/dorkbox/systemTray/Checkbox.java | 4 +-- src/dorkbox/systemTray/MenuItem.java | 4 +-- src/dorkbox/systemTray/ui/awt/AwtMenu.java | 14 +++++----- .../systemTray/ui/awt/AwtMenuItem.java | 12 ++++---- .../ui/awt/AwtMenuItemCheckbox.java | 14 +++++----- .../ui/awt/AwtMenuItemSeparator.java | 4 +-- .../systemTray/ui/awt/AwtMenuItemStatus.java | 6 ++-- src/dorkbox/systemTray/ui/awt/_AwtTray.java | 10 +++---- .../systemTray/ui/swing/SwingMenu.java | 16 +++++------ .../systemTray/ui/swing/SwingMenuItem.java | 14 +++++----- .../ui/swing/SwingMenuItemCheckbox.java | 16 +++++------ .../ui/swing/SwingMenuItemSeparator.java | 4 +-- .../ui/swing/SwingMenuItemStatus.java | 6 ++-- .../systemTray/ui/swing/_SwingTray.java | 10 +++---- .../systemTray/util/HeavyCheckMark.java | 4 +-- .../systemTray/util/ImageResizeUtil.java | 28 +++++++++---------- .../systemTray/util/SizeAndScalingUtil.java | 6 ++-- test/dorkbox/TestTray.java | 8 +++--- test/dorkbox/TestTrayJavaFX.java | 8 +++--- test/dorkbox/TestTraySwt.java | 8 +++--- 20 files changed, 98 insertions(+), 98 deletions(-) diff --git a/src/dorkbox/systemTray/Checkbox.java b/src/dorkbox/systemTray/Checkbox.java index 14b5b47..b366366 100644 --- a/src/dorkbox/systemTray/Checkbox.java +++ b/src/dorkbox/systemTray/Checkbox.java @@ -18,7 +18,7 @@ package dorkbox.systemTray; import java.awt.event.ActionListener; import dorkbox.systemTray.peer.CheckboxPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; /** * This represents a common menu-checkbox entry, that is cross platform in nature @@ -189,7 +189,7 @@ class Checkbox extends Entry { */ public void setShortcut(final int key) { - this.mnemonicKey = Swing.getFromVirtualKey(key); + this.mnemonicKey = SwingUtil.getFromVirtualKey(key); if (peer != null) { ((CheckboxPeer) peer).setShortcut(this); diff --git a/src/dorkbox/systemTray/MenuItem.java b/src/dorkbox/systemTray/MenuItem.java index 1ca6caa..a0bc7be 100644 --- a/src/dorkbox/systemTray/MenuItem.java +++ b/src/dorkbox/systemTray/MenuItem.java @@ -25,7 +25,7 @@ import javax.imageio.stream.ImageInputStream; import dorkbox.systemTray.peer.MenuItemPeer; import dorkbox.systemTray.util.ImageResizeUtil; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; /** * This represents a common menu-entry, that is cross platform in nature @@ -336,7 +336,7 @@ class MenuItem extends Entry { */ public void setShortcut(final int key) { - this.mnemonicKey = Swing.getFromVirtualKey(key); + this.mnemonicKey = SwingUtil.getFromVirtualKey(key); if (peer != null) { ((MenuItemPeer) peer).setShortcut(this); diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenu.java b/src/dorkbox/systemTray/ui/awt/AwtMenu.java index a07ff83..d9e112a 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenu.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenu.java @@ -26,7 +26,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.Status; import dorkbox.systemTray.peer.MenuPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; // this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both @SuppressWarnings("ForLoopReplaceableByForEach") @@ -53,7 +53,7 @@ class AwtMenu implements MenuPeer { public void add(final Menu parentMenu, final Entry entry, final int index) { // must always be called on the EDT - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -92,7 +92,7 @@ class AwtMenu implements MenuPeer { @Override public void setEnabled(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -105,7 +105,7 @@ class AwtMenu implements MenuPeer { @Override public void setText(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -125,9 +125,9 @@ class AwtMenu implements MenuPeer { public void setShortcut(final MenuItem menuItem) { // yikes... - final int vKey = Swing.getVirtualKey(menuItem.getShortcut()); + final int vKey = SwingUtil.getVirtualKey(menuItem.getShortcut()); - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -139,7 +139,7 @@ class AwtMenu implements MenuPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java index ef634ad..99018c7 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java @@ -21,7 +21,7 @@ import java.awt.event.ActionListener; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.MenuItemPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class AwtMenuItem implements MenuItemPeer { @@ -45,7 +45,7 @@ class AwtMenuItem implements MenuItemPeer { @Override public void setEnabled(final dorkbox.systemTray.MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -57,7 +57,7 @@ class AwtMenuItem implements MenuItemPeer { @Override public void setText(final dorkbox.systemTray.MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -103,9 +103,9 @@ class AwtMenuItem implements MenuItemPeer { void setShortcut(final dorkbox.systemTray.MenuItem menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = Swing.getVirtualKey(shortcut); + final int vKey = SwingUtil.getVirtualKey(shortcut); - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -118,7 +118,7 @@ class AwtMenuItem implements MenuItemPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java index 17750a5..fac468c 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java @@ -22,7 +22,7 @@ import java.awt.event.ActionListener; import dorkbox.systemTray.Checkbox; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.CheckboxPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class AwtMenuItemCheckbox implements CheckboxPeer { @@ -42,7 +42,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { @Override public void setEnabled(final Checkbox menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -54,7 +54,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { @Override public void setText(final Checkbox menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -102,9 +102,9 @@ class AwtMenuItemCheckbox implements CheckboxPeer { void setShortcut(final Checkbox menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = Swing.getVirtualKey(shortcut); + final int vKey = SwingUtil.getVirtualKey(shortcut); - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -122,7 +122,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { if (checked != this.isChecked) { this.isChecked = checked; - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -136,7 +136,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java index 7a9c385..459ccf0 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemSeparator.java @@ -17,7 +17,7 @@ package dorkbox.systemTray.ui.awt; import dorkbox.systemTray.peer.EntryPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class AwtMenuItemSeparator implements EntryPeer { @@ -34,7 +34,7 @@ class AwtMenuItemSeparator implements EntryPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java index c5310ee..e3ee146 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemStatus.java @@ -22,7 +22,7 @@ import java.awt.MenuItem; import dorkbox.systemTray.Status; import dorkbox.systemTray.peer.StatusPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class AwtMenuItemStatus implements StatusPeer { @@ -39,7 +39,7 @@ class AwtMenuItemStatus implements StatusPeer { @Override public void setText(final Status menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -63,7 +63,7 @@ class AwtMenuItemStatus implements StatusPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/awt/_AwtTray.java b/src/dorkbox/systemTray/ui/awt/_AwtTray.java index fa0b52e..673e8dd 100644 --- a/src/dorkbox/systemTray/ui/awt/_AwtTray.java +++ b/src/dorkbox/systemTray/ui/awt/_AwtTray.java @@ -27,7 +27,7 @@ import javax.swing.ImageIcon; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Tray; import dorkbox.util.OS; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; /** * Class for handling all system tray interaction, via AWT. Pretty much EXCLUSIVELY for on MacOS, because that is the only time this @@ -69,7 +69,7 @@ class _AwtTray extends Tray { @Override public void setEnabled(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -136,7 +136,7 @@ class _AwtTray extends Tray { return; } - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -185,7 +185,7 @@ class _AwtTray extends Tray { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -217,7 +217,7 @@ class _AwtTray extends Tray { } this.tooltipText = tooltipText; - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenu.java b/src/dorkbox/systemTray/ui/swing/SwingMenu.java index e1242d9..6d4ece1 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenu.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenu.java @@ -30,7 +30,7 @@ import dorkbox.systemTray.Separator; import dorkbox.systemTray.Status; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.MenuPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; // this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both (and duplicate code) @SuppressWarnings("ForLoopReplaceableByForEach") @@ -69,7 +69,7 @@ class SwingMenu implements MenuPeer { public void add(final Menu parentMenu, final Entry entry, final int index) { // must always be called on the EDT - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -107,7 +107,7 @@ class SwingMenu implements MenuPeer { @Override public void setImage(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -127,7 +127,7 @@ class SwingMenu implements MenuPeer { @Override public void setEnabled(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -141,7 +141,7 @@ class SwingMenu implements MenuPeer { @Override public void setText(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -162,9 +162,9 @@ class SwingMenu implements MenuPeer { void setShortcut(final MenuItem menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = Swing.getVirtualKey(shortcut); + final int vKey = SwingUtil.getVirtualKey(shortcut); - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -179,7 +179,7 @@ class SwingMenu implements MenuPeer { @Override public synchronized void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java index 90ff945..eac0de2 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java @@ -28,7 +28,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.MenuItemPeer; import dorkbox.systemTray.util.ImageResizeUtil; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class SwingMenuItem implements MenuItemPeer { @@ -78,7 +78,7 @@ class SwingMenuItem implements MenuItemPeer { @Override public void setImage(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -97,7 +97,7 @@ class SwingMenuItem implements MenuItemPeer { @Override public void setEnabled(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -109,7 +109,7 @@ class SwingMenuItem implements MenuItemPeer { @Override public void setText(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -155,9 +155,9 @@ class SwingMenuItem implements MenuItemPeer { void setShortcut(final MenuItem menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = Swing.getVirtualKey(shortcut); + final int vKey = SwingUtil.getVirtualKey(shortcut); - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -170,7 +170,7 @@ class SwingMenuItem implements MenuItemPeer { public void remove() { //noinspection Duplicates - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java index fb5afa9..c2dd068 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java @@ -26,8 +26,8 @@ import dorkbox.systemTray.Entry; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.CheckboxPeer; import dorkbox.systemTray.util.HeavyCheckMark; -import dorkbox.util.Font; -import dorkbox.util.Swing; +import dorkbox.util.FontUtil; +import dorkbox.util.SwingUtil; class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { @@ -50,7 +50,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { } // Having the checkmark size the same size as the letter X is a reasonably nice size. - int size = Font.getFontHeight(jMenuItem.getFont(), "X"); + int size = FontUtil.getFontHeight(jMenuItem.getFont(), "X"); // this is the largest size of an image used in a JMenuItem, before the size of the JMenuItem is forced to be larger int menuImageSize = SystemTray.get() @@ -73,7 +73,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { @Override public void setEnabled(final Checkbox menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -85,7 +85,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { @Override public void setText(final Checkbox menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -133,9 +133,9 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { void setShortcut(final Checkbox menuItem) { char shortcut = menuItem.getShortcut(); // yikes... - final int vKey = Swing.getVirtualKey(shortcut); + final int vKey = SwingUtil.getVirtualKey(shortcut); - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -153,7 +153,7 @@ class SwingMenuItemCheckbox extends SwingMenuItem implements CheckboxPeer { if (checked != this.isChecked) { this.isChecked = checked; - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java index be9571c..ebf0d0a 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemSeparator.java @@ -19,7 +19,7 @@ import javax.swing.JSeparator; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.EntryPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class SwingMenuItemSeparator implements EntryPeer { @@ -40,7 +40,7 @@ class SwingMenuItemSeparator implements EntryPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java index c655909..e1bb46c 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemStatus.java @@ -24,7 +24,7 @@ import dorkbox.systemTray.Entry; import dorkbox.systemTray.Status; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.StatusPeer; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; class SwingMenuItemStatus implements StatusPeer { @@ -54,7 +54,7 @@ class SwingMenuItemStatus implements StatusPeer { @Override public void setText(final Status menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -66,7 +66,7 @@ class SwingMenuItemStatus implements StatusPeer { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/ui/swing/_SwingTray.java b/src/dorkbox/systemTray/ui/swing/_SwingTray.java index 3ae2e2d..2e6374f 100644 --- a/src/dorkbox/systemTray/ui/swing/_SwingTray.java +++ b/src/dorkbox/systemTray/ui/swing/_SwingTray.java @@ -29,7 +29,7 @@ import javax.swing.JPopupMenu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Tray; import dorkbox.util.OS; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; import dorkbox.util.jna.linux.GtkEventDispatch; /** @@ -66,7 +66,7 @@ class _SwingTray extends Tray { @Override public void setEnabled(final MenuItem menuItem) { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -104,7 +104,7 @@ class _SwingTray extends Tray { return; } - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -170,7 +170,7 @@ class _SwingTray extends Tray { @Override public void remove() { - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { @@ -207,7 +207,7 @@ class _SwingTray extends Tray { } this.tooltipText = tooltipText; - Swing.invokeLater(new Runnable() { + SwingUtil.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/dorkbox/systemTray/util/HeavyCheckMark.java b/src/dorkbox/systemTray/util/HeavyCheckMark.java index 536d298..2fad45b 100644 --- a/src/dorkbox/systemTray/util/HeavyCheckMark.java +++ b/src/dorkbox/systemTray/util/HeavyCheckMark.java @@ -11,7 +11,7 @@ import java.io.File; import javax.imageio.ImageIO; import dorkbox.systemTray.SystemTray; -import dorkbox.util.Cache; +import dorkbox.util.CacheUtil; public class HeavyCheckMark { private static final double SVG_ORIG_SIZE_Y = 222.0D; @@ -51,7 +51,7 @@ public class HeavyCheckMark { targetImageSize = checkMarkSize; } - final File newFile = Cache.create(name); + final File newFile = CacheUtil.create(name); if (newFile.canRead() || newFile.length() == 0) { try { BufferedImage img = HeavyCheckMark.draw(color, checkMarkSize, targetImageSize); diff --git a/src/dorkbox/systemTray/util/ImageResizeUtil.java b/src/dorkbox/systemTray/util/ImageResizeUtil.java index b650a16..bd7f718 100644 --- a/src/dorkbox/systemTray/util/ImageResizeUtil.java +++ b/src/dorkbox/systemTray/util/ImageResizeUtil.java @@ -30,7 +30,7 @@ import javax.imageio.ImageIO; import javax.imageio.stream.ImageInputStream; import dorkbox.systemTray.SystemTray; -import dorkbox.util.Cache; +import dorkbox.util.CacheUtil; import dorkbox.util.IO; import dorkbox.util.ImageUtil; @@ -47,7 +47,7 @@ class ImageResizeUtil { File getTransparentImage(final int imageSize) { // NOTE: this does not need to be called on the EDT try { - final File newFile = Cache.create(imageSize + "_empty.png"); + final File newFile = CacheUtil.create(imageSize + "_empty.png"); return ImageUtil.createImage(imageSize, newFile, null); } catch (IOException e) { throw new RuntimeException("Unable to generate transparent image! Something is severely wrong!"); @@ -69,12 +69,12 @@ class ImageResizeUtil { imageStream.mark(0); // check if we already have this file information saved to disk, based on size + hash of data - final String cacheName = size + "_" + Cache.createNameAsHash(imageStream); + final String cacheName = size + "_" + CacheUtil.createNameAsHash(imageStream); ((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException // if we already have this fileName, reuse it - final File check = Cache.check(cacheName); + final File check = CacheUtil.check(cacheName); if (check != null) { return check; } @@ -83,7 +83,7 @@ class ImageResizeUtil { File resizedFile = resizeFileNoCheck(size, imageStream); // now cache that file - return Cache.save(cacheName, resizedFile); + return CacheUtil.save(cacheName, resizedFile); } catch (Exception e) { // this must be thrown throw new RuntimeException("Serious problems! Unable to extract error image, this should NEVER happen!", e); @@ -130,12 +130,12 @@ class ImageResizeUtil { imageStream.mark(0); // check if we already have this file information saved to disk, based on size + hash of data - cacheName = size + "_" + Cache.createNameAsHash(imageStream); + cacheName = size + "_" + CacheUtil.createNameAsHash(imageStream); ((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException // if we already have this fileName, reuse it - final File check = Cache.check(cacheName); + final File check = CacheUtil.check(cacheName); if (check != null) { return check; } @@ -165,7 +165,7 @@ class ImageResizeUtil { // now cache that file try { - return Cache.save(cacheName, resizedFile); + return CacheUtil.save(cacheName, resizedFile); } catch (Exception e) { // have to serve up the error image instead. SystemTray.logger.error("Error caching image. Using error icon instead", e); @@ -181,7 +181,7 @@ class ImageResizeUtil { } else { // no resize necessary, just cache as is. try { - return Cache.save(cacheName, imageStream); + return CacheUtil.save(cacheName, imageStream); } catch (Exception e) { // have to serve up the error image instead. SystemTray.logger.error("Error caching image. Using error icon instead", e); @@ -216,7 +216,7 @@ class ImageResizeUtil { File resizeFileNoCheck(final int size, InputStream inputStream) throws IOException { // have to resize the file (and return the new path) - File newFile = Cache.create("temp_resize.png"); + File newFile = CacheUtil.create("temp_resize.png"); // if it's already there, we have to delete it newFile.delete(); @@ -288,7 +288,7 @@ class ImageResizeUtil { return file; } else { - return Cache.save(imageUrl); + return CacheUtil.save(imageUrl); } } catch (Exception e) { // have to serve up the error image instead. @@ -307,7 +307,7 @@ class ImageResizeUtil { return ImageResizeUtil.resizeAndCache(getSize(isTrayImage), imageStream); } else { try { - return Cache.save(imageStream); + return CacheUtil.save(imageStream); } catch (IOException e) { SystemTray.logger.error("Error checking cache for information. Using error icon instead", e); return getErrorImage(0); @@ -337,7 +337,7 @@ class ImageResizeUtil { return file; } else { - File file = Cache.save(imageInputStream); + File file = CacheUtil.save(imageInputStream); imageInputStream.close(); // BAOS doesn't do anything, but here for completeness + documentation return file; @@ -362,7 +362,7 @@ class ImageResizeUtil { if (SystemTray.AUTO_SIZE) { return resizeAndCache(getSize(isTrayImage), fileStream); } else { - return Cache.save(fileStream); + return CacheUtil.save(fileStream); } } catch (Exception e) { // have to serve up the error image instead. diff --git a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java index a4509f0..1ff4a8b 100644 --- a/src/dorkbox/systemTray/util/SizeAndScalingUtil.java +++ b/src/dorkbox/systemTray/util/SizeAndScalingUtil.java @@ -33,7 +33,7 @@ import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.Tray; import dorkbox.systemTray.ui.swing._SwingTray; import dorkbox.util.OS; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; import dorkbox.util.jna.linux.GtkTheme; import dorkbox.util.jna.windows.User32; @@ -147,7 +147,7 @@ class SizeAndScalingUtil { } else { final AtomicInteger iconSize = new AtomicInteger(); - Swing.invokeAndWaitQuietly(new Runnable() { + SwingUtil.invokeAndWaitQuietly(new Runnable() { @Override public void run() { @@ -159,7 +159,7 @@ class SizeAndScalingUtil { } // this is the largest size of an image used in a JMenuItem, before the size of the JMenuItem is forced to be larger - int height = Swing.getLargestIconHeightForButton(jMenuItem); + int height = SwingUtil.getLargestIconHeightForButton(jMenuItem); iconSize.set(height); } }); diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 1544efa..74173bd 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -25,8 +25,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; -import dorkbox.util.Cache; -import dorkbox.util.Swing; +import dorkbox.util.CacheUtil; +import dorkbox.util.SwingUtil; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -58,9 +58,9 @@ class TestTray { public TestTray() { - Cache.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. + CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. - Swing.setLookAndFeel(null); // set Native L&F (this is the System L&F instead of CrossPlatform L&F) + 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(); diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index 2f59643..da6253b 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -24,10 +24,10 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; -import dorkbox.util.Cache; +import dorkbox.util.CacheUtil; import dorkbox.util.JavaFX; import dorkbox.util.OS; -import dorkbox.util.Swing; +import dorkbox.util.SwingUtil; import javafx.application.Application; import javafx.application.Platform; import javafx.event.ActionEvent; @@ -120,9 +120,9 @@ class TestTrayJavaFX { primaryStage.show(); - Cache.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. + CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. - Swing.setLookAndFeel(null); // set Native L&F (this is the System L&F instead of CrossPlatform L&F) + 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(); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index 0f08b09..e4f14b5 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -30,8 +30,8 @@ import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; -import dorkbox.util.Cache; -import dorkbox.util.Swing; +import dorkbox.util.CacheUtil; +import dorkbox.util.SwingUtil; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -74,9 +74,9 @@ class TestTraySwt { helloWorldTest.setText("Hello World SWT ................. "); helloWorldTest.pack(); - Cache.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. + CacheUtil.clear(); // for test apps, make sure the cache is always reset. You should never do this in production. - Swing.setLookAndFeel(null); // set Native L&F (this is the System L&F instead of CrossPlatform L&F) + 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(); From 39a3bd5b62f64c4132ff0abb6db45dccbf9cc264 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 15 Jul 2017 23:55:36 +0200 Subject: [PATCH 22/92] ShellProcessBuilder -> ShellExecutor. Static method to simplify shell execution added. Added ability to pass in environment variables to new process. Added better detection for current shell in linux/unix (could be improved...). Added flag for executing process as a shell command. Output now defaults to en-US.UTF_8 --- .../systemTray/gnomeShell/Extension.java | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/dorkbox/systemTray/gnomeShell/Extension.java b/src/dorkbox/systemTray/gnomeShell/Extension.java index c3f1a6b..1fccee6 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,7 +26,6 @@ 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; @@ -37,7 +35,7 @@ import dorkbox.systemTray.SystemTray; import dorkbox.util.IO; import dorkbox.util.OSUtil; import dorkbox.util.Property; -import dorkbox.util.process.ShellProcessBuilder; +import dorkbox.util.process.ShellExecutor; @SuppressWarnings({"DanglingJavadoc", "WeakerAccess"}) public @@ -61,19 +59,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 +131,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 +157,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"); @@ -188,7 +179,7 @@ class Extension { } // now we have to restart the gnome shell via bash - final ShellProcessBuilder restartShell = new ShellProcessBuilder(); + final ShellExecutor restartShell = new ShellExecutor(); // restart shell in background process restartShell.addArgument(SHELL_RESTART_COMMAND); restartShell.start(); From 1c08c270e26ffb584f630181cdc838c111b6971b Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 00:52:17 +0200 Subject: [PATCH 23/92] Fixed readme typos. Updated compatibility matrix. Of important note, swing on linux now renders a background to fake transparency. There is a short visual glitch while the background is fetched. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ef80570..464c2b0 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ SystemTray Professional, cross-platform **SystemTray** support for *Swing/AWT*, *GtkStatusIcon*, and *AppIndicator* system-tray types for java applications on Java 6+. -This library provides **OS Native** menus and **Swing/AWT** menus, depending on the OS and Desktop Environment, and if AutoDetect (the default) is set. Linux/Unix will automatically choose *Nativ*e menus, Windows will choose *Swing*, and MacOS will choose *AWT*. - - Please note that *Native* menus, follow the specified look and feel of that OS and are limited by what is supported on the OS. Consequently they are not consistent across all platforms and environments. +This library provides **OS Native** menus and **Swing/AWT** menus, depending on the OS and Desktop Environment, and if AutoDetect (the default) is set. Linux/Unix will automatically choose *Native* (*GtkStatusIcon*, and *AppIndicator*) menus, Windows will choose *Swing*, and MacOS will choose *AWT*. + - Please note that the *Native* adn *AWT* menus follow the specified look and feel of that OS and are limited by what is supported on the OS. Consequently they are not consistent across all platforms and environments.   @@ -31,7 +31,7 @@ Problems and Restrictions - **SWT** can use *GTK2* or *GTK3*. If you want to use *GTK2* you must force SWT into *GTK2 mode* via `System.setProperty("SWT_GTK3", "0");` before SWT is initialized and only if there are problems with the autodetection, you can also set `SystemTray.FORCE_GTK2=true;`. - - **AppIndicators** under Ubuntu 16.04 (and possibly other distro's) **will not** work as a different user (ie: as a sudo'd user to `root`), since AppIndicators require a dbus connection to the current user's window manager -- and this cannot happen between different user accounts. **There is no workaround.** + - **AppIndicators** under Ubuntu 16.04 (and possibly other distro's) **will not** work as a different user (ie: as a sudo'd user to `root`), since AppIndicators require a dbus connection to the current user's window manager -- and this cannot happen between different user accounts. We attempt to detect this, and fallback to using Swing. - **MacOSX** is a *special snowflake* in how it handles GUI events, and so there are some bizzaro combinations of SWT, JavaFX, and Swing that do not work together (see the `Notes` below for the details.) @@ -52,6 +52,7 @@ OS | Java/Swing | JavaFX | SWT XUbuntu 16.04 | ✓ | ✓ | ✓ | Ubuntu 16.04 | ✓ | + | ✓ | UbuntuGnome 16.04 | ✓ | + | ✓ | +UbuntuGnome 17.04 | ✓ | + | ✓ | Fedora 23 | ✓ | ✓ | ✓ | Fedora 24 | ✓ | ✓ | ✓ | Fedora 25 | ✓ | ✓ | ✓ | @@ -71,7 +72,7 @@ Win 10 | ✓ | ✓ | ✓ | Notes: ------- -- Ubuntu 16.04+ with JavaFX require `libappindicator1` because of JavaFX GTK and indicator panel incompatibilities. See [more details](https://github.com/dorkbox/SystemTray/issues/14#issuecomment-248853532). +- Ubuntu 16.04+ with JavaFX require `libappindicator1` because of JavaFX GTK and indicator panel incompatibilities. See [more details](https://github.com/dorkbox/SystemTray/issues/14#issuecomment-248853532). We attempt to fallback to using Swing in this situation. - MacOSX JavaFX (Java7) is incompatible with the SystemTray by default. See [issue details](https://bugs.openjdk.java.net/browse/JDK-8116017). - To fix this do one of the following From e9d10b0bf91f9bdf80dd54f17a31a0ed248d06b0 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 02:07:07 +0200 Subject: [PATCH 24/92] Added 'about' -> https://github.com/dorkbox/SystemTray to the test examples. --- test/dorkbox/TestTray.java | 15 +++++++++++++++ test/dorkbox/TestTrayJavaFX.java | 13 +++++++++++++ test/dorkbox/TestTraySwt.java | 13 +++++++++++++ 3 files changed, 41 insertions(+) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 74173bd..75035bd 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; @@ -26,6 +27,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; import dorkbox.util.CacheUtil; +import dorkbox.util.Desktop; import dorkbox.util.SwingUtil; /** @@ -122,6 +124,19 @@ 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"); +// Desktop.browseDirectory("/"); + Desktop.launchEmail("foo@example.com"); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + })); Menu submenu = new Menu("Options", BLUE_CAMPING); submenu.setShortcut('t'); diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index da6253b..d8fcac6 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; @@ -25,6 +26,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; import dorkbox.util.CacheUtil; +import dorkbox.util.Desktop; import dorkbox.util.JavaFX; import dorkbox.util.OS; import dorkbox.util.SwingUtil; @@ -184,6 +186,17 @@ 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(); + } + } + })); Menu submenu = new Menu("Options", BLUE_CAMPING); submenu.setShortcut('t'); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index e4f14b5..e9c405d 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; @@ -31,6 +32,7 @@ import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; import dorkbox.util.CacheUtil; +import dorkbox.util.Desktop; import dorkbox.util.SwingUtil; /** @@ -138,6 +140,17 @@ 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(); + } + } + })); Menu submenu = new Menu("Options", BLUE_CAMPING); submenu.setShortcut('t'); From 086085a5ce56929cdef5a51f4aab61534ea815eb Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 02:17:57 +0200 Subject: [PATCH 25/92] Updated license --- LICENSE | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/LICENSE b/LICENSE index 8db30fb..b1acbbf 100644 --- a/LICENSE +++ b/LICENSE @@ -19,6 +19,12 @@ Copyright 2010, Brave New Software Project, Inc. + - QZTray - Apache 2.0 License + https://github.com/tresf/tray/blob/dorkbox/src/qz/utils/ShellUtilities.java + Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC + Partial code released as Apache 2.0 for exclusive use by dorkbox, llc + + - SLF4J - MIT License http://www.slf4j.org Copyright 2004-2008, QOS.ch From 08b86cf16f1b385cfa2987f0948f8994cbe47da5 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 02:19:37 +0200 Subject: [PATCH 26/92] Removed test code --- test/dorkbox/TestTray.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 75035bd..8e8bcb7 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -129,9 +129,7 @@ class TestTray { public void actionPerformed(final ActionEvent e) { try { -// Desktop.browseURL("https://github.com/dorkbox/SystemTray"); -// Desktop.browseDirectory("/"); - Desktop.launchEmail("foo@example.com"); + Desktop.browseURL("https://github.com/dorkbox/SystemTray"); } catch (IOException e1) { e1.printStackTrace(); } From e1a191e155bd7640b9b666896790e03ecf6215a5 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 12:22:23 +0200 Subject: [PATCH 27/92] UI components are now cached --- src/dorkbox/systemTray/util/LinuxSwingUI.java | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/dorkbox/systemTray/util/LinuxSwingUI.java b/src/dorkbox/systemTray/util/LinuxSwingUI.java index d8cb24f..88a15f3 100644 --- a/src/dorkbox/systemTray/util/LinuxSwingUI.java +++ b/src/dorkbox/systemTray/util/LinuxSwingUI.java @@ -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) * @@ -80,19 +83,23 @@ class LinuxSwingUI implements SwingUIFactory { @Override public PopupMenuUI getMenuUI(final JPopupMenu jPopupMenu, final Menu entry) { - return new DefaultPopupMenuUI(jPopupMenu) { - @Override - public - void installUI(final JComponent c) { - super.installUI(c); + if (popupMenuUI == null) { + popupMenuUI = new DefaultPopupMenuUI(jPopupMenu) { + @Override + public + void installUI(final JComponent c) { + super.installUI(c); - JPopupMenu popupMenu = (JPopupMenu) c; + JPopupMenu popupMenu = (JPopupMenu) c; - // borderUI resource border type will get changed internally! - // setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0)); - popupMenu.setBorder(new EmptyBorder(1, 1, 1, 1)); - } - }; + // borderUI resource border type will get changed internally! + // setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0)); + popupMenu.setBorder(new EmptyBorder(1, 1, 1, 1)); + } + }; + } + + return popupMenuUI; } /** @@ -106,19 +113,23 @@ class LinuxSwingUI 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 (menuItemUI == null) { + menuItemUI = new DefaultMenuItemUI(jMenuItem) { + @Override + public + void installUI(final JComponent c) { + super.installUI(c); - JMenuItem menuItem = (JMenuItem) c; - menuItem.setIconTextGap(8); - // the original is hardcoded to always be 2 (top/bottom). We want this to be larger, so the vertical spacing looks like - // other menus - c.setBorder(new Metal_MenuItemBorder(4)); - } - }; + JMenuItem menuItem = (JMenuItem) c; + menuItem.setIconTextGap(8); + // the original is hardcoded to always be 2 (top/bottom). We want this to be larger, so the vertical spacing looks like + // other menus + c.setBorder(new Metal_MenuItemBorder(4)); + } + }; + } + + return menuItemUI; } /** @@ -131,9 +142,12 @@ class LinuxSwingUI implements SwingUIFactory { @Override public SeparatorUI getSeparatorUI(final JSeparator jSeparator) { - return new DefaultSeparatorUI(jSeparator); - } + if (separatorUI == null) { + separatorUI = new DefaultSeparatorUI(jSeparator); + } + return separatorUI; + } /** * This saves a vector CheckMark to a correctly sized PNG file. The checkmark image will ALWAYS be centered in the targetImageSize From a034321080346582025c6f8c7bbda9cea697465b Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 12:32:19 +0200 Subject: [PATCH 28/92] (revert) UI components are now cached --- src/dorkbox/systemTray/util/LinuxSwingUI.java | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/dorkbox/systemTray/util/LinuxSwingUI.java b/src/dorkbox/systemTray/util/LinuxSwingUI.java index 88a15f3..6e10387 100644 --- a/src/dorkbox/systemTray/util/LinuxSwingUI.java +++ b/src/dorkbox/systemTray/util/LinuxSwingUI.java @@ -83,23 +83,19 @@ class LinuxSwingUI implements SwingUIFactory { @Override public PopupMenuUI getMenuUI(final JPopupMenu jPopupMenu, final Menu entry) { - if (popupMenuUI == null) { - popupMenuUI = new DefaultPopupMenuUI(jPopupMenu) { - @Override - public - void installUI(final JComponent c) { - super.installUI(c); + return new DefaultPopupMenuUI(jPopupMenu) { + @Override + public + void installUI(final JComponent c) { + super.installUI(c); - JPopupMenu popupMenu = (JPopupMenu) c; + JPopupMenu popupMenu = (JPopupMenu) c; - // borderUI resource border type will get changed internally! - // setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0)); - popupMenu.setBorder(new EmptyBorder(1, 1, 1, 1)); - } - }; - } - - return popupMenuUI; + // borderUI resource border type will get changed internally! + // setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0)); + popupMenu.setBorder(new EmptyBorder(1, 1, 1, 1)); + } + }; } /** @@ -113,23 +109,19 @@ class LinuxSwingUI implements SwingUIFactory { @Override public MenuItemUI getItemUI(final JMenuItem jMenuItem, final Entry entry) { - if (menuItemUI == null) { - menuItemUI = new DefaultMenuItemUI(jMenuItem) { - @Override - public - void installUI(final JComponent c) { - super.installUI(c); + return new DefaultMenuItemUI(jMenuItem) { + @Override + public + void installUI(final JComponent c) { + super.installUI(c); - JMenuItem menuItem = (JMenuItem) c; - menuItem.setIconTextGap(8); - // the original is hardcoded to always be 2 (top/bottom). We want this to be larger, so the vertical spacing looks like - // other menus - c.setBorder(new Metal_MenuItemBorder(4)); - } - }; - } - - return menuItemUI; + JMenuItem menuItem = (JMenuItem) c; + menuItem.setIconTextGap(8); + // the original is hardcoded to always be 2 (top/bottom). We want this to be larger, so the vertical spacing looks like + // other menus + c.setBorder(new Metal_MenuItemBorder(4)); + } + }; } /** @@ -142,11 +134,7 @@ class LinuxSwingUI implements SwingUIFactory { @Override public SeparatorUI getSeparatorUI(final JSeparator jSeparator) { - if (separatorUI == null) { - separatorUI = new DefaultSeparatorUI(jSeparator); - } - - return separatorUI; + return new DefaultSeparatorUI(jSeparator); } /** From 2eea54bea3a26597d179e7859e659ea2886836e4 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 16 Jul 2017 14:29:00 +0200 Subject: [PATCH 29/92] Fixed for WindowXP bug: "text/icon/checkmark alignment schemes severely broken". http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4199382 --- .../systemTray/util/WindowsSwingUI.java | 99 +++++++++++++++++-- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/src/dorkbox/systemTray/util/WindowsSwingUI.java b/src/dorkbox/systemTray/util/WindowsSwingUI.java index 988e844..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.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); + } + }; + } } /** From 66324bbf1a02f1779ea5cd1424fcefaed68dfa2b Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 18 Jul 2017 02:26:09 +0200 Subject: [PATCH 30/92] Code polish. Moved shell start logic outside class --- src/dorkbox/systemTray/Tray.java | 29 +++++++++++++------ .../systemTray/gnomeShell/Extension.java | 21 ++++++++------ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/dorkbox/systemTray/Tray.java b/src/dorkbox/systemTray/Tray.java index d99cfcd..089a82a 100644 --- a/src/dorkbox/systemTray/Tray.java +++ b/src/dorkbox/systemTray/Tray.java @@ -36,20 +36,31 @@ class Tray extends Menu { void installExtension() { // do we need to install the GNOME extension?? if (Tray.usingGnome) { + boolean showInfo = false; + if (OSUtil.Linux.isDebian()) { + showInfo = !Extension.isInstalled(); + if (SystemTray.DEBUG) { + SystemTray.logger.debug("Running Debian."); + } + + } if (OSUtil.Linux.isArch()) { + showInfo = !Extension.isInstalled(); 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(); } + + if (showInfo) { + 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."); + return; + } + + // Automatically install the extension for everyone except Arch/Debian. It's bonkers. + Extension.install(); } } diff --git a/src/dorkbox/systemTray/gnomeShell/Extension.java b/src/dorkbox/systemTray/gnomeShell/Extension.java index 1fccee6..81c4c6b 100644 --- a/src/dorkbox/systemTray/gnomeShell/Extension.java +++ b/src/dorkbox/systemTray/gnomeShell/Extension.java @@ -35,6 +35,7 @@ import dorkbox.systemTray.SystemTray; import dorkbox.util.IO; import dorkbox.util.OSUtil; import dorkbox.util.Property; +import dorkbox.util.process.ShellAsyncExecutor; import dorkbox.util.process.ShellExecutor; @SuppressWarnings({"DanglingJavadoc", "WeakerAccess"}) @@ -170,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; } @@ -178,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 ShellExecutor restartShell = new ShellExecutor(); - // 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. } @@ -206,17 +205,21 @@ 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; // should just be 3.14.1 or 3.20 or similar String gnomeVersion = OSUtil.DesktopEnv.getGnomeVersion(); + System.err.println("GNOME VER " + gnomeVersion); if (gnomeVersion == null) { return; } From 03b7cdbcb775b2d4cf2f519d7d9191a74cbae16f Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 18 Jul 2017 16:38:12 +0200 Subject: [PATCH 31/92] Moved swing GTK detection into SwingUtil. Kali linux now supported. --- src/dorkbox/systemTray/SystemTray.java | 59 +++++++++----------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index c3336dd..cb48915 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -59,7 +59,6 @@ import dorkbox.util.Swt; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gtk; import dorkbox.util.jna.linux.GtkEventDispatch; -import sun.security.action.GetPropertyAction; /** @@ -253,7 +252,12 @@ 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.isKali()) { + return selectTypeQuietly(TrayType.GtkStatusIcon); + } if (OSUtil.Linux.isDebian()) { // note: Debian Gnome3 does NOT work! (tested on Debian 8.5 and 8.6 default installs) @@ -452,44 +456,19 @@ class SystemTray { } } else if (isNix) { - // linux/unix can use all of the tray types + // linux/unix can use all of the tray types. 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 + // NOTE: if the UI uses the 'getSystemLookAndFeelClassName' and is on Linux and it's the GtkLookAndFeel, + // this will cause GTK2 to get loaded first, which will cause conflicts if one tries to use GTK3 if (!FORCE_GTK2 && !JavaFX.isLoaded && !Swt.isLoaded) { - - String currentUI = UIManager.getLookAndFeel() - .getClass() - .getName(); - - boolean mustForceGtk2 = false; - - // 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) { + if (SwingUtil.getLoadedGtkVersion() == 2) { // 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"); - } 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."); - - systemTrayMenu = null; - systemTray = null; - return; + // we must use GTK2 because Swing is configured to use GTK2 + FORCE_GTK2 = true; + if (DEBUG) { + logger.debug("Forcing GTK2 because Swing has already loaded GTK2"); } } } @@ -509,7 +488,9 @@ class SystemTray { // we must use GTK2, because SWT is GTK2 FORCE_GTK2 = true; - logger.warn("Forcing GTK2 because SWT is GTK2"); + if (DEBUG) { + logger.debug("Forcing GTK2 because SWT is GTK2"); + } } } else if (JavaFX.isLoaded) { @@ -540,7 +521,9 @@ class SystemTray { // we must use GTK2, because JavaFX is GTK2 FORCE_GTK2 = true; - logger.warn("Forcing GTK2 because JavaFX is GTK2"); + if (DEBUG) { + logger.debug("Forcing GTK2 because JavaFX is GTK2"); + } } } } @@ -714,7 +697,7 @@ class SystemTray { // 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(); } From 1e21d305beba3469d3f1ab0c21a2cdc49a97fbda Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 18 Jul 2017 16:43:13 +0200 Subject: [PATCH 32/92] Commented out test code from the examples --- test/dorkbox/TestTray.java | 3 +-- test/dorkbox/TestTrayJavaFX.java | 3 +-- test/dorkbox/TestTraySwt.java | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 8e8bcb7..4c567eb 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -28,7 +28,6 @@ import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; import dorkbox.util.CacheUtil; import dorkbox.util.Desktop; -import dorkbox.util.SwingUtil; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -62,7 +61,7 @@ class TestTray { TestTray() { 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) + // 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(); diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index d8fcac6..4680a21 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -29,7 +29,6 @@ import dorkbox.util.CacheUtil; import dorkbox.util.Desktop; import dorkbox.util.JavaFX; import dorkbox.util.OS; -import dorkbox.util.SwingUtil; import javafx.application.Application; import javafx.application.Platform; import javafx.event.ActionEvent; @@ -124,7 +123,7 @@ class TestTrayJavaFX { 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) + // 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(); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index e9c405d..3aecbd6 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -33,7 +33,6 @@ import dorkbox.systemTray.Separator; import dorkbox.systemTray.SystemTray; import dorkbox.util.CacheUtil; import dorkbox.util.Desktop; -import dorkbox.util.SwingUtil; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -78,7 +77,7 @@ class TestTraySwt { 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) + // 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(); From 1bfdca63be5ff2806296f7e35511786563b7ca8d Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 18 Jul 2017 17:17:26 +0200 Subject: [PATCH 33/92] Removed debug code --- src/dorkbox/systemTray/gnomeShell/Extension.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dorkbox/systemTray/gnomeShell/Extension.java b/src/dorkbox/systemTray/gnomeShell/Extension.java index 81c4c6b..eedb723 100644 --- a/src/dorkbox/systemTray/gnomeShell/Extension.java +++ b/src/dorkbox/systemTray/gnomeShell/Extension.java @@ -219,7 +219,6 @@ class Extension { // should just be 3.14.1 or 3.20 or similar String gnomeVersion = OSUtil.DesktopEnv.getGnomeVersion(); - System.err.println("GNOME VER " + gnomeVersion); if (gnomeVersion == null) { return; } From d8e9c32e8b3e46a1e2fad8725d89fff6c9112c00 Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 18 Jul 2017 17:19:05 +0200 Subject: [PATCH 34/92] Compatibility matrix is now Linux/Mac/Win, and in alphabetical order. Added Kali linux (also works as root). --- README.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 464c2b0..0b7cfcb 100644 --- a/README.md +++ b/README.md @@ -45,26 +45,30 @@ Problems and Restrictions Compatibility Matrix ------------------ -`✓`=supported, `-`= not supported, `+`= see notes +`✓`=supported, `-`= not supported OS | Java/Swing | JavaFX | SWT --- | --- | --- | --- | -XUbuntu 16.04 | ✓ | ✓ | ✓ | -Ubuntu 16.04 | ✓ | + | ✓ | -UbuntuGnome 16.04 | ✓ | + | ✓ | -UbuntuGnome 17.04 | ✓ | + | ✓ | +Arch Linux + Gnome3 | ✓ | ✓ | ✓ | +Debian 8.5 + Gnome3 | - | - | - | +Debian 8.6 + Gnome3 | - | - | - | +Elementary OS 0.3.2 | - | ✓ | ✓ | +Elementary OS 0.4 | - | ✓ | ✓ | Fedora 23 | ✓ | ✓ | ✓ | Fedora 24 | ✓ | ✓ | ✓ | Fedora 25 | ✓ | ✓ | ✓ | Fedora 25 KDE | ✓ | ✓ | ✓ | -LinuxMint 18 | ✓ | ✓ | ✓ | -Elementary OS 0.3.2 | - | ✓ | ✓ | -Elementary OS 0.4 | - | ✓ | ✓ | -Arch Linux + Gnome3 | ✓ | ✓ | ✓ | FreeBSD 11 + Gnome3 | ✓ | ✓ | + | -Debian 8.5 + Gnome3 | - | - | - | -Debian 8.6 + Gnome3 | - | - | - | -MacOSx | ✓ | + | ✓ | +Kali 2016 | ✓ | ✓ | ✓ | +Kali 2017 | ✓ | ✓ | ✓ | +LinuxMint 18 | ✓ | ✓ | ✓ | +Ubuntu 12.04 | ✓ | ✓ | ✓ | +Ubuntu 14.04 | ✓ | ✓ | ✓ | +Ubuntu 16.04 | ✓ | ✓ | ✓ | +UbuntuGnome 16.04 | ✓ | ✓ | ✓ | +UbuntuGnome 17.04 | ✓ | ✓ | ✓ | +XUbuntu 16.04 | ✓ | ✓ | ✓ | +MacOSx | ✓ | ✓ | ✓ | Win XP | ✓ | ✓ | ✓ | Win 7 | ✓ | ✓ | ✓ | Win 8.1 | ✓ | ✓ | ✓ | @@ -72,7 +76,9 @@ Win 10 | ✓ | ✓ | ✓ | Notes: ------- -- Ubuntu 16.04+ with JavaFX require `libappindicator1` because of JavaFX GTK and indicator panel incompatibilities. See [more details](https://github.com/dorkbox/SystemTray/issues/14#issuecomment-248853532). We attempt to fallback to using Swing in this situation. + - Ubuntu 16.04+ with JavaFX require `libappindicator1` because of JavaFX GTK and indicator panel incompatibilities. See [more details](https://github.com/dorkbox/SystemTray/issues/14#issuecomment-248853532). We attempt to fallback to using Swing in this situation. + + - Ubuntu 17.04+ Java only supports the X11 backend. MIR and Wayland are not supported. - MacOSX JavaFX (Java7) is incompatible with the SystemTray by default. See [issue details](https://bugs.openjdk.java.net/browse/JDK-8116017). - To fix this do one of the following From f996d9e1c1707f12f40e0780e98e0c31dcd2401e Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 19 Jul 2017 00:52:07 +0200 Subject: [PATCH 35/92] Cleaned up how GNOME tray extension is installed. Added support for PREFER_GTK3. Various OS fixes. --- src/dorkbox/systemTray/SystemTray.java | 131 +++++++++++++----- src/dorkbox/systemTray/Tray.java | 34 ----- .../ui/gtk/_GtkStatusIconNativeTray.java | 6 +- 3 files changed, 103 insertions(+), 68 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index cb48915..d2f574e 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -38,6 +38,7 @@ import javax.swing.UIManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import dorkbox.systemTray.gnomeShell.Extension; import dorkbox.systemTray.ui.awt._AwtTray; import dorkbox.systemTray.ui.gtk._AppIndicatorNativeTray; import dorkbox.systemTray.ui.gtk._GtkStatusIconNativeTray; @@ -96,6 +97,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. @@ -259,9 +264,14 @@ class SystemTray { return selectTypeQuietly(TrayType.GtkStatusIcon); } - 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.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); @@ -456,19 +466,27 @@ class SystemTray { } } else if (isNix) { - // linux/unix can use all of the tray types. GTK versions are really sensitive... + // 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 and it's the GtkLookAndFeel, // this will cause GTK2 to get loaded first, which will cause conflicts if one tries to use GTK3 if (!FORCE_GTK2 && !JavaFX.isLoaded && !Swt.isLoaded) { - if (SwingUtil.getLoadedGtkVersion() == 2) { + int loadedGtkVersion = SwingUtil.getLoadedGtkVersion(); + if (loadedGtkVersion == 2) { // 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. - // we must use GTK2 because Swing is configured to use GTK2 + // we must use GTK2 because Java is configured to use GTK2 FORCE_GTK2 = true; if (DEBUG) { - logger.debug("Forcing GTK2 because Swing has already loaded GTK2"); + logger.debug("Forcing GTK2 because Java has already loaded GTK2"); + } + } + else if (loadedGtkVersion == 3 && !PREFER_GTK3) { + PREFER_GTK3 = true; + + if (DEBUG) { + logger.debug("Preferring GTK3 even though specified otherwise, because Java has already loaded GTK3"); } } } @@ -476,15 +494,33 @@ class SystemTray { // 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 (Swt.isGtk3 && 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;`"); + if (Swt.isGtk3) { + if (FORCE_GTK2) { + if (AUTO_FIX_INCONSISTENCIES) { + FORCE_GTK2 = false; - systemTrayMenu = null; - systemTray = null; - return; - } else if (!Swt.isGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { + 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;`"); + } else { + 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; + } + } + + if (!PREFER_GTK3) { + PREFER_GTK3 = true; + + if (DEBUG) { + logger.debug("Preferring GTK3 even though specified otherwise, because SWT is GTK3"); + } + } + } else if (!FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { // we must use GTK2, because SWT is GTK2 FORCE_GTK2 = true; @@ -500,24 +536,36 @@ class SystemTray { // 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 (JavaFX.isGtk3 && 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 (JavaFX.isGtk3) { + if (FORCE_GTK2) { + // if we are java9, then we can change it -- otherwise we cannot. + if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) { + 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`."); + 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`."); - } 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."); + } + 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."); - systemTrayMenu = null; - systemTray = null; - return; + systemTrayMenu = null; + systemTray = null; + return; + } } - } else if (!JavaFX.isGtk3 && !FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { + + if (!PREFER_GTK3) { + PREFER_GTK3 = true; + + if (DEBUG) { + logger.debug("Preferring GTK3 even though specified otherwise, because JavaFX is GTK3"); + } + } + } + else if (!FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { // we must use GTK2, because JavaFX is GTK2 FORCE_GTK2 = true; @@ -550,7 +598,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: {}", FORCE_GTK2); } // Note: AppIndicators DO NOT support tooltips. We could try to create one, by creating a GTK widget and attaching it on @@ -567,7 +616,7 @@ class SystemTray { } - // fix various incompatibilities + // 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)) { @@ -599,6 +648,15 @@ class SystemTray { 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()) { @@ -637,7 +695,7 @@ class SystemTray { if (isNix) { // linux/unix need access to GTK, so load it up before the tray is loaded! - GtkEventDispatch.startGui(FORCE_GTK2, DEBUG); + GtkEventDispatch.startGui(FORCE_GTK2, PREFER_GTK3, DEBUG); GtkEventDispatch.waitForEventsToComplete(); if (DEBUG) { @@ -672,6 +730,15 @@ class SystemTray { "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)) { diff --git a/src/dorkbox/systemTray/Tray.java b/src/dorkbox/systemTray/Tray.java index 089a82a..834b156 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,38 +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) { - boolean showInfo = false; - if (OSUtil.Linux.isDebian()) { - showInfo = !Extension.isInstalled(); - if (SystemTray.DEBUG) { - SystemTray.logger.debug("Running Debian."); - } - - } - if (OSUtil.Linux.isArch()) { - showInfo = !Extension.isInstalled(); - if (SystemTray.DEBUG) { - SystemTray.logger.debug("Running Arch Linux."); - } - } - - if (showInfo) { - 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."); - return; - } - - // Automatically install the extension for everyone except Arch/Debian. 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 diff --git a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java index c35342d..213c6af 100644 --- a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java @@ -202,8 +202,10 @@ class _GtkStatusIconNativeTray extends Tray { bind(gtkMenu, null, systemTray); - // do we need to install the GNOME extension?? - Tray.installExtension(); + // install the Gnome extension + if (Tray.usingGnome) { + Extension.install(); + } } @Override From 8a96bfb7fd2c725f11cef3e7c4f4b106aa4e6a44 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 19 Jul 2017 00:57:46 +0200 Subject: [PATCH 36/92] Updated Readme --- README.md | 101 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 0b7cfcb..da8dac1 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,26 @@ SystemTray Professional, cross-platform **SystemTray** support for *Swing/AWT*, *GtkStatusIcon*, and *AppIndicator* system-tray types for java applications on Java 6+. -This library provides **OS Native** menus and **Swing/AWT** menus, depending on the OS and Desktop Environment, and if AutoDetect (the default) is set. Linux/Unix will automatically choose *Native* (*GtkStatusIcon*, and *AppIndicator*) menus, Windows will choose *Swing*, and MacOS will choose *AWT*. - - Please note that the *Native* adn *AWT* menus follow the specified look and feel of that OS and are limited by what is supported on the OS. Consequently they are not consistent across all platforms and environments. +This library provides **OS Native** menus and **Swing/AWT** menus, depending on the OS and Desktop Environment and if AutoDetect (the default) is enabled. + + - Linux/Unix will automatically choose *Native* (*GtkStatusIcon*, and *AppIndicator*) menus, Windows will choose *Swing*, and MacOS will choose *AWT*. + + - Please note that the *Native* and *AWT* menus follow the specified look and feel of that OS and are limited by what is supported on the OS. Consequently they are not consistent across all platforms and environments. + + - In most cases on Linux/Unix, *Native* menus are used. In cases where libraries are missing or there are un-resolvable GTK version conflicts, we try to fallback to using *Swing*.   The following unique problems are also solved by this library: 1. *Sun/Oracle* system-tray icons on gnu/linux **do not** support images with transparent backgrounds - 2. *Sun/Oracle* system-tray and *SWT* system-tray implementations **do not** support app-indicators, which are necessary on different distributions of gnu/linux and unix - 3. *Sun/Oracle* system-tray menus on Windows **look absolutely horrid** - 4. *Sun/Oracle* system-tray icons on Windows are **hard-coded** to a max size of 24x24 (it was last updated in *2006*) - 5. *Sun/Oracle* system-tray menus on MacOS **do not** always respond to both mouse buttons, where Apple menus do - 6. Windows *native* menus **do not** support images attached to menu entries - 7. Windows menus **do not** support a different L&F from the running application + 1. *Sun/Oracle* system-tray and *SWT* system-tray implementations **do not** support app-indicators, which are necessary on different distributions of gnu/linux and unix + 1. *GtkStatusIcons* on GNOME3 desktop environments are hidden by default + 1. *Sun/Oracle* system-tray menus on Windows **look absolutely horrid** + 1. *Sun/Oracle* system-tray icons on Windows are **hard-coded** to a max size of 24x24 (it was last updated in *2006*) + 1. *Sun/Oracle* system-tray menus on MacOS **do not** always respond to both mouse buttons, where Apple menus do + 1. Windows *native* menus **do not** support images attached to menu entries + 1. Windows menus **do not** support a different L&F from the running application + 1. Windows, Linux, and MacOSX menus (native or otherwise) do not support HiDPI configurations @@ -31,7 +38,7 @@ Problems and Restrictions - **SWT** can use *GTK2* or *GTK3*. If you want to use *GTK2* you must force SWT into *GTK2 mode* via `System.setProperty("SWT_GTK3", "0");` before SWT is initialized and only if there are problems with the autodetection, you can also set `SystemTray.FORCE_GTK2=true;`. - - **AppIndicators** under Ubuntu 16.04 (and possibly other distro's) **will not** work as a different user (ie: as a sudo'd user to `root`), since AppIndicators require a dbus connection to the current user's window manager -- and this cannot happen between different user accounts. We attempt to detect this, and fallback to using Swing. + - **AppIndicators** under Ubuntu 16.04 (and possibly other distro's) **will not** work as a different user (ie: as a sudo'd user to `root`), since AppIndicators require a dbus connection to the current user's window manager -- and this cannot happen between different user accounts. We attempt to detect this and fallback to using Swing. - **MacOSX** is a *special snowflake* in how it handles GUI events, and so there are some bizzaro combinations of SWT, JavaFX, and Swing that do not work together (see the `Notes` below for the details.) @@ -43,43 +50,57 @@ Problems and Restrictions - **Linux/Unix Menus** Some linux environments only support right-click to display the menu, and it is not possible to change the behavior. -Compatibility Matrix +AutoDetect Compatibility List ------------------ -`✓`=supported, `-`= not supported - -OS | Java/Swing | JavaFX | SWT ---- | --- | --- | --- | -Arch Linux + Gnome3 | ✓ | ✓ | ✓ | -Debian 8.5 + Gnome3 | - | - | - | -Debian 8.6 + Gnome3 | - | - | - | -Elementary OS 0.3.2 | - | ✓ | ✓ | -Elementary OS 0.4 | - | ✓ | ✓ | -Fedora 23 | ✓ | ✓ | ✓ | -Fedora 24 | ✓ | ✓ | ✓ | -Fedora 25 | ✓ | ✓ | ✓ | -Fedora 25 KDE | ✓ | ✓ | ✓ | -FreeBSD 11 + Gnome3 | ✓ | ✓ | + | -Kali 2016 | ✓ | ✓ | ✓ | -Kali 2017 | ✓ | ✓ | ✓ | -LinuxMint 18 | ✓ | ✓ | ✓ | -Ubuntu 12.04 | ✓ | ✓ | ✓ | -Ubuntu 14.04 | ✓ | ✓ | ✓ | -Ubuntu 16.04 | ✓ | ✓ | ✓ | -UbuntuGnome 16.04 | ✓ | ✓ | ✓ | -UbuntuGnome 17.04 | ✓ | ✓ | ✓ | -XUbuntu 16.04 | ✓ | ✓ | ✓ | -MacOSx | ✓ | ✓ | ✓ | -Win XP | ✓ | ✓ | ✓ | -Win 7 | ✓ | ✓ | ✓ | -Win 8.1 | ✓ | ✓ | ✓ | -Win 10 | ✓ | ✓ | ✓ | + +OS | Supported +--- | --- | +Arch Linux + Gnome3 | ✓ | + | | +Debian 8.5 + Gnome3 | ✓ | +Debian 8.6 + Gnome3 | ✓ | + | | +Elementary OS 0.3.2 | ✓ | +Elementary OS 0.4 | ✓ | + | | +Fedora 23 | ✓ | +Fedora 24 | ✓ | +Fedora 25 | ✓ | +Fedora 25 KDE | ✓ | + | | +FreeBSD 11 + Gnome3 | ✓ | + | | +Kali 2016 | ✓ | +Kali 2017 | ✓ | + | | +LinuxMint 18 | ✓ | + | | +Ubuntu 12.04 | ✓ | +Ubuntu 14.04 | ✓ | +Ubuntu 16.04 | ✓ | +Ubuntu 17.04 | ✓ | +UbuntuGnome 16.04 | ✓ | +UbuntuGnome 17.04 | ✓ | + | | +XUbuntu 16.04 | ✓ | + | | +MacOSx | ✓ | + | | +Windows XP | ✓ | +Windows 7 | ✓ | +Windows 8.1 | ✓ | +Windows 10 | ✓ | Notes: ------- + - The compatibility list only applies while the SystemTray is in `AutoDetect` mode. Not all OSes support forcing a custom tray type. + - Ubuntu 16.04+ with JavaFX require `libappindicator1` because of JavaFX GTK and indicator panel incompatibilities. See [more details](https://github.com/dorkbox/SystemTray/issues/14#issuecomment-248853532). We attempt to fallback to using Swing in this situation. - Ubuntu 17.04+ Java only supports the X11 backend. MIR and Wayland are not supported. + - Debian + GNOME 3, SystemTray works, but will only show in a tray via pressing SUPER+M. + - MacOSX JavaFX (Java7) is incompatible with the SystemTray by default. See [issue details](https://bugs.openjdk.java.net/browse/JDK-8116017). - To fix this do one of the following - Upgrade to Java 8 @@ -102,6 +123,10 @@ SystemTray.AUTO_SIZE (type boolean, default value 'true') SystemTray.FORCE_GTK2 (type boolean, default value 'false') - Forces the system tray to always choose GTK2 (even when GTK3 might be available). + +SystemTray.PREFER_GTK3 (type boolean, default value 'true') + - Prefer to load GTK3 before trying to load GTK2. + SystemTray.FORCE_TRAY_TYPE (type SystemTray.TrayType, default value 'AutoDetect') - Forces the system tray detection to be AutoDetect, GtkStatusIcon, AppIndicator, Swing, or AWT. From 8c15c251761dfebcd58916a4a0480dfea9082a59 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 19 Jul 2017 01:08:39 +0200 Subject: [PATCH 37/92] Now using "Version" class, which permits version betas/builds and comparisons. --- src/dorkbox/systemTray/SystemTray.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index d2f574e..7ca6f4a 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -57,6 +57,7 @@ import dorkbox.util.OSUtil; import dorkbox.util.Property; import dorkbox.util.SwingUtil; import dorkbox.util.Swt; +import dorkbox.util.Version; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gtk; import dorkbox.util.jna.linux.GtkEventDispatch; @@ -902,8 +903,8 @@ class SystemTray { * Gets the version number. */ public static - String getVersion() { - return "3.1"; + Version getVersion() { + return new Version("3.1"); } /** From e97e8922a19f7c80bfa7d5ae481c1423f1e75860 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 20 Jul 2017 23:59:15 +0200 Subject: [PATCH 38/92] Updated SystemTray description and license info for QZTray. --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index b1acbbf..71476df 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ - Dorkbox SystemTray - Apache 2.0 License https://github.com/dorkbox Copyright 2014, dorkbox, llc - Cross-platform SystemTray, GtkStatusIcon, and AppIndicator support for applications for Java 6+ + Cross-platform SystemTray support for Swing/AWT, GtkStatusIcon, and AppIndicator on Java 6+ - Dorkbox Utils - Apache 2.0 License diff --git a/README.md b/README.md index da8dac1..6be10b1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ SystemTray ========== -Professional, cross-platform **SystemTray** support for *Swing/AWT*, *GtkStatusIcon*, and *AppIndicator* system-tray types for java applications on Java 6+. +Professional, cross-platform **SystemTray** support for *Swing/AWT*, *GtkStatusIcon*, and *AppIndicator* on Java 6+. This library provides **OS Native** menus and **Swing/AWT** menus, depending on the OS and Desktop Environment and if AutoDetect (the default) is enabled. From ac9b52caea2dcdde6c1727190c8df56ed5967ec2 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 22 Jul 2017 13:51:32 +0200 Subject: [PATCH 39/92] Removed Windows Native implementation. It cannot be themed by Windows, so it looks like AWT. --- .../systemTray/ui/_WindowsNativeTray.java | 268 ------------------ 1 file changed, 268 deletions(-) delete mode 100644 src/dorkbox/systemTray/ui/_WindowsNativeTray.java diff --git a/src/dorkbox/systemTray/ui/_WindowsNativeTray.java b/src/dorkbox/systemTray/ui/_WindowsNativeTray.java deleted file mode 100644 index c617acd..0000000 --- a/src/dorkbox/systemTray/ui/_WindowsNativeTray.java +++ /dev/null @@ -1,268 +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.ui; - -import static com.sun.jna.platform.win32.WinDef.HWND; -import static com.sun.jna.platform.win32.WinDef.LPARAM; -import static com.sun.jna.platform.win32.WinDef.POINT; -import static com.sun.jna.platform.win32.WinDef.WPARAM; -import static com.sun.jna.platform.win32.WinUser.WM_QUIT; -import static dorkbox.util.jna.windows.Shell32.NIM_ADD; -import static dorkbox.util.jna.windows.Shell32.NIM_DELETE; -import static dorkbox.util.jna.windows.Shell32.NIM_MODIFY; -import static dorkbox.util.jna.windows.Shell32.Shell_NotifyIcon; -import static dorkbox.util.jna.windows.User32.User32; -import static dorkbox.util.jna.windows.User32.WM_LBUTTONUP; -import static dorkbox.util.jna.windows.User32.WM_RBUTTONUP; -import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_SHELLNOTIFY; -import static dorkbox.util.jna.windows.WindowsEventDispatch.WM_TASKBARCREATED; - -import java.io.File; - -import javax.swing.ImageIcon; - -import dorkbox.systemTray.SystemTray; -import dorkbox.systemTray.Tray; -import dorkbox.util.ImageUtil; -import dorkbox.util.jna.windows.HBITMAPWrap; -import dorkbox.util.jna.windows.HICONWrap; -import dorkbox.util.jna.windows.Kernel32; -import dorkbox.util.jna.windows.Listener; -import dorkbox.util.jna.windows.Shell32; -import dorkbox.util.jna.windows.WindowsEventDispatch; -import dorkbox.util.jna.windows.structs.NOTIFYICONDATA; - - -/** - * Native implementation of a System tray on Windows. - */ -class _WindowsNativeTray extends Tray { - private final Listener quitListener; - private final Listener menuListener; - - // is the system tray visible or not. - private volatile boolean visible = true; - - private volatile File imageFile; - private volatile HICONWrap imageIcon; - - private volatile String tooltipText = ""; - private final Listener showListener; - - private _WindowsNativeTray(final dorkbox.systemTray.SystemTray systemTray) { - super(); - - // we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization. - - // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks - // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). -// final WindowsMenu windowsMenu = new WindowsMenu() { -// @Override -// public -// void setEnabled(final MenuItem menuItem) { -// boolean enabled = menuItem.getEnabled(); -// -// if (visible && !enabled) { -// // hide -// hide(); -// } -// else if (!visible && enabled) { -// // show -// show(); -// } -// } -// -// @Override -// public -// void setImage(final MenuItem menuItem) { -// imageFile = menuItem.getImage(); -// -// if (imageIcon != null) { -// imageIcon.close(); -// } -// imageIcon = convertImage(imageFile); -// -// NOTIFYICONDATA nid = new NOTIFYICONDATA(); -// nid.hWnd = WindowsEventDispatch.get(); -// nid.setIcon(imageIcon); -// -// if (!Shell32.Shell_NotifyIcon(NIM_MODIFY, nid)) { -// SystemTray.logger.error("Error setting the image for the tray. {}", Kernel32.getLastErrorMessage()); -// } -// } -// -// @Override -// public -// void setText(final MenuItem menuItem) { -// // no op. -// } -// -// @Override -// public -// void setShortcut(final MenuItem menuItem) { -// // no op -// } -// -// @Override -// public -// void remove() { -// hide(); -// -// super.remove(); -// -// User32.IMPL.PostMessage(WindowsEventDispatch.get(), WM_QUIT, new WPARAM(0), new LPARAM(0)); -// } -// }; - - // will wait until it's started up. - WindowsEventDispatch.start(); - - HWND hWnd = WindowsEventDispatch.get(); - if (hWnd == null) { - throw new RuntimeException("The Windows System Tray is not supported! Please write an issue and include your OS type and " + - "configuration"); - } - - showListener = new Listener() { - @Override - public - void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) { - show(); - } - }; - quitListener = new Listener() { - @Override - public - void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) { - WindowsEventDispatch.stop(); - WindowsEventDispatch.removeListener(showListener); - WindowsEventDispatch.removeListener(quitListener); - WindowsEventDispatch.removeListener(menuListener); - } - }; - - - menuListener = new Listener() { - final POINT mousePosition = new POINT(); - - @Override - public - void run(final HWND hWnd, final WPARAM wParam, final LPARAM lParam) { - int lp = lParam.intValue(); - switch (lp) { - case WM_LBUTTONUP: - if (User32.GetCursorPos(mousePosition)) { - // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks - // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). - // windowsMenu.showContextMenu(mousePosition); - } - break; - case WM_RBUTTONUP: - if (User32.GetCursorPos(mousePosition)) { - // this should be a swing menu, not a "native" menu, which will not be themed to match the OS style. It looks - // marginally better than AWT. This was verified via a completely native menu system (not the AWT menu system). - // windowsMenu.showContextMenu(mousePosition); - } - break; - } - } - }; - - WindowsEventDispatch.addListener(WM_TASKBARCREATED, showListener); - WindowsEventDispatch.addListener(WM_QUIT, quitListener); - WindowsEventDispatch.addListener(WM_SHELLNOTIFY, menuListener); - - show(); - -// bind(windowsMenu, null, systemTray); - } - -// public synchronized void balloon (String title, String message, int millis) { -// balloonNotifyIconData.hWnd = this.windowNotifyIconData.hWnd; -// balloonNotifyIconData.uID = this.windowNotifyIconData.uID; -// balloonNotifyIconData.setBalloon(title, message, millis, NIIF_NONE); -// Shell_NotifyIcon(NIM_MODIFY, balloonNotifyIconData); -// } - - - private void hide() { - if (imageIcon != null) { - imageIcon.close(); - imageIcon = null; - } - - NOTIFYICONDATA nid = new NOTIFYICONDATA(); - nid.hWnd = WindowsEventDispatch.get(); - - if (!Shell32.Shell_NotifyIcon(NIM_DELETE, nid)) { - SystemTray.logger.error("Error hiding tray. {}", Kernel32.getLastErrorMessage()); - } - visible = false; - } - - private void show() { - if (imageIcon != null) { - imageIcon.close(); - } - imageIcon = convertImage(imageFile); - - NOTIFYICONDATA nid = new NOTIFYICONDATA(); - nid.hWnd = WindowsEventDispatch.get(); - nid.setTooltip(tooltipText); - nid.setIcon(imageIcon); - nid.setCallback(WM_SHELLNOTIFY); - - if (!Shell_NotifyIcon(NIM_ADD, nid)) { - SystemTray.logger.error("Error showing tray. {}", Kernel32.getLastErrorMessage()); - } - visible = true; - } - - @Override - protected - void setTooltip_(final String tooltipText) { - if (this.tooltipText.equals(tooltipText)){ - return; - } - this.tooltipText = tooltipText; - - NOTIFYICONDATA nid = new NOTIFYICONDATA(); - nid.hWnd = WindowsEventDispatch.get(); - nid.setTooltip(tooltipText); - - Shell_NotifyIcon(NIM_MODIFY, nid); - } - - @Override - public - boolean hasImage() { - return imageFile != null; - } - - private static - HICONWrap convertImage(final File imageFile) { - if (imageFile != null) { - ImageIcon imageIcon = new ImageIcon(imageFile.getAbsolutePath()); - // fully loads the image and returns when it's done loading the image - imageIcon = new ImageIcon(imageIcon.getImage()); - - HBITMAPWrap hbitmapTrayIcon = new HBITMAPWrap(ImageUtil.getBufferedImage(imageIcon)); - return new HICONWrap(hbitmapTrayIcon); - } - - return null; - } -} From 73512abcef777e217158a2a10280d7bd9b275489 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 22 Jul 2017 13:52:08 +0200 Subject: [PATCH 40/92] Updated license info from QZTray --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 71476df..13f51fa 100644 --- a/LICENSE +++ b/LICENSE @@ -22,7 +22,7 @@ - QZTray - Apache 2.0 License https://github.com/tresf/tray/blob/dorkbox/src/qz/utils/ShellUtilities.java Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC - Partial code released as Apache 2.0 for exclusive use by dorkbox, llc + Partial code released as Apache 2.0 for use in the SystemTray project by dorkbox, llc. Used with permission. - SLF4J - MIT License From 4ceef63fc8f6400ab95844b54d504137cda87aa4 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 23 Jul 2017 01:48:37 +0200 Subject: [PATCH 41/92] Added jna_platform --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6be10b1..9b601d2 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,7 @@ https://oss.sonatype.org/content/repositories/releases/com/dorkbox/SystemTray/ https://repo1.maven.org/maven2/net/java/dev/jna/jna/ +https://repo1.maven.org/maven2/net/java/dev/jna/jna-platform/ https://repo1.maven.org/maven2/org/slf4j/slf4j-api/ https://repo1.maven.org/maven2/org/javassist/javassist/ From ec0f4c911c385158c93281604194d485a334ee36 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 23 Jul 2017 11:24:09 +0200 Subject: [PATCH 42/92] Refactored checking for which GTK version is loaded. The logic has been simplified. --- src/dorkbox/systemTray/SystemTray.java | 233 ++++++++++++++++--------- 1 file changed, 152 insertions(+), 81 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 7ca6f4a..a8e2c41 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -60,6 +60,7 @@ import dorkbox.util.Swt; import dorkbox.util.Version; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gtk; +import dorkbox.util.jna.linux.GtkCheck; import dorkbox.util.jna.linux.GtkEventDispatch; @@ -469,41 +470,162 @@ class SystemTray { else if (isNix) { // 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 and it's the GtkLookAndFeel, - // this will cause GTK2 to get loaded first, which will cause conflicts if one tries to use GTK3 - if (!FORCE_GTK2 && !JavaFX.isLoaded && !Swt.isLoaded) { - int loadedGtkVersion = SwingUtil.getLoadedGtkVersion(); - if (loadedGtkVersion == 2) { - // 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. + // 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+. - // 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"); + // 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"); + + // 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. + + // 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 { + // we are already forcing GTK2, so no extra actions necessary } - } - else if (loadedGtkVersion == 3 && !PREFER_GTK3) { - PREFER_GTK3 = true; + } else { + // !AUTO_FIX_INCONSISTENCIES - if (DEBUG) { - logger.debug("Preferring GTK3 even though specified otherwise, because Java has already loaded GTK3"); + 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; + 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"); + 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+. - if (Swt.isGtk3) { - if (FORCE_GTK2) { - if (AUTO_FIX_INCONSISTENCIES) { + 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 (!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"); + + 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;`"); - } else { + } + + 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. + + 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;`"); @@ -514,64 +636,13 @@ class SystemTray { } } - if (!PREFER_GTK3) { - PREFER_GTK3 = true; + 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."); - if (DEBUG) { - logger.debug("Preferring GTK3 even though specified otherwise, because SWT is GTK3"); - } - } - } else if (!FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { - // we must use GTK2, because SWT is GTK2 - FORCE_GTK2 = true; - - if (DEBUG) { - logger.debug("Forcing GTK2 because SWT is GTK2"); - } - } - } - else 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 (JavaFX.isGtk3) { - if (FORCE_GTK2) { - // if we are java9, then we can change it -- otherwise we cannot. - if (OS.javaVersion == 9 && AUTO_FIX_INCONSISTENCIES) { - 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`."); - - } - 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."); - - systemTrayMenu = null; - systemTray = null; - return; - } - } - - if (!PREFER_GTK3) { - PREFER_GTK3 = true; - - if (DEBUG) { - logger.debug("Preferring GTK3 even though specified otherwise, because JavaFX is GTK3"); - } - } - } - else if (!FORCE_GTK2 && AUTO_FIX_INCONSISTENCIES) { - // we must use GTK2, because JavaFX is GTK2 - FORCE_GTK2 = true; - - if (DEBUG) { - logger.debug("Forcing GTK2 because JavaFX is GTK2"); + systemTrayMenu = null; + systemTray = null; + return; } } } From 210e423b5fd3a1f5dfd95c11ce5e63d0c85f3df8 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 23 Jul 2017 15:30:36 +0200 Subject: [PATCH 43/92] Added ChromeOS to detection. It does not work as there are no libraries supporting it, and Swing cannot create a native peer via X11. --- README.md | 2 ++ src/dorkbox/systemTray/SystemTray.java | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b601d2..be3d198 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ OS | Supported --- | --- | Arch Linux + Gnome3 | ✓ | | | +ChromeOS | - | + | | Debian 8.5 + Gnome3 | ✓ | Debian 8.6 + Gnome3 | ✓ | | | diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index a8e2c41..c5fb8ab 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -328,6 +328,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) @@ -687,6 +693,14 @@ class SystemTray { trayType = selectTypeQuietly(SystemTray.FORCE_TRAY_TYPE); } + if (trayType == null && OSUtil.DesktopEnv.isChromeOS()) { + logger.error("ChromeOS detected and it is not supported. Aborting."); + + systemTrayMenu = null; + systemTray = null; + return; + } + // fix various incompatibilities with selected tray types if (isNix) { @@ -765,7 +779,7 @@ class SystemTray { // 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) { + if (isNix && !isTrayType(trayType, TrayType.Swing)) { // linux/unix need access to GTK, so load it up before the tray is loaded! GtkEventDispatch.startGui(FORCE_GTK2, PREFER_GTK3, DEBUG); GtkEventDispatch.waitForEventsToComplete(); From eb09c39ec49d934660c4f0837e7fbfef3f76ca62 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 23 Jul 2017 15:35:45 +0200 Subject: [PATCH 44/92] Code polish --- src/dorkbox/systemTray/SystemTray.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index c5fb8ab..96d158d 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -397,8 +397,6 @@ class SystemTray { return; } - systemTray = new SystemTray(); - // if (DEBUG) { // Properties properties = System.getProperties(); // for (Map.Entry entry : properties.entrySet()) { @@ -899,6 +897,7 @@ 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 (JavaFX.isLoaded || Swt.isLoaded || From 365645ecd3277d6783a21f99c48da2533277a30e Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 23 Jul 2017 18:02:50 +0200 Subject: [PATCH 45/92] Updated POM information to include "dependencies", since it is easy to incorrectly add it as a dependency to ones own project. --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index be3d198..74b97ba 100644 --- a/README.md +++ b/README.md @@ -258,11 +258,14 @@ This project includes some utility classes that are a small subset of a much lar Maven Info --------- ```` - - com.dorkbox - SystemTray - 3.1 - + + ... + + com.dorkbox + SystemTray + 3.1 + + ```` From 65e7c669b5f6cf592246af46fa204089e193ea30 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 24 Jul 2017 14:32:58 +0200 Subject: [PATCH 46/92] Added Menu Entry tooltip support. Emits warning on first load of tooltips (as they are not supported by all OS configurations). This warning can be suppressed. Misc fix for loading swing with GTK. --- src/dorkbox/systemTray/MenuItem.java | 45 ++++++++ src/dorkbox/systemTray/SystemTray.java | 101 +++++++++--------- src/dorkbox/systemTray/Tray.java | 26 ----- src/dorkbox/systemTray/peer/MenuItemPeer.java | 2 + src/dorkbox/systemTray/ui/awt/AwtMenu.java | 12 +++ .../systemTray/ui/awt/AwtMenuItem.java | 7 ++ src/dorkbox/systemTray/ui/awt/_AwtTray.java | 50 ++++----- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 6 ++ .../systemTray/ui/gtk/GtkMenuItem.java | 14 +++ .../ui/gtk/_AppIndicatorNativeTray.java | 12 +-- .../ui/gtk/_GtkStatusIconNativeTray.java | 38 ++++--- .../systemTray/ui/swing/SwingMenu.java | 6 ++ .../systemTray/ui/swing/SwingMenuItem.java | 12 +++ .../systemTray/ui/swing/_SwingTray.java | 46 ++++---- test/dorkbox/TestTray.java | 2 + test/dorkbox/TestTrayJavaFX.java | 2 + test/dorkbox/TestTraySwt.java | 2 + 17 files changed, 238 insertions(+), 145 deletions(-) diff --git a/src/dorkbox/systemTray/MenuItem.java b/src/dorkbox/systemTray/MenuItem.java index a0bc7be..29737bb 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,6 +347,47 @@ class MenuItem extends Entry { } } + /** + * 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 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."); + } + } + + 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; + } + @Override public void remove() { diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 96d158d..89faa7b 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -675,7 +675,7 @@ class SystemTray { logger.debug("Forced tray type: {}", FORCE_TRAY_TYPE.name()); } logger.debug("Force GTK2: {}", FORCE_GTK2); - logger.debug("Prefer GTK3: {}", 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 @@ -777,8 +777,9 @@ class SystemTray { // 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 && !isTrayType(trayType, TrayType.Swing)) { + if (isNix) { // 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(); @@ -788,65 +789,65 @@ class SystemTray { logger.debug("Is the system already running GTK? {}", Gtk.alreadyRunningGTK); } - // NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3. - // appindicator3 doesn't support menu icons via GTK2!! - if (!Gtk.isLoaded) { - trayType = selectTypeQuietly(TrayType.Swing); - - 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) + 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 (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 (!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."); - } - } } - - // have to make adjustments BEFORE the tray/menu image size calculations if (AUTO_FIX_INCONSISTENCIES && isTrayType(trayType, TrayType.Swing) && SystemTray.SWING_UI == null) { if (isNix) { diff --git a/src/dorkbox/systemTray/Tray.java b/src/dorkbox/systemTray/Tray.java index 834b156..f0d36af 100644 --- a/src/dorkbox/systemTray/Tray.java +++ b/src/dorkbox/systemTray/Tray.java @@ -90,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/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/ui/awt/AwtMenu.java b/src/dorkbox/systemTray/ui/awt/AwtMenu.java index d9e112a..df7d527 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenu.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenu.java @@ -136,6 +136,18 @@ class AwtMenu implements MenuPeer { }); } + @Override + public + void setTooltip(final MenuItem menuItem) { + SwingUtil.invokeLater(new Runnable() { + @Override + public + void run() { + _native.setLabel(menuItem.getTooltip()); + } + }); + } + @Override public void remove() { diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java index 99018c7..384a8eb 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java @@ -19,6 +19,7 @@ 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.util.SwingUtil; @@ -114,6 +115,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/ui/awt/_AwtTray.java b/src/dorkbox/systemTray/ui/awt/_AwtTray.java index 673e8dd..883233c 100644 --- a/src/dorkbox/systemTray/ui/awt/_AwtTray.java +++ b/src/dorkbox/systemTray/ui/awt/_AwtTray.java @@ -182,6 +182,31 @@ class _AwtTray extends Tray { // 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 { 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/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index 9098756..308e74d 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -346,6 +346,12 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { setText(menuItem); } + @Override + public + void setTooltip(final MenuItem menuItem) { + + } + /** * called when a child removes itself from the parent menu. Does not work for sub-menus * diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index 8e76ec9..a23a9b7 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java @@ -167,6 +167,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 diff --git a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java index 920a15f..95bb737 100644 --- a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java @@ -186,6 +186,12 @@ class _AppIndicatorNativeTray extends Tray { // 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() { @@ -231,12 +237,6 @@ class _AppIndicatorNativeTray extends Tray { 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/ui/gtk/_GtkStatusIconNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java index 213c6af..ee4b95f 100644 --- a/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_GtkStatusIconNativeTray.java @@ -123,6 +123,27 @@ class _GtkStatusIconNativeTray extends Tray { // 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() { @@ -208,23 +229,6 @@ class _GtkStatusIconNativeTray extends Tray { } } - @Override - protected - void setTooltip_(final String tooltipText) { - if (this.tooltipText.equals(tooltipText)){ - return; - } - this.tooltipText = tooltipText; - - GtkEventDispatch.dispatch(new Runnable() { - @Override - public - void run() { - Gtk2.gtk_status_icon_set_tooltip_text(trayIcon, tooltipText); - } - }); - } - @Override public boolean hasImage() { diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenu.java b/src/dorkbox/systemTray/ui/swing/SwingMenu.java index 6d4ece1..59350ea 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenu.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenu.java @@ -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/ui/swing/SwingMenuItem.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java index eac0de2..1e771f5 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java @@ -166,6 +166,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/ui/swing/_SwingTray.java b/src/dorkbox/systemTray/ui/swing/_SwingTray.java index 2e6374f..0255957 100644 --- a/src/dorkbox/systemTray/ui/swing/_SwingTray.java +++ b/src/dorkbox/systemTray/ui/swing/_SwingTray.java @@ -167,6 +167,31 @@ class _SwingTray extends Tray { // 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 { 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/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 4c567eb..b28ff41 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -102,12 +102,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); diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index 4680a21..de3c643 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -164,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); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index 3aecbd6..75b9141 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -118,12 +118,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); From 64bd95d86171c9c84a85ef1d6c74958a47bcff95 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 24 Jul 2017 14:37:58 +0200 Subject: [PATCH 47/92] Added GtkMenu support for tooltips. --- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index 308e74d..62951cb 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -349,7 +349,15 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { @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()); + } + }); } /** From 8bc6e60008a3f4e6865ca4ca69d064409b50bf66 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 24 Jul 2017 14:42:04 +0200 Subject: [PATCH 48/92] Updated readme to reflect tooltip info --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74b97ba..f6f894b 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Problems and Restrictions - **Gnome3** (Fedora, Manjaro, Arch, etc) environments by default **do not** allow the SystemTray icon to be shown. This has been worked around (it will be placed next to the clock) for most Gnome environments, except for Arch linux. Another workaround is to install the [Top Icons plugin](https://extensions.gnome.org/extension/1031/topicons/) plugin 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. - - **ToolTips** The maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop Environments. Please note that **Ubuntu** does not always support this! + - **ToolTips** The maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop Environments. Specifically, Swing and GtkStatusIcon types support tray tooltips and menu tooltips. AWT and AppIndicator types do not support tooltips of any kind. Please note that **Ubuntu** uses AppIndicators! - **Linux/Unix Menus** Some linux environments only support right-click to display the menu, and it is not possible to change the behavior. From 4bfd62bf857e27c36469884923efa6358ebdcd88 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 24 Jul 2017 14:54:13 +0200 Subject: [PATCH 49/92] Fixed typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6f894b..7c6e958 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Extension.ENABLE_SHELL_RESTART (type boolean, default value 'true') - Permit the gnome-shell to be restarted when the extension is installed. -Extension.SHELL_RESTART_COMMAND (type String, default value 'nome-shell --replace &') +Extension.SHELL_RESTART_COMMAND (type String, default value 'gnome-shell --replace &') - Command to restart the gnome-shell. It is recommended to start it in the background (hence '&') From a1d24d85c980b6e6cb9b9f4e3571ea393b518c6b Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 24 Jul 2017 14:58:56 +0200 Subject: [PATCH 50/92] Updated readme for info about swing+linux --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7c6e958..d9fe6b5 100644 --- a/README.md +++ b/README.md @@ -216,12 +216,10 @@ Note: Gnome-shell users can install an extension to support placing the tray ico is initially hidden. ```` ```` -Note: AppIndicator environments (mostly, just Ubuntu) might notice the menu - getting constructed (it starts out small, then fills the space). Sometimes - even the small menu will get stuck, and be slightly visible behind the - larger menu. - If this happens to you, please let us know in an issue, with detailed - system info, please! +Note: We have fixed the Swing notificat tray on Linux (it no longer has a greyish background), however + to facilitate this, a screen-shot is grabbed where the icon is. Because this must happen after the + icon is placed, *sometimes* you can see this happen. Unfortunately this is the only way to fix + this problem, and there are no other known workarounds outside of writing an X11 wrapper from scratch. ````   From fecb27014f8d5146c445b40263aee86fb045f0ef Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 24 Aug 2017 21:33:23 +0200 Subject: [PATCH 51/92] Updated version to 3.2 --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d9fe6b5..75476f3 100644 --- a/README.md +++ b/README.md @@ -261,7 +261,7 @@ Maven Info com.dorkbox SystemTray - 3.1 + 3.2 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 89faa7b..f1ecfc0 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -989,7 +989,7 @@ class SystemTray { */ public static Version getVersion() { - return new Version("3.1"); + return new Version("3.2"); } /** From 8b0c90156e073ce1d090b0dc4d68ef555f36ba66 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 24 Aug 2017 22:51:24 +0200 Subject: [PATCH 52/92] Forgot to disable debug. Updated version. --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75476f3..f7692ec 100644 --- a/README.md +++ b/README.md @@ -261,7 +261,7 @@ Maven Info com.dorkbox SystemTray - 3.2 + 3.3 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index f1ecfc0..7697235 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -140,7 +140,7 @@ 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; @@ -989,7 +989,7 @@ class SystemTray { */ public static Version getVersion() { - return new Version("3.2"); + return new Version("3.3"); } /** From 350e497fff135ec03bb98dc68d1873739278369c Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 24 Aug 2017 23:12:26 +0200 Subject: [PATCH 53/92] Updated Readme to reflect a `gotcha` + workaround when using awt.Desktop methods. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f7692ec..35b0b0b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The following unique problems are also solved by this library: 1. Windows *native* menus **do not** support images attached to menu entries 1. Windows menus **do not** support a different L&F from the running application 1. Windows, Linux, and MacOSX menus (native or otherwise) do not support HiDPI configurations + 1. java.awt.Desktop.getDesktop() is **broken** when using GTK3, or on MacOS. @@ -49,6 +50,9 @@ Problems and Restrictions - **ToolTips** The maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop Environments. Specifically, Swing and GtkStatusIcon types support tray tooltips and menu tooltips. AWT and AppIndicator types do not support tooltips of any kind. Please note that **Ubuntu** uses AppIndicators! - **Linux/Unix Menus** Some linux environments only support right-click to display the menu, and it is not possible to change the behavior. + + - **Linux/Unix and java.awt.Desktop.getDesktop()** Please use the `dorkbox.util.Desktop` class as a replacement, which will + intelligently call the correct OS API to open a folder/directory, email, or browser. (*Many thanks to QZ Tray for this*). AutoDetect Compatibility List ------------------ From 7154447c09a306675ce19efd18ab00802e728661 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 24 Aug 2017 23:19:05 +0200 Subject: [PATCH 54/92] Fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35b0b0b..dbf487e 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ Note: Gnome-shell users can install an extension to support placing the tray ico is initially hidden. ```` ```` -Note: We have fixed the Swing notificat tray on Linux (it no longer has a greyish background), however +Note: We have fixed the Swing notification tray on Linux (it no longer has a greyish background), however to facilitate this, a screen-shot is grabbed where the icon is. Because this must happen after the icon is placed, *sometimes* you can see this happen. Unfortunately this is the only way to fix this problem, and there are no other known workarounds outside of writing an X11 wrapper from scratch. From ee480b0e117b5a574e7a9579d4d391a790225ea8 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 14:49:59 +0200 Subject: [PATCH 55/92] simplified isUnity() calls. comment polish --- src/dorkbox/systemTray/SystemTray.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 7697235..a79afeb 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -706,7 +706,7 @@ class SystemTray { if (isTrayType(trayType, TrayType.GtkStatusIcon)) { OSUtil.DesktopEnv.Env de = OSUtil.DesktopEnv.get(); - if (de == OSUtil.DesktopEnv.Env.Unity || de == OSUtil.DesktopEnv.Env.Unity7 && OSUtil.Linux.isUbuntu()) { + if (OSUtil.DesktopEnv.isUnity(de) && OSUtil.Linux.isUbuntu()) { if (AUTO_FIX_INCONSISTENCIES) { // GTK2 does not support AppIndicators! if (Gtk.isGtk2) { @@ -790,7 +790,7 @@ class SystemTray { } if (!isTrayType(trayType, TrayType.Swing)) { -// NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3. + // NOTE: appindicator1 -> GTk2, appindicator3 -> GTK3. // appindicator3 doesn't support menu icons via GTK2!! if (!Gtk.isLoaded) { trayType = selectTypeQuietly(TrayType.Swing); From 9479f34a29503fcb7c3bfbc3c8bd504248f2c5cf Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 14:52:04 +0200 Subject: [PATCH 56/92] Added temp directory to test case --- test/dorkbox/TestTray.java | 15 +++++++++++++++ test/dorkbox/TestTrayJavaFX.java | 12 ++++++++++++ test/dorkbox/TestTraySwt.java | 13 +++++++++++++ 3 files changed, 40 insertions(+) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index b28ff41..4ff22aa 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -28,6 +28,7 @@ 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 @@ -137,6 +138,20 @@ class TestTray { } })); + + 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); diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index de3c643..9be8455 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -199,6 +199,18 @@ class TestTrayJavaFX { } })); + 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); diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index 75b9141..cab9517 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -33,6 +33,7 @@ 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 @@ -153,6 +154,18 @@ class TestTraySwt { } })); + 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); From 428f39baf196e18cd112b2b57d06717f05c62492 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 14:53:02 +0200 Subject: [PATCH 57/92] Fixed debug messages that were not inside debug wrapped call. --- src/dorkbox/systemTray/SystemTray.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index a79afeb..1cd42da 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -863,8 +863,10 @@ class SystemTray { 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) From 0a2ffb451d3f64123c78b866faf187e5bab1287d Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 14:54:44 +0200 Subject: [PATCH 58/92] Fixed issue #59. Awt menus cannot show tooltips. --- src/dorkbox/systemTray/ui/awt/AwtMenu.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenu.java b/src/dorkbox/systemTray/ui/awt/AwtMenu.java index df7d527..98f07e4 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenu.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenu.java @@ -139,13 +139,7 @@ class AwtMenu implements MenuPeer { @Override public void setTooltip(final MenuItem menuItem) { - SwingUtil.invokeLater(new Runnable() { - @Override - public - void run() { - _native.setLabel(menuItem.getTooltip()); - } - }); + // no op. (awt menus cannot show tooltips) } @Override From c8e88fb5687d2ea0beae5157d0c651da738aac10 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 15:15:31 +0200 Subject: [PATCH 59/92] Cleaned up image resizing --- .../systemTray/util/ImageResizeUtil.java | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/dorkbox/systemTray/util/ImageResizeUtil.java b/src/dorkbox/systemTray/util/ImageResizeUtil.java index bd7f718..8746f96 100644 --- a/src/dorkbox/systemTray/util/ImageResizeUtil.java +++ b/src/dorkbox/systemTray/util/ImageResizeUtil.java @@ -223,30 +223,16 @@ class ImageResizeUtil { // resize the image, keep aspect ratio Image image = ImageUtil.getImageImmediate(ImageIO.read(inputStream)); - int height = image.getHeight(null); - int width = image.getWidth(null); - - 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); - } + // the smaller dimension have padding, so the larger dimension is the size of this image. + BufferedImage bufferedImage = ImageUtil.getSquareBufferedImage(image); // 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 +310,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()); From 1d0d67d0d57be7e59d4da44f44ff7d99adb06e88 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 15:23:43 +0200 Subject: [PATCH 60/92] language cleanup --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dbf487e..17e6bbf 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ This library provides **OS Native** menus and **Swing/AWT** menus, depending on   The following unique problems are also solved by this library: - 1. *Sun/Oracle* system-tray icons on gnu/linux **do not** support images with transparent backgrounds - 1. *Sun/Oracle* system-tray and *SWT* system-tray implementations **do not** support app-indicators, which are necessary on different distributions of gnu/linux and unix + 1. *Sun/Oracle* system-tray icons on Linux/Unix **do not** support images with transparent backgrounds + 1. *Sun/Oracle* system-tray and *SWT* system-tray implementations **do not** support app-indicators, which are necessary on different distributions of Linux/Unix 1. *GtkStatusIcons* on GNOME3 desktop environments are hidden by default 1. *Sun/Oracle* system-tray menus on Windows **look absolutely horrid** 1. *Sun/Oracle* system-tray icons on Windows are **hard-coded** to a max size of 24x24 (it was last updated in *2006*) @@ -45,11 +45,11 @@ Problems and Restrictions - **MacOSX** *native* menus cannot display images attached to menu entries. If desired, one could override the default for MacOSX so that it uses *Swing* instead of *AWT*, however this will result the SystemTray no-longer supporting the OS theme and transparency. The default of *AWT* was chosen because it looks much, much better than *Swing*. - - **Gnome3** (Fedora, Manjaro, Arch, etc) environments by default **do not** allow the SystemTray icon to be shown. This has been worked around (it will be placed next to the clock) for most Gnome environments, except for Arch linux. Another workaround is to install the [Top Icons plugin](https://extensions.gnome.org/extension/1031/topicons/) plugin 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. + - **Gnome3** (Fedora, Manjaro, Arch, etc) environments by default **do not** allow the SystemTray icon to be shown. This has been worked around (it will be placed next to the clock) for most Gnome environments, except for Arch Linux. Another workaround is to install the [Top Icons plugin](https://extensions.gnome.org/extension/1031/topicons/) plugin 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. - **ToolTips** The maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop Environments. Specifically, Swing and GtkStatusIcon types support tray tooltips and menu tooltips. AWT and AppIndicator types do not support tooltips of any kind. Please note that **Ubuntu** uses AppIndicators! - - **Linux/Unix Menus** Some linux environments only support right-click to display the menu, and it is not possible to change the behavior. + - **Linux/Unix Menus** Some Linux environments only support right-click to display the menu, and it is not possible to change the behavior. - **Linux/Unix and java.awt.Desktop.getDesktop()** Please use the `dorkbox.util.Desktop` class as a replacement, which will intelligently call the correct OS API to open a folder/directory, email, or browser. (*Many thanks to QZ Tray for this*). From 4c4c6bf56d9f61e25b8201d2e2ebec0e73bac18d Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 15:32:11 +0200 Subject: [PATCH 61/92] Updated version --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17e6bbf..639f563 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ Maven Info com.dorkbox SystemTray - 3.3 + 3.4 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 1cd42da..fdacd43 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -991,7 +991,7 @@ class SystemTray { */ public static Version getVersion() { - return new Version("3.3"); + return new Version("3.4"); } /** From 123d66f320d0965cfe25ac68557cf0d40fb892b1 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Sep 2017 17:48:14 +0200 Subject: [PATCH 62/92] Cleaned up unnecessary iml setings --- SystemTray.iml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/SystemTray.iml b/SystemTray.iml index 919d9eb..e543631 100755 --- a/SystemTray.iml +++ b/SystemTray.iml @@ -1,30 +1,10 @@ - - - - - - - - - - - - - - - - - - - - @@ -37,4 +17,4 @@ - \ No newline at end of file + From 9d92c9749d58202546f097f69683b52995cb9575 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 14 Sep 2017 23:58:10 +0200 Subject: [PATCH 63/92] Reverted change to use Version object. --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 639f563..6f2c620 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ Maven Info com.dorkbox SystemTray - 3.4 + 3.5 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index fdacd43..f6aaea7 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -57,7 +57,6 @@ import dorkbox.util.OSUtil; import dorkbox.util.Property; import dorkbox.util.SwingUtil; import dorkbox.util.Swt; -import dorkbox.util.Version; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gtk; import dorkbox.util.jna.linux.GtkCheck; @@ -990,8 +989,8 @@ class SystemTray { * Gets the version number. */ public static - Version getVersion() { - return new Version("3.4"); + String getVersion() { + return "3.5"; } /** From 104717c0169c5ea744f01221c182f512d397024e Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 17 Sep 2017 17:41:37 +0200 Subject: [PATCH 64/92] Fixed auto-resizing images --- .../systemTray/util/ImageResizeUtil.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/dorkbox/systemTray/util/ImageResizeUtil.java b/src/dorkbox/systemTray/util/ImageResizeUtil.java index 8746f96..307c849 100644 --- a/src/dorkbox/systemTray/util/ImageResizeUtil.java +++ b/src/dorkbox/systemTray/util/ImageResizeUtil.java @@ -220,11 +220,22 @@ 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)); - // the smaller dimension have padding, so the larger dimension is the size of this image. - BufferedImage bufferedImage = ImageUtil.getSquareBufferedImage(image); + BufferedImage bufferedImage = ImageUtil.getBufferedImage(image); + + // resize the image, keep aspect ratio + int width = bufferedImage.getWidth(); + int height = bufferedImage.getHeight(); + if (width > height) { + 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 ImageIO.write(bufferedImage, "png", newFile); // made up extension From 46c8bb712ef928424a8bed561af356b34a6ddade Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 17 Sep 2017 21:01:00 +0200 Subject: [PATCH 65/92] Fixed *.remove() methods for GTK, so they are continuously executed on the GTK thread. --- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 22 +++++++++++++++---- .../systemTray/ui/gtk/GtkMenuItem.java | 11 ++++++++-- .../ui/gtk/GtkMenuItemCheckbox.java | 11 ++++++++-- .../ui/gtk/GtkMenuItemSeparator.java | 11 ++++++++-- .../systemTray/ui/gtk/GtkMenuItemStatus.java | 11 ++++++++-- 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index 62951cb..be32bde 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -252,7 +252,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { // is overridden by system tray setLegitImage(menuItem.getImage() != null); - GtkEventDispatch.dispatch(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { @@ -273,7 +273,14 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { Gtk2.gtk_widget_show_all(_native); } - }); + }; + + if (GtkEventDispatch.isDispatch.get()) { + runnable.run(); + } + else { + GtkEventDispatch.dispatch(runnable); + } } // is overridden in tray impl @@ -378,7 +385,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { @Override public void remove() { - GtkEventDispatch.dispatch(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { @@ -401,6 +408,13 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { parent.createMenu(); // must be on EDT } } - }); + }; + + if (GtkEventDispatch.isDispatch.get()) { + runnable.run(); + } + else { + GtkEventDispatch.dispatch(runnable); + } } } diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index a23a9b7..5b4d74c 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java @@ -185,7 +185,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { @Override public void remove() { - GtkEventDispatch.dispatch(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { @@ -201,6 +201,13 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { parent.remove(GtkMenuItem.this); } - }); + }; + + if (GtkEventDispatch.isDispatch.get()) { + runnable.run(); + } + else { + GtkEventDispatch.dispatch(runnable); + } } } diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java index 1ec98e1..31011b6 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java @@ -291,7 +291,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall @Override public void remove() { - GtkEventDispatch.dispatch(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { @@ -306,6 +306,13 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall parent.remove(GtkMenuItemCheckbox.this); } - }); + }; + + if (GtkEventDispatch.isDispatch.get()) { + runnable.run(); + } + else { + GtkEventDispatch.dispatch(runnable); + } } } diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java index 104a9aa..91e8c86 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java @@ -37,7 +37,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer { @Override public void remove() { - GtkEventDispatch.dispatch(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { @@ -45,7 +45,14 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer { parent.remove(GtkMenuItemSeparator.this); } - }); + }; + + if (GtkEventDispatch.isDispatch.get()) { + runnable.run(); + } + else { + GtkEventDispatch.dispatch(runnable); + } } @Override diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java index be359a4..717cd1e 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java @@ -61,7 +61,7 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer { @Override public void remove() { - GtkEventDispatch.dispatch(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { @@ -71,6 +71,13 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer { parent.remove(GtkMenuItemStatus.this); } - }); + }; + + if (GtkEventDispatch.isDispatch.get()) { + runnable.run(); + } + else { + GtkEventDispatch.dispatch(runnable); + } } } From 79a7ccbe5c52ad181bfecf2c39dc4da61a70d0f6 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 17 Sep 2017 21:01:29 +0200 Subject: [PATCH 66/92] Changed test examples to add menu as an 'out of order' entry --- test/dorkbox/TestTray.java | 5 +++-- test/dorkbox/TestTrayJavaFX.java | 6 +++--- test/dorkbox/TestTraySwt.java | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 4ff22aa..4ac7123 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -60,6 +60,7 @@ class TestTray { public TestTray() { + 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) @@ -154,7 +155,7 @@ class TestTray { Menu submenu = new Menu("Options", BLUE_CAMPING); submenu.setShortcut('t'); - mainMenu.add(submenu); + MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() { @Override @@ -182,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 9be8455..18b510f 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -120,7 +120,7 @@ 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); // set Native L&F (this is the System L&F instead of CrossPlatform L&F) @@ -213,7 +213,7 @@ class TestTrayJavaFX { Menu submenu = new Menu("Options", BLUE_CAMPING); submenu.setShortcut('t'); - mainMenu.add(submenu); + MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() { @Override @@ -241,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 cab9517..1371480 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -76,6 +76,7 @@ class TestTraySwt { helloWorldTest.setText("Hello World SWT ................. "); helloWorldTest.pack(); + 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) @@ -168,7 +169,7 @@ class TestTraySwt { Menu submenu = new Menu("Options", BLUE_CAMPING); submenu.setShortcut('t'); - mainMenu.add(submenu); + MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() { @Override @@ -213,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(); From bed426607e072568f3b4507c67476d2c0c71d2c3 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 17 Sep 2017 21:34:30 +0200 Subject: [PATCH 67/92] Updated version --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f2c620..0fbba8d 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ Maven Info com.dorkbox SystemTray - 3.5 + 3.6 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index f6aaea7..46fccd2 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -990,7 +990,7 @@ class SystemTray { */ public static String getVersion() { - return "3.5"; + return "3.6"; } /** From cf62fd7ed5379ccf00b40679906a7394d3ed4747 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 17 Sep 2017 23:52:32 +0200 Subject: [PATCH 68/92] Added SystemTray.ENABLE_ROOT_CHECK --- README.md | 8 +++++++- src/dorkbox/systemTray/SystemTray.java | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fbba8d..b658dd2 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,12 @@ SystemTray.ENABLE_SHUTDOWN_HOOK (type boolean, default value 'true') SystemTray.AUTO_FIX_INCONSISTENCIES (type boolean, default value 'true') - Allows the SystemTray logic to resolve various OS inconsistencies for the SystemTray in different combinations + + +SystemTray.ENABLE_ROOT_CHECK (type boolean, default value 'true') + - 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 SystemTray.SWING_UI (type SwingUIFactory, default value 'null') @@ -265,7 +271,7 @@ Maven Info com.dorkbox SystemTray - 3.6 + 3.7 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 46fccd2..eb3c754 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -129,6 +129,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. @@ -742,7 +751,7 @@ class SystemTray { } } - 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); @@ -990,7 +999,7 @@ class SystemTray { */ public static String getVersion() { - return "3.6"; + return "3.7"; } /** From 884a32dd81da0bb548cf5d575510e68e2465dcaa Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 18 Sep 2017 00:26:32 +0200 Subject: [PATCH 69/92] Added information on how to run this as a different user. --- README.md | 3 ++- src/dorkbox/systemTray/SystemTray.java | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b658dd2..4012903 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,8 @@ Notes: - SWT builds for FreeBSD do not exist. + - Linux/Unix: If you want to run this library as a different user, you will need to launch your application via `sudo su username /bin/sh -c "DBUS_SESSION_BUS_ADDRESS='unix:abstract=/tmp/dbus-cLtEoBPmgC' XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP program-name"`, where `unix:abstract=/tmp/dbus-cLtEoBPmgC` from `/run/user/{uid}/dbus-session`. You will also want to disable the root check + warnings via `SystemTray.ENABLE_ROOT_CHECK=false;` See [issue](https://github.com/dorkbox/SystemTray/issues/63) for more details. +     @@ -230,7 +232,6 @@ Note: We have fixed the Swing notification tray on Linux (it no longer has a gre to facilitate this, a screen-shot is grabbed where the icon is. Because this must happen after the icon is placed, *sometimes* you can see this happen. Unfortunately this is the only way to fix this problem, and there are no other known workarounds outside of writing an X11 wrapper from scratch. - ````     diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index eb3c754..7ea9e41 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -757,11 +757,12 @@ class SystemTray { 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."); } } } From b17e34582273e9793c6c5fb3915ad4352815cbc3 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 22 Sep 2017 09:11:56 +0200 Subject: [PATCH 70/92] Fixed issue with chromeOS initialization (the fix is in the Utils). --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4012903..f336e75 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ Maven Info com.dorkbox SystemTray - 3.7 + 3.8 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 7ea9e41..28df640 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -1000,7 +1000,7 @@ class SystemTray { */ public static String getVersion() { - return "3.7"; + return "3.8"; } /** From c4a00e7917a8468c6f46f71987a249ae815bc4a0 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 28 Sep 2017 22:10:20 +0200 Subject: [PATCH 71/92] Updated version (fix from utils: xdg-open now runs outside of the GtkEventDispatch thread) --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f336e75..cf1ada1 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ Maven Info com.dorkbox SystemTray - 3.8 + 3.9 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 28df640..0f10ef4 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -1000,7 +1000,7 @@ class SystemTray { */ public static String getVersion() { - return "3.8"; + return "3.9"; } /** From 0b866b2bf1db0f111ffe0adfa07ecb6e1054448c Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 28 Sep 2017 23:51:54 +0200 Subject: [PATCH 72/92] SystemTray menu item callbacks now occur on their own dispatch thread (instead of being on whatever OS's event dispatch thread), in order to provide consistent actions across all platforms. --- .../systemTray/ui/awt/AwtMenuItem.java | 31 +++++++------ .../ui/awt/AwtMenuItemCheckbox.java | 22 ++++++---- .../systemTray/ui/gtk/GtkMenuItem.java | 43 +++++++++++++------ .../ui/gtk/GtkMenuItemCheckbox.java | 26 ++++++----- .../systemTray/ui/swing/SwingMenuItem.java | 29 +++++++------ .../ui/swing/SwingMenuItemCheckbox.java | 22 ++++++---- .../systemTray/util/EventDispatch.java | 24 +++++++++++ 7 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 src/dorkbox/systemTray/util/EventDispatch.java diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java index 384a8eb..5ca7523 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItem.java @@ -22,6 +22,7 @@ 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 { @@ -75,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 diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java index fac468c..86c96d4 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java @@ -22,6 +22,7 @@ import java.awt.event.ActionListener; 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 { @@ -75,21 +76,26 @@ class AwtMenuItemCheckbox 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/ui/gtk/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index 5b4d74c..ae5cc11 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java @@ -17,6 +17,7 @@ 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; @@ -24,6 +25,7 @@ import com.sun.jna.Pointer; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; 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; @@ -32,7 +34,7 @@ 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! @@ -56,15 +58,9 @@ 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 Gtk2.TRUE; @@ -153,10 +149,34 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { }); } + @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 @@ -193,7 +213,6 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { GtkMenuItem.super.remove(); - menuItemForActionCallback = null; if (image != null) { Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it image = null; diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java index 31011b6..8dfcef9 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java @@ -27,6 +27,7 @@ import com.sun.jna.Pointer; import dorkbox.systemTray.Checkbox; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.peer.CheckboxPeer; +import dorkbox.systemTray.util.EventDispatch; import dorkbox.systemTray.util.HeavyCheckMark; import dorkbox.systemTray.util.ImageResizeUtil; import dorkbox.util.OSUtil; @@ -133,9 +134,9 @@ 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 Gtk2.TRUE; @@ -206,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); + } } - } + }); } }; } diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java index 1e771f5..555b3ad 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItem.java @@ -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 diff --git a/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java index c2dd068..a72e590 100644 --- a/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/swing/SwingMenuItemCheckbox.java @@ -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/util/EventDispatch.java b/src/dorkbox/systemTray/util/EventDispatch.java new file mode 100644 index 0000000..b0b5f2b --- /dev/null +++ b/src/dorkbox/systemTray/util/EventDispatch.java @@ -0,0 +1,24 @@ +package dorkbox.systemTray.util; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import dorkbox.util.DaemonThreadFactory; + +/** + * 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 peculariaties across on GTK with how AWT/SWING react with the GTK Event + * Dispatch Thread. + */ +public +class EventDispatch { + private static final Executor eventDispatchExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("SystemTray")); + + /** + * Schedule an event to occur sometime in the future. + */ + public static + void runLater(Runnable runnable) { + eventDispatchExecutor.execute(runnable); + } +} From 249badb6ec65874f4949bd39c7fc95f4596ede65 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 28 Sep 2017 23:56:06 +0200 Subject: [PATCH 73/92] Added readme notes for how callbacks occur now on their own thread. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index cf1ada1..f5a8676 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,11 @@ Notes: ------- - The compatibility list only applies while the SystemTray is in `AutoDetect` mode. Not all OSes support forcing a custom tray type. + - The menu item callbacks occur on **their own dispatch thread** (instead of being on whatever OS's event dispatch thread), in order to + provide consistent actions across all platforms. It is critical to make sure that access to Swing/etc that depend on running events + inside their own EDT, are properly called. IE: `SwingUtilities.invokeLater()`. Do not use `invokeAndWait()` as weird GUI anomalies + can happen. + - Ubuntu 16.04+ with JavaFX require `libappindicator1` because of JavaFX GTK and indicator panel incompatibilities. See [more details](https://github.com/dorkbox/SystemTray/issues/14#issuecomment-248853532). We attempt to fallback to using Swing in this situation. - Ubuntu 17.04+ Java only supports the X11 backend. MIR and Wayland are not supported. From 41d3082efcaaa6473a29ecb2cea650bf380d9868 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 29 Sep 2017 12:19:36 +0200 Subject: [PATCH 74/92] Updated version --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f5a8676..1f7cd35 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ Maven Info com.dorkbox SystemTray - 3.9 + 3.10 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 0f10ef4..b358fbb 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -1000,7 +1000,7 @@ class SystemTray { */ public static String getVersion() { - return "3.9"; + return "3.10"; } /** From 7f4f14b9eea908796f0144256f58f3753611e3f7 Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 3 Oct 2017 00:07:28 +0200 Subject: [PATCH 75/92] Switched from the DaemonThreadFactory, to the NamedThreadFactory (standardized support by utilities) --- src/dorkbox/systemTray/util/EventDispatch.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/util/EventDispatch.java b/src/dorkbox/systemTray/util/EventDispatch.java index b0b5f2b..ae78634 100644 --- a/src/dorkbox/systemTray/util/EventDispatch.java +++ b/src/dorkbox/systemTray/util/EventDispatch.java @@ -3,7 +3,7 @@ package dorkbox.systemTray.util; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import dorkbox.util.DaemonThreadFactory; +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 @@ -12,7 +12,7 @@ import dorkbox.util.DaemonThreadFactory; */ public class EventDispatch { - private static final Executor eventDispatchExecutor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("SystemTray")); + private static final Executor eventDispatchExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("SystemTray", true)); /** * Schedule an event to occur sometime in the future. From 411adde613a91122b130670b8c60a1410d6f1bba Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 3 Oct 2017 00:13:17 +0200 Subject: [PATCH 76/92] Updated version --- README.md | 2 +- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f7cd35..fe5b8b6 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ Maven Info com.dorkbox SystemTray - 3.10 + 3.11 ```` diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index b358fbb..d1fb04a 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -1000,7 +1000,7 @@ class SystemTray { */ public static String getVersion() { - return "3.10"; + return "3.11"; } /** From 0b53d5388fee855bcb3f436e184f63efccee38fa Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 12 Nov 2017 00:53:45 +0100 Subject: [PATCH 77/92] Fixed typo in comments --- test/example.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/example.c b/test/example.c index 3b01c42..8dc318a 100644 --- a/test/example.c +++ b/test/example.c @@ -5,7 +5,7 @@ // 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 && ./example From f44374d11d960da0bd438a03241315b7b1064907 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 15 Nov 2017 15:16:07 +0100 Subject: [PATCH 78/92] Added shutdown to Event Dispatch --- .../systemTray/util/EventDispatch.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/dorkbox/systemTray/util/EventDispatch.java b/src/dorkbox/systemTray/util/EventDispatch.java index ae78634..e9927aa 100644 --- a/src/dorkbox/systemTray/util/EventDispatch.java +++ b/src/dorkbox/systemTray/util/EventDispatch.java @@ -1,24 +1,37 @@ package dorkbox.systemTray.util; -import java.util.concurrent.Executor; +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 peculariaties across on GTK with how AWT/SWING react with the GTK Event + * 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 final Executor eventDispatchExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("SystemTray", true)); + private static ExecutorService eventDispatchExecutor = null; /** * Schedule an event to occur sometime in the future. */ - public static + 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() { + eventDispatchExecutor.shutdownNow(); + eventDispatchExecutor = null; + } } From 740348b6c4a222d97f77dfd2aa9856dc2c57337a Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 15 Nov 2017 15:16:40 +0100 Subject: [PATCH 79/92] Added Separator Peer (so code is consistent) --- src/dorkbox/systemTray/Separator.java | 12 ++++++++++ .../systemTray/peer/SeparatorPeer.java | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/dorkbox/systemTray/peer/SeparatorPeer.java 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/peer/SeparatorPeer.java b/src/dorkbox/systemTray/peer/SeparatorPeer.java new file mode 100644 index 0000000..6898b6d --- /dev/null +++ b/src/dorkbox/systemTray/peer/SeparatorPeer.java @@ -0,0 +1,23 @@ +/* + * 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.peer; + +/** + * Internal component used to bind the API to the implementation + */ +public +interface SeparatorPeer extends EntryPeer { +} From e1cca820c3daa7c79da466c5bbb61e156cc9bf5e Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 15 Nov 2017 17:30:47 +0100 Subject: [PATCH 80/92] Fixed issues with spacer images, fixed issues with adding images to a menu entry after adding it to a menu. Removed GTK event dispatch queue, properly fixed out-of-order menu creation. During entry removal, the native peer takes care of cleaning up the native bits. --- src/dorkbox/systemTray/Entry.java | 2 +- src/dorkbox/systemTray/Menu.java | 24 +------ src/dorkbox/systemTray/MenuItem.java | 12 ---- src/dorkbox/systemTray/SystemTray.java | 32 +++++++++- .../systemTray/ui/gtk/GtkBaseMenuItem.java | 47 ++++++++++---- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 62 +++++++++---------- .../systemTray/ui/gtk/GtkMenuItem.java | 26 ++++---- .../ui/gtk/GtkMenuItemCheckbox.java | 17 ++--- .../ui/gtk/GtkMenuItemSeparator.java | 15 ++--- .../systemTray/ui/gtk/GtkMenuItemStatus.java | 15 ++--- .../ui/gtk/_AppIndicatorNativeTray.java | 15 +++-- 11 files changed, 137 insertions(+), 130 deletions(-) diff --git a/src/dorkbox/systemTray/Entry.java b/src/dorkbox/systemTray/Entry.java index ce58250..f632d96 100644 --- a/src/dorkbox/systemTray/Entry.java +++ b/src/dorkbox/systemTray/Entry.java @@ -81,7 +81,7 @@ class Entry { } /** - * Removes this menu entry from the menu and releases all system resources associated with this menu entry + * Removes this menu entry from the menu and releases all system resources associated with this menu entry. */ public void remove() { diff --git a/src/dorkbox/systemTray/Menu.java b/src/dorkbox/systemTray/Menu.java index bbc75dd..87eb828 100644 --- a/src/dorkbox/systemTray/Menu.java +++ b/src/dorkbox/systemTray/Menu.java @@ -408,33 +408,15 @@ class Menu extends MenuItem { } } - /** - * This removes all menu entries from this menu - */ - public - void clear() { - List copy; - synchronized (menuEntries) { - // access on this object must be synchronized for object visibility - // a copy is made to prevent deadlocks from occurring when operating in different threads - // have to make copy because we are deleting all of them, and sub-menus remove themselves from parents - copy = new ArrayList(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 29737bb..f00e10c 100644 --- a/src/dorkbox/systemTray/MenuItem.java +++ b/src/dorkbox/systemTray/MenuItem.java @@ -387,16 +387,4 @@ class MenuItem extends Entry { String getTooltip() { return this.tooltip; } - - @Override - public - void remove() { - if (peer != null) { - setImage_(null); - setText(null); - setCallback(null); - } - - super.remove(); - } } diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index d1fb04a..afb25a6 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -44,6 +44,7 @@ import dorkbox.systemTray.ui.gtk._AppIndicatorNativeTray; import dorkbox.systemTray.ui.gtk._GtkStatusIconNativeTray; import dorkbox.systemTray.ui.swing.SwingUIFactory; import dorkbox.systemTray.ui.swing._SwingTray; +import dorkbox.systemTray.util.EventDispatch; import dorkbox.systemTray.util.ImageResizeUtil; import dorkbox.systemTray.util.LinuxSwingUI; import dorkbox.systemTray.util.SizeAndScalingUtil; @@ -226,7 +227,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) { @@ -310,6 +311,19 @@ class SystemTray { // kde 5.8+ is "high DPI", so we need to adjust the scale. Image resize will do that } case Unity: { + try { + String ubuntuVersion = OSUtil.Linux.getUbuntuVersion(); + String[] split = ubuntuVersion.split("."); + int major = Integer.parseInt(split[0]); + int minor = Integer.parseInt(split[1]); + + // <=16.04 it is better to use GtkStatusIcons. + if (major < 16 || (major == 16 && minor <= 4)) { + return selectTypeQuietly(TrayType.GtkStatusIcon); + } + } catch (Exception ignored) { + } + // Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell". return selectTypeQuietly(TrayType.AppIndicator); } @@ -393,6 +407,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; @@ -1025,13 +1052,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(); } /** diff --git a/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java index 9ef764c..72b69c9 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkBaseMenuItem.java @@ -30,7 +30,7 @@ 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; @@ -51,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. @@ -64,18 +97,10 @@ class GtkBaseMenuItem implements EntryPeer { return; } - 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); - } + removeSpacerImage(); if (everyoneElseHasImages) { - 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); + addSpacerImage(); } Gtk2.gtk_widget_show_all(_native); diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index be32bde..55b8244 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -197,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 @@ -206,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()); + } } }); } @@ -252,7 +267,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { // is overridden by system tray setLegitImage(menuItem.getImage() != null); - Runnable runnable = new Runnable() { + GtkEventDispatch.dispatch(new Runnable() { @Override public void run() { @@ -263,8 +278,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { } if (menuItem.getImage() != null) { - image = Gtk2.gtk_image_new_from_file(menuItem.getImage() - .getAbsolutePath()); + 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 @@ -273,14 +287,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { Gtk2.gtk_widget_show_all(_native); } - }; - - if (GtkEventDispatch.isDispatch.get()) { - runnable.run(); - } - else { - GtkEventDispatch.dispatch(runnable); - } + }); } // is overridden in tray impl @@ -385,7 +392,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { @Override public void remove() { - Runnable runnable = new Runnable() { + GtkEventDispatch.dispatch(new Runnable() { @Override public void run() { @@ -408,13 +415,6 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { parent.createMenu(); // must be on EDT } } - }; - - if (GtkEventDispatch.isDispatch.get()) { - runnable.run(); - } - else { - GtkEventDispatch.dispatch(runnable); - } + }); } } diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java index ae5cc11..f68277d 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItem.java @@ -73,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() { @@ -86,6 +87,9 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { } if (menuItem.getImage() != null) { + // 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); @@ -93,6 +97,11 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { // must always re-set always-show after setting the image 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(); + } Gtk2.gtk_widget_show_all(_native); } @@ -205,14 +214,16 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { @Override public void remove() { - Runnable runnable = new Runnable() { + GtkEventDispatch.dispatch(new Runnable() { @Override public void run() { - Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it - GtkMenuItem.super.remove(); + callback = null; + + Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it + if (image != null) { Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it image = null; @@ -220,13 +231,6 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback { parent.remove(GtkMenuItem.this); } - }; - - if (GtkEventDispatch.isDispatch.get()) { - runnable.run(); - } - else { - GtkEventDispatch.dispatch(runnable); - } + }); } } diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java index 8dfcef9..222bfe2 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemCheckbox.java @@ -297,14 +297,16 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall @Override public void remove() { - Runnable runnable = new Runnable() { + GtkEventDispatch.dispatch(new Runnable() { @Override public void run() { - Gtk2.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) { Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it image = null; @@ -312,13 +314,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall parent.remove(GtkMenuItemCheckbox.this); } - }; - - if (GtkEventDispatch.isDispatch.get()) { - runnable.run(); - } - else { - GtkEventDispatch.dispatch(runnable); - } + }); } } diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java index 91e8c86..bbd16fe 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemSeparator.java @@ -17,10 +17,10 @@ package dorkbox.systemTray.ui.gtk; import static dorkbox.util.jna.linux.Gtk.Gtk2; -import dorkbox.systemTray.peer.EntryPeer; +import dorkbox.systemTray.peer.SeparatorPeer; import dorkbox.util.jna.linux.GtkEventDispatch; -class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer { +class GtkMenuItemSeparator extends GtkBaseMenuItem implements SeparatorPeer { private final GtkMenu parent; @@ -37,7 +37,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer { @Override public void remove() { - Runnable runnable = new Runnable() { + GtkEventDispatch.dispatch(new Runnable() { @Override public void run() { @@ -45,14 +45,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer { parent.remove(GtkMenuItemSeparator.this); } - }; - - if (GtkEventDispatch.isDispatch.get()) { - runnable.run(); - } - else { - GtkEventDispatch.dispatch(runnable); - } + }); } @Override diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java index 717cd1e..5ea179e 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenuItemStatus.java @@ -61,23 +61,16 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer { @Override public void remove() { - Runnable runnable = new Runnable() { + GtkEventDispatch.dispatch(new Runnable() { @Override public void run() { - Gtk2.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); } - }; - - if (GtkEventDispatch.isDispatch.get()) { - runnable.run(); - } - else { - GtkEventDispatch.dispatch(runnable); - } + }); } } diff --git a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java index 95bb737..9ee2f2d 100644 --- a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java @@ -197,25 +197,24 @@ class _AppIndicatorNativeTray extends Tray { 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); 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(); } } }; From 142be61a9cdba06f5f8de75efb7e09688dfe4b70 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 22:46:19 +0100 Subject: [PATCH 81/92] Code polish --- src/dorkbox/systemTray/ui/gtk/GtkMenu.java | 2 +- .../ui/gtk/_AppIndicatorNativeTray.java | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java index 55b8244..2de5462 100644 --- a/src/dorkbox/systemTray/ui/gtk/GtkMenu.java +++ b/src/dorkbox/systemTray/ui/gtk/GtkMenu.java @@ -159,8 +159,8 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer { } } - Gtk2.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) } /** diff --git a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java index 9ee2f2d..b897a73 100644 --- a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java @@ -23,7 +23,6 @@ import com.sun.jna.Pointer; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.Tray; -import dorkbox.systemTray.gnomeShell.Extension; import dorkbox.systemTray.util.ImageResizeUtil; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gobject; @@ -91,7 +90,7 @@ class _AppIndicatorNativeTray extends Tray { // 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 +109,7 @@ class _AppIndicatorNativeTray extends Tray { 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 +125,7 @@ class _AppIndicatorNativeTray extends Tray { // 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 +140,11 @@ class _AppIndicatorNativeTray extends Tray { 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 +163,11 @@ class _AppIndicatorNativeTray extends Tray { @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); } } }); @@ -208,7 +207,7 @@ class _AppIndicatorNativeTray extends Tray { 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); From 80f497f954072cde61b256390d07c9ba17edfa7d Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 22:48:09 +0100 Subject: [PATCH 82/92] Null pointer fix --- src/dorkbox/systemTray/util/EventDispatch.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/util/EventDispatch.java b/src/dorkbox/systemTray/util/EventDispatch.java index e9927aa..9ae2818 100644 --- a/src/dorkbox/systemTray/util/EventDispatch.java +++ b/src/dorkbox/systemTray/util/EventDispatch.java @@ -31,7 +31,9 @@ class EventDispatch { */ public static synchronized void shutdown() { - eventDispatchExecutor.shutdownNow(); - eventDispatchExecutor = null; + if (eventDispatchExecutor != null) { + eventDispatchExecutor.shutdownNow(); + eventDispatchExecutor = null; + } } } From 4427ed753fb671d52a168f8c96af3c459d9b107f Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 22:50:44 +0100 Subject: [PATCH 83/92] Removed invalid option for ubuntu. Comment polish --- src/dorkbox/systemTray/SystemTray.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index afb25a6..5139d8f 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -311,24 +311,11 @@ class SystemTray { // kde 5.8+ is "high DPI", so we need to adjust the scale. Image resize will do that } case Unity: { - try { - String ubuntuVersion = OSUtil.Linux.getUbuntuVersion(); - String[] split = ubuntuVersion.split("."); - int major = Integer.parseInt(split[0]); - int minor = Integer.parseInt(split[1]); - - // <=16.04 it is better to use GtkStatusIcons. - if (major < 16 || (major == 16 && minor <= 4)) { - return selectTypeQuietly(TrayType.GtkStatusIcon); - } - } catch (Exception ignored) { - } - // Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell". return selectTypeQuietly(TrayType.AppIndicator); } case Unity7: { - // Ubuntu Unity7 is a weird combination. It's "Gnome", but it's not "Gnome Shell". + // 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: { From 4c413c5e81e19512837a2a70d58493ac43b75405 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 22:58:53 +0100 Subject: [PATCH 84/92] Updated package for ShellExecutor project (it's no longer part of Utilities) --- src/dorkbox/systemTray/gnomeShell/Extension.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dorkbox/systemTray/gnomeShell/Extension.java b/src/dorkbox/systemTray/gnomeShell/Extension.java index eedb723..72e5f19 100644 --- a/src/dorkbox/systemTray/gnomeShell/Extension.java +++ b/src/dorkbox/systemTray/gnomeShell/Extension.java @@ -31,12 +31,12 @@ 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.ShellAsyncExecutor; -import dorkbox.util.process.ShellExecutor; @SuppressWarnings({"DanglingJavadoc", "WeakerAccess"}) public From 1f7cb016d9f7bd0d2695cecba4b566beb33f7ebc Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 22:59:26 +0100 Subject: [PATCH 85/92] Added ShellExecutor to file list in readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe5b8b6..ad5ddb6 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,9 @@ Maven Info Or if you don't want to use Maven, you can access the latest files and source-code directly from here: https://github.com/dorkbox/SystemTray/releases -https://oss.sonatype.org/content/repositories/releases/com/dorkbox/SystemTray/ + +https://oss.sonatype.org/content/repositories/releases/com/dorkbox/SystemTray/ +https://oss.sonatype.org/content/repositories/releases/com/dorkbox/ShellExecutor/ From 679ac5e9053b3468ad3102f804c52acf6a9afcc3 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 22:59:39 +0100 Subject: [PATCH 86/92] Added shell executor --- SystemTray.iml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SystemTray.iml b/SystemTray.iml index e543631..385980c 100755 --- a/SystemTray.iml +++ b/SystemTray.iml @@ -9,6 +9,7 @@ + @@ -17,4 +18,4 @@ - + \ No newline at end of file From 3bfb2b571861908c3f671ee7f55f0e282d1fba1c Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 1 Dec 2017 23:00:24 +0100 Subject: [PATCH 87/92] Reverted WIP debugging commit --- src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java index b897a73..b9efc95 100644 --- a/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java +++ b/src/dorkbox/systemTray/ui/gtk/_AppIndicatorNativeTray.java @@ -23,6 +23,7 @@ import com.sun.jna.Pointer; import dorkbox.systemTray.MenuItem; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.Tray; +import dorkbox.systemTray.gnomeShell.Extension; import dorkbox.systemTray.util.ImageResizeUtil; import dorkbox.util.jna.linux.AppIndicator; import dorkbox.util.jna.linux.Gobject; @@ -125,7 +126,7 @@ class _AppIndicatorNativeTray extends Tray { // in extension.js, so don't change it // additionally, this is required to be set HERE (not somewhere else) - // appIndicator.app_indicator_set_title(Extension.DEFAULT_NAME); + appIndicator.app_indicator_set_title(Extension.DEFAULT_NAME); } } From 4de8ddca3a9533e36dfdf147a4152180e88dd3ee Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 2 Dec 2017 15:08:43 +0100 Subject: [PATCH 88/92] Added info about Ubuntu 17.10 (it's not yet supported) --- src/dorkbox/systemTray/SystemTray.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 5139d8f..19765cc 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -294,6 +294,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; From 6a2f135012a489ca56855f44220adfafb76fb6d3 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 2 Dec 2017 21:16:27 +0100 Subject: [PATCH 89/92] updated readme typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad5ddb6..791ecfc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The following unique problems are also solved by this library: 1. Windows *native* menus **do not** support images attached to menu entries 1. Windows menus **do not** support a different L&F from the running application 1. Windows, Linux, and MacOSX menus (native or otherwise) do not support HiDPI configurations - 1. java.awt.Desktop.getDesktop() is **broken** when using GTK3, or on MacOS. + 1. java.awt.Desktop.getDesktop() is **broken** when using GTK3 or on MacOS. From a3b7d72ac909c4518a6d3495c2f5d04a77a3268f Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 3 Dec 2017 00:43:27 +0100 Subject: [PATCH 90/92] Updated version --- README.md | 4 ++-- src/dorkbox/systemTray/SystemTray.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 791ecfc..02d15fc 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ Maven Info com.dorkbox SystemTray - 3.11 + 3.12 ```` @@ -286,7 +286,7 @@ Maven Info Or if you don't want to use Maven, you can access the latest files and source-code directly from here: https://github.com/dorkbox/SystemTray/releases -https://oss.sonatype.org/content/repositories/releases/com/dorkbox/SystemTray/ +https://oss.sonatype.org/content/repositories/releases/com/dorkbox/SystemTray/ https://oss.sonatype.org/content/repositories/releases/com/dorkbox/ShellExecutor/ diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index 19765cc..98578ec 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -1021,7 +1021,7 @@ class SystemTray { */ public static String getVersion() { - return "3.11"; + return "3.12"; } /** From 806c2c71b43d7a150e8751d0ebf2d9b9e5391d1e Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 8 Jan 2018 20:53:55 +0100 Subject: [PATCH 91/92] Fixed issue with isOracleVM logic --- src/dorkbox/systemTray/util/SystemTrayFixes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dorkbox/systemTray/util/SystemTrayFixes.java b/src/dorkbox/systemTray/util/SystemTrayFixes.java index 26566bc..fe1b69e 100644 --- a/src/dorkbox/systemTray/util/SystemTrayFixes.java +++ b/src/dorkbox/systemTray/util/SystemTrayFixes.java @@ -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 "); } From 7b008f9ef7130cb253728adcadbe443bfeac4f59 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 8 Jan 2018 21:04:47 +0100 Subject: [PATCH 92/92] Fixed issues with AWT and callbacks. issue #73 --- .../systemTray/ui/awt/AwtMenuItemCheckbox.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java index 86c96d4..50e3182 100644 --- a/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/ui/awt/AwtMenuItemCheckbox.java @@ -18,6 +18,8 @@ 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; @@ -31,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. @@ -68,19 +70,20 @@ 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); @@ -99,7 +102,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer { } }; - _native.addActionListener(callback); + _native.addItemListener(this.callback); } } @@ -150,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);