Fixed issues with calling gtk_widget_destroy too much. Fixed
threading issue with isDispatch.
This commit is contained in:
parent
7c6f3ca77b
commit
a3118a07f4
@ -22,7 +22,6 @@ import java.awt.event.ActionListener;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import com.sun.jna.Function;
|
import com.sun.jna.Function;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
@ -57,9 +56,16 @@ class Gtk {
|
|||||||
private static boolean alreadyRunningGTK = false;
|
private static boolean alreadyRunningGTK = false;
|
||||||
private static boolean isLoaded = false;
|
private static boolean isLoaded = false;
|
||||||
|
|
||||||
private static AtomicBoolean isDispatch = new AtomicBoolean(false);
|
// 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<Boolean> isDispatch = new ThreadLocal<Boolean>() {
|
||||||
|
@Override
|
||||||
|
protected
|
||||||
|
Boolean initialValue() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private static final int TIMEOUT = 2;
|
private static final int TIMEOUT = 2000000;
|
||||||
|
|
||||||
// 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-x11-2.0.so.0 | grep gtk
|
||||||
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
|
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
|
||||||
@ -399,11 +405,11 @@ class Gtk {
|
|||||||
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
|
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
|
||||||
if (SystemTray.DEBUG) {
|
if (SystemTray.DEBUG) {
|
||||||
SystemTray.logger.error("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
|
SystemTray.logger.error("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
|
||||||
"to complete. Please adjust `SystemTray.TIMEOUT` to a value which better suites your environment.",
|
"to complete.",
|
||||||
new Exception(""));
|
new Exception(""));
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
|
throw new RuntimeException("Something is very wrong. The Event Dispatch Queue took longer than " + TIMEOUT + " seconds " +
|
||||||
"to complete. Please adjust `SystemTray.TIMEOUT` to a value which better suites your environment.");
|
"to complete.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@ -507,14 +513,18 @@ class Gtk {
|
|||||||
|
|
||||||
public static native void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
|
public static native void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
|
||||||
|
|
||||||
public static native void gtk_menu_shell_deactivate(Pointer menu_shell, Pointer child);
|
// Typically this results in the menu shell being erased from the screen
|
||||||
|
public static native void gtk_menu_shell_deactivate(Pointer menuShell);
|
||||||
|
|
||||||
public static native void gtk_widget_set_sensitive(Pointer widget, boolean sensitive);
|
public static native void gtk_widget_set_sensitive(Pointer widget, boolean sensitive);
|
||||||
|
|
||||||
public static native void gtk_container_remove(Pointer menu, Pointer subItem);
|
|
||||||
|
|
||||||
public static native void gtk_widget_show_all(Pointer widget);
|
public static native void gtk_widget_show_all(Pointer widget);
|
||||||
|
|
||||||
|
// will automatically get destroyed if no other references to it
|
||||||
|
public static native void gtk_container_remove(Pointer parentWidget, Pointer widget);
|
||||||
|
|
||||||
|
// from: https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-destroy
|
||||||
|
// You should typically call this function on top level widgets, and rarely on child widgets.
|
||||||
public static native void gtk_widget_destroy(Pointer widget);
|
public static native void gtk_widget_destroy(Pointer widget);
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,12 @@ class GtkBaseMenuItem implements EntryPeer {
|
|||||||
// these have to be volatile, because they can be changed from any thread
|
// these have to be volatile, because they can be changed from any thread
|
||||||
private volatile Pointer spacerImage;
|
private volatile Pointer spacerImage;
|
||||||
|
|
||||||
GtkBaseMenuItem() {
|
// the native GTK component
|
||||||
|
protected final Pointer _native;
|
||||||
|
|
||||||
|
GtkBaseMenuItem(final Pointer _native) {
|
||||||
|
this._native = _native;
|
||||||
|
|
||||||
// cannot be done in a static initializer, because the tray icon size might not yet have been determined
|
// cannot be done in a static initializer, because the tray icon size might not yet have been determined
|
||||||
if (transparentIcon == null) {
|
if (transparentIcon == null) {
|
||||||
transparentIcon = ImageUtils.getTransparentImage(ImageUtils.ENTRY_SIZE);
|
transparentIcon = ImageUtils.getTransparentImage(ImageUtils.ENTRY_SIZE);
|
||||||
@ -56,14 +61,14 @@ class GtkBaseMenuItem implements EntryPeer {
|
|||||||
* called on the DISPATCH thread
|
* called on the DISPATCH thread
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
void setSpacerImage(final Pointer _native, final boolean everyoneElseHasImages) {
|
void setSpacerImage(final boolean everyoneElseHasImages) {
|
||||||
if (hasLegitImage) {
|
if (hasLegitImage) {
|
||||||
// we have a legit icon, so there is nothing else we can do.
|
// we have a legit icon, so there is nothing else we can do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spacerImage != null) {
|
if (spacerImage != null) {
|
||||||
Gtk.gtk_widget_destroy(spacerImage);
|
Gtk.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
|
||||||
spacerImage = null;
|
spacerImage = null;
|
||||||
Gtk.gtk_widget_show_all(_native);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
@ -81,18 +86,17 @@ class GtkBaseMenuItem implements EntryPeer {
|
|||||||
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
abstract void onDeleteMenu(final Pointer parentNative);
|
// always on EDT
|
||||||
abstract void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu);
|
void onDeleteMenu(final Pointer parentNative) {
|
||||||
|
|
||||||
// always on dispatch
|
|
||||||
void onDeleteMenu(final Pointer parentNative, final Pointer _native) {
|
|
||||||
Gobject.g_object_force_floating(_native); // makes it a floating reference
|
Gobject.g_object_force_floating(_native); // makes it a floating reference
|
||||||
Gtk.gtk_container_remove(parentNative, _native);
|
Gtk.gtk_container_remove(parentNative, _native);
|
||||||
}
|
}
|
||||||
|
|
||||||
// always on dispatch
|
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
void onCreateMenu(final Pointer parentNative, final Pointer _native, final boolean hasImagesInMenu) {
|
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
setSpacerImage(_native, hasImagesInMenu);
|
// always on EDT
|
||||||
|
void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu) {
|
||||||
|
setSpacerImage(hasImagesInMenu);
|
||||||
|
|
||||||
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
|
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
|
||||||
Gtk.gtk_menu_shell_append(parentNative, _native);
|
Gtk.gtk_menu_shell_append(parentNative, _native);
|
||||||
@ -108,7 +112,7 @@ class GtkBaseMenuItem implements EntryPeer {
|
|||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
if (spacerImage != null) {
|
if (spacerImage != null) {
|
||||||
Gtk.gtk_widget_destroy(spacerImage);
|
Gtk.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
|
||||||
spacerImage = null;
|
spacerImage = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package dorkbox.systemTray.nativeUI;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
@ -37,7 +38,6 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
private final GtkMenu parent;
|
private final GtkMenu parent;
|
||||||
volatile Pointer _nativeMenu; // must ONLY be created at the end of delete!
|
volatile Pointer _nativeMenu; // must ONLY be created at the end of delete!
|
||||||
|
|
||||||
private final Pointer _nativeEntry; // is what is added to the parent menu, if we are NOT on the system tray
|
|
||||||
private volatile Pointer image;
|
private volatile Pointer image;
|
||||||
|
|
||||||
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
|
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
|
||||||
@ -46,24 +46,30 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
private volatile char mnemonicKey = 0;
|
private volatile char mnemonicKey = 0;
|
||||||
|
|
||||||
// have to make sure no other methods can call obliterate, delete, or create menu once it's already started
|
// have to make sure no other methods can call obliterate, delete, or create menu once it's already started
|
||||||
private volatile boolean obliterateInProgress = false;
|
private AtomicBoolean obliterateInProgress = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// called by the system tray constructors
|
||||||
|
// This is NOT a copy constructor!
|
||||||
|
@SuppressWarnings("IncompleteCopyConstructor")
|
||||||
|
GtkMenu() {
|
||||||
|
super(null);
|
||||||
|
this.parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
// This is NOT a copy constructor!
|
// This is NOT a copy constructor!
|
||||||
@SuppressWarnings("IncompleteCopyConstructor")
|
@SuppressWarnings("IncompleteCopyConstructor")
|
||||||
GtkMenu(final GtkMenu parent) {
|
GtkMenu(final GtkMenu parent) {
|
||||||
|
super(Gtk.gtk_image_menu_item_new_with_mnemonic("")); // is what is added to the parent menu (so images work)
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
|
||||||
if (parent != null) {
|
|
||||||
_nativeEntry = Gtk.gtk_image_menu_item_new_with_mnemonic(""); // is what is added to the parent menu (so images work)
|
|
||||||
} else {
|
|
||||||
_nativeEntry = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GtkMenu getParent() {
|
GtkMenu getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALWAYS CALLED ON THE EDT
|
||||||
|
*/
|
||||||
private
|
private
|
||||||
void add(final GtkBaseMenuItem item, final int index) {
|
void add(final GtkBaseMenuItem item, final int index) {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
@ -75,6 +81,8 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called inside the gdk_threads block
|
* Called inside the gdk_threads block
|
||||||
|
*
|
||||||
|
* ALWAYS CALLED ON THE EDT
|
||||||
*/
|
*/
|
||||||
protected
|
protected
|
||||||
void onMenuAdded(final Pointer menu) {
|
void onMenuAdded(final Pointer menu) {
|
||||||
@ -82,20 +90,22 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
|
||||||
/**
|
/**
|
||||||
* Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object.
|
* Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object.
|
||||||
|
*
|
||||||
|
* some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
* To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
*
|
||||||
|
* ALWAYS CALLED ON EDT
|
||||||
*/
|
*/
|
||||||
private
|
private
|
||||||
void deleteMenu() {
|
void deleteMenu() {
|
||||||
if (obliterateInProgress) {
|
if (obliterateInProgress.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nativeMenu != null) {
|
if (_nativeMenu != null) {
|
||||||
// have to remove all other menu entries
|
// have to remove all other menu entries
|
||||||
synchronized (menuEntries) {
|
|
||||||
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
||||||
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
|
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
|
||||||
menuEntry__.onDeleteMenu(_nativeMenu);
|
menuEntry__.onDeleteMenu(_nativeMenu);
|
||||||
@ -103,7 +113,6 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
|
|
||||||
Gtk.gtk_widget_destroy(_nativeMenu);
|
Gtk.gtk_widget_destroy(_nativeMenu);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.deleteMenu();
|
parent.deleteMenu();
|
||||||
@ -114,15 +123,20 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
|
|
||||||
// binds sub-menu to entry (if it exists! it does not for the root menu)
|
// binds sub-menu to entry (if it exists! it does not for the root menu)
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
Gtk.gtk_menu_item_set_submenu(_nativeEntry, _nativeMenu);
|
Gtk.gtk_menu_item_set_submenu(_native, _nativeMenu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
/**
|
||||||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
* some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
|
||||||
|
*
|
||||||
|
* To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||||
|
*
|
||||||
|
* ALWAYS CALLED ON THE EDT
|
||||||
|
*/
|
||||||
private
|
private
|
||||||
void createMenu() {
|
void createMenu() {
|
||||||
if (obliterateInProgress) {
|
if (obliterateInProgress.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +144,9 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
parent.createMenu();
|
parent.createMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now add back other menu entries
|
||||||
boolean hasImages = false;
|
boolean hasImages = false;
|
||||||
|
|
||||||
// now add back other menu entries
|
|
||||||
synchronized (menuEntries) {
|
|
||||||
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
|
||||||
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
|
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
|
||||||
hasImages |= menuEntry__.hasImage();
|
hasImages |= menuEntry__.hasImage();
|
||||||
@ -153,39 +166,37 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuAdded(_nativeMenu);
|
|
||||||
Gtk.gtk_widget_show_all(_nativeMenu); // necessary to guarantee widget is visible (doesn't always show_all for all children)
|
Gtk.gtk_widget_show_all(_nativeMenu); // necessary to guarantee widget is visible (doesn't always show_all for all children)
|
||||||
}
|
onMenuAdded(_nativeMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* must be called on the dispatch thread
|
|
||||||
*
|
|
||||||
* Completely obliterates the menu, no possible way to reconstruct it.
|
* Completely obliterates the menu, no possible way to reconstruct it.
|
||||||
|
*
|
||||||
|
* ALWAYS CALLED ON THE EDT
|
||||||
*/
|
*/
|
||||||
private
|
private
|
||||||
void obliterateMenu() {
|
void obliterateMenu() {
|
||||||
if (_nativeMenu != null && !obliterateInProgress) {
|
if (_nativeMenu != null && !obliterateInProgress.get()) {
|
||||||
obliterateInProgress = true;
|
obliterateInProgress.set(true);
|
||||||
|
|
||||||
// have to remove all other menu entries
|
// have to remove all other menu entries
|
||||||
synchronized (menuEntries) {
|
|
||||||
// a copy is made because sub-menus remove themselves from parents when .remove() is called. If we don't
|
// a copy is made because sub-menus remove themselves from parents when .remove() is called. If we don't
|
||||||
// do this, errors will be had because indices don't line up anymore.
|
// do this, errors will be had because indices don't line up anymore.
|
||||||
ArrayList<GtkBaseMenuItem> menuEntriesCopy = new ArrayList<GtkBaseMenuItem>(this.menuEntries);
|
ArrayList<GtkBaseMenuItem> menuEntriesCopy = new ArrayList<GtkBaseMenuItem>(menuEntries);
|
||||||
|
menuEntries.clear();
|
||||||
|
|
||||||
for (int i = 0, menuEntriesSize = menuEntriesCopy.size(); i < menuEntriesSize; i++) {
|
for (int i = 0, menuEntriesSize = menuEntriesCopy.size(); i < menuEntriesSize; i++) {
|
||||||
final GtkBaseMenuItem menuEntry__ = menuEntriesCopy.get(i);
|
final GtkBaseMenuItem menuEntry__ = menuEntriesCopy.get(i);
|
||||||
menuEntry__.remove();
|
menuEntry__.remove();
|
||||||
}
|
}
|
||||||
this.menuEntries.clear();
|
|
||||||
menuEntriesCopy.clear();
|
menuEntriesCopy.clear();
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(_nativeMenu);
|
Gtk.gtk_widget_destroy(_nativeMenu);
|
||||||
_nativeMenu = null;
|
_nativeMenu = null;
|
||||||
}
|
|
||||||
|
|
||||||
obliterateInProgress = false;
|
obliterateInProgress.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,21 +264,21 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
Gtk.gtk_widget_destroy(image);
|
Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
|
||||||
image = null;
|
image = null;
|
||||||
Gtk.gtk_widget_show_all(_nativeEntry);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuItem.getImage() != null) {
|
if (menuItem.getImage() != null) {
|
||||||
image = Gtk.gtk_image_new_from_file(menuItem.getImage()
|
image = Gtk.gtk_image_new_from_file(menuItem.getImage()
|
||||||
.getAbsolutePath());
|
.getAbsolutePath());
|
||||||
Gtk.gtk_image_menu_item_set_image(_nativeEntry, image);
|
Gtk.gtk_image_menu_item_set_image(_native, image);
|
||||||
|
|
||||||
// must always re-set always-show after setting the image
|
// must always re-set always-show after setting the image
|
||||||
Gtk.gtk_image_menu_item_set_always_show_image(_nativeEntry, true);
|
Gtk.gtk_image_menu_item_set_always_show_image(_native, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.gtk_widget_show_all(_nativeEntry);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -281,7 +292,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Gtk.gtk_widget_set_sensitive(_nativeEntry, menuItem.getEnabled());
|
Gtk.gtk_widget_set_sensitive(_native, menuItem.getEnabled());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -320,8 +331,8 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Gtk.gtk_menu_item_set_label(_nativeEntry, textWithMnemonic);
|
Gtk.gtk_menu_item_set_label(_native, textWithMnemonic);
|
||||||
Gtk.gtk_widget_show_all(_nativeEntry);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -342,38 +353,25 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
setText(menuItem);
|
setText(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
@Override
|
* called when a child removes itself from the parent menu. Does not work for sub-menus
|
||||||
void onDeleteMenu(final Pointer parentNative) {
|
*
|
||||||
if (parent != null) {
|
* ALWAYS CALLED ON THE EDT
|
||||||
onDeleteMenu(parentNative, _nativeEntry);
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu) {
|
|
||||||
if (parent != null) {
|
|
||||||
onCreateMenu(parentNative, _nativeEntry, hasImagesInMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// called when a child removes itself from the parent menu. Does not work for sub-menus
|
|
||||||
public
|
public
|
||||||
void remove(final GtkBaseMenuItem item) {
|
void remove(final GtkBaseMenuItem item) {
|
||||||
synchronized (menuEntries) {
|
|
||||||
menuEntries.remove(item);
|
menuEntries.remove(item);
|
||||||
}
|
|
||||||
|
|
||||||
// have to rebuild the menu now...
|
// have to rebuild the menu now...
|
||||||
deleteMenu();
|
deleteMenu(); // must be on EDT
|
||||||
createMenu();
|
createMenu(); // must be on EDT
|
||||||
}
|
}
|
||||||
|
|
||||||
// a child will always remove itself from the parent.
|
// a child will always remove itself from the parent.
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void remove() {
|
void remove() {
|
||||||
Gtk.dispatchAndWait(new Runnable() {
|
Gtk.dispatch(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
@ -381,21 +379,19 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
|
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
// have to remove from the parent.menuEntries first
|
// have to remove from the parent.menuEntries first
|
||||||
synchronized (parent.menuEntries) {
|
|
||||||
parent.menuEntries.remove(GtkMenu.this);
|
parent.menuEntries.remove(GtkMenu.this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// delete all of the children of this submenu (must happen before the menuEntry is removed)
|
// delete all of the children of this submenu (must happen before the menuEntry is removed)
|
||||||
obliterateMenu();
|
obliterateMenu(); // must be on EDT
|
||||||
|
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
// remove the gtk entry item from our menu NATIVE components
|
// remove the gtk entry item from our menu NATIVE components
|
||||||
Gtk.gtk_menu_item_set_submenu(_nativeEntry, null);
|
Gtk.gtk_menu_item_set_submenu(_native, null);
|
||||||
|
|
||||||
// have to rebuild the menu now...
|
// have to rebuild the menu now...
|
||||||
parent.deleteMenu();
|
parent.deleteMenu(); // must be on EDT
|
||||||
parent.createMenu();
|
parent.createMenu(); // must be on EDT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,6 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||||||
private final NativeLong nativeLong;
|
private final NativeLong nativeLong;
|
||||||
|
|
||||||
private final GtkMenu parent;
|
private final GtkMenu parent;
|
||||||
protected final Pointer _native = Gtk.gtk_image_menu_item_new_with_mnemonic("");
|
|
||||||
|
|
||||||
// these have to be volatile, because they can be changed from any thread
|
// these have to be volatile, because they can be changed from any thread
|
||||||
private volatile MenuItem menuItemForActionCallback;
|
private volatile MenuItem menuItemForActionCallback;
|
||||||
@ -48,6 +47,8 @@ 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
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuItem(final GtkMenu parent) {
|
GtkMenuItem(final GtkMenu parent) {
|
||||||
|
super(Gtk.gtk_image_menu_item_new_with_mnemonic(""));
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
nativeLong = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
|
nativeLong = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
|
||||||
}
|
}
|
||||||
@ -85,7 +86,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
Gtk.gtk_widget_destroy(image);
|
Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
|
||||||
image = null;
|
image = null;
|
||||||
Gtk.gtk_widget_show_all(_native);
|
Gtk.gtk_widget_show_all(_native);
|
||||||
}
|
}
|
||||||
@ -168,16 +169,6 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||||||
setText(menuItem);
|
setText(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void onDeleteMenu(final Pointer parentNative) {
|
|
||||||
onDeleteMenu(parentNative, _native);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu) {
|
|
||||||
onCreateMenu(parentNative, _native, hasImagesInMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
@SuppressWarnings("Duplicates")
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
@ -186,19 +177,16 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Gtk.gtk_container_remove(parent._nativeMenu, _native);
|
Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||||
Gtk.gtk_menu_shell_deactivate(parent._nativeMenu, _native);
|
|
||||||
|
|
||||||
GtkMenuItem.super.remove();
|
GtkMenuItem.super.remove();
|
||||||
|
|
||||||
menuItemForActionCallback = null;
|
menuItemForActionCallback = null;
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
Gtk.gtk_widget_destroy(image);
|
Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
|
||||||
image = null;
|
image = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(_native);
|
|
||||||
|
|
||||||
parent.remove(GtkMenuItem.this);
|
parent.remove(GtkMenuItem.this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -36,11 +36,9 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
private final NativeLong nativeLong;
|
private final NativeLong nativeLong;
|
||||||
|
|
||||||
private final GtkMenu parent;
|
private final GtkMenu parent;
|
||||||
private final Pointer _native = Gtk.gtk_check_menu_item_new_with_mnemonic("");
|
|
||||||
|
|
||||||
|
|
||||||
// these have to be volatile, because they can be changed from any thread
|
// these have to be volatile, because they can be changed from any thread
|
||||||
private volatile Checkbox menuItem;
|
private volatile Checkbox checkbox;
|
||||||
private volatile Pointer image;
|
private volatile Pointer image;
|
||||||
|
|
||||||
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
|
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
|
||||||
@ -53,6 +51,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuItemCheckbox(final GtkMenu parent) {
|
GtkMenuItemCheckbox(final GtkMenu parent) {
|
||||||
|
super(Gtk.gtk_check_menu_item_new_with_mnemonic(""));
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
|
||||||
// cannot be done in a static initializer, because the tray icon size might not yet have been determined
|
// cannot be done in a static initializer, because the tray icon size might not yet have been determined
|
||||||
@ -67,13 +66,13 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
int callback(final Pointer instance, final Pointer data) {
|
int callback(final Pointer instance, final Pointer data) {
|
||||||
if (menuItem != null) {
|
if (checkbox != null) {
|
||||||
final ActionListener cb = menuItem.getCallback();
|
final ActionListener cb = checkbox.getCallback();
|
||||||
if (cb != null) {
|
if (cb != null) {
|
||||||
try {
|
try {
|
||||||
Gtk.proxyClick(menuItem, cb);
|
Gtk.proxyClick(checkbox, cb);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
SystemTray.logger.error("Error calling menu entry checkbox {} click event.", menuItem.getText(), e);
|
SystemTray.logger.error("Error calling menu entry checkbox {} click event.", checkbox.getText(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +86,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
void setSpacerImage(final Pointer _native, final boolean everyoneElseHasImages) {
|
void setSpacerImage(final boolean everyoneElseHasImages) {
|
||||||
// no op
|
// no op
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,8 +137,8 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setCallback(final Checkbox menuItem) {
|
void setCallback(final Checkbox checkbox) {
|
||||||
this.menuItem = menuItem;
|
this.checkbox = checkbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -162,16 +161,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
setText(menuItem);
|
setText(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void onDeleteMenu(final Pointer parentNative) {
|
|
||||||
onDeleteMenu(parentNative, _native);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu) {
|
|
||||||
onCreateMenu(parentNative, _native, hasImagesInMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
@SuppressWarnings("Duplicates")
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
@ -180,18 +169,15 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Gtk.gtk_container_remove(parent._nativeMenu, _native);
|
Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||||
Gtk.gtk_menu_shell_deactivate(parent._nativeMenu, _native);
|
|
||||||
|
|
||||||
GtkMenuItemCheckbox.super.remove();
|
GtkMenuItemCheckbox.super.remove();
|
||||||
|
|
||||||
menuItem = null;
|
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
Gtk.gtk_widget_destroy(image);
|
Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
|
||||||
image = null;
|
image = null;
|
||||||
}
|
}
|
||||||
|
checkbox = null;
|
||||||
Gtk.gtk_widget_destroy(_native);
|
|
||||||
|
|
||||||
parent.remove(GtkMenuItemCheckbox.this);
|
parent.remove(GtkMenuItemCheckbox.this);
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.systemTray.nativeUI;
|
package dorkbox.systemTray.nativeUI;
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.jna.linux.Gtk;
|
import dorkbox.systemTray.jna.linux.Gtk;
|
||||||
import dorkbox.systemTray.peer.EntryPeer;
|
import dorkbox.systemTray.peer.EntryPeer;
|
||||||
|
|
||||||
class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
||||||
|
|
||||||
private final GtkMenu parent;
|
private final GtkMenu parent;
|
||||||
private final Pointer _native = Gtk.gtk_separator_menu_item_new();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuItemSeparator(final GtkMenu parent) {
|
GtkMenuItemSeparator(final GtkMenu parent) {
|
||||||
|
super(Gtk.gtk_separator_menu_item_new());
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +39,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Gtk.gtk_container_remove(parent._nativeMenu, _native);
|
Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||||
Gtk.gtk_menu_shell_deactivate(parent._nativeMenu, _native);
|
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(_native);
|
|
||||||
|
|
||||||
parent.remove(GtkMenuItemSeparator.this);
|
parent.remove(GtkMenuItemSeparator.this);
|
||||||
}
|
}
|
||||||
@ -58,17 +52,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
void setSpacerImage(final Pointer _native, final boolean everyoneElseHasImages) {
|
void setSpacerImage(final boolean everyoneElseHasImages) {
|
||||||
// no op
|
// no op
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void onDeleteMenu(final Pointer parentNative) {
|
|
||||||
onDeleteMenu(parentNative, _native);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu) {
|
|
||||||
onCreateMenu(parentNative, _native, hasImagesInMenu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.systemTray.nativeUI;
|
package dorkbox.systemTray.nativeUI;
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
|
||||||
|
|
||||||
import dorkbox.systemTray.Status;
|
import dorkbox.systemTray.Status;
|
||||||
import dorkbox.systemTray.jna.linux.Gtk;
|
import dorkbox.systemTray.jna.linux.Gtk;
|
||||||
import dorkbox.systemTray.peer.StatusPeer;
|
import dorkbox.systemTray.peer.StatusPeer;
|
||||||
@ -26,14 +24,13 @@ import dorkbox.systemTray.peer.StatusPeer;
|
|||||||
class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
|
class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
|
||||||
|
|
||||||
private final GtkMenu parent;
|
private final GtkMenu parent;
|
||||||
private final Pointer _native = Gtk.gtk_image_menu_item_new_with_mnemonic("");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
*/
|
*/
|
||||||
GtkMenuItemStatus(final GtkMenu parent) {
|
GtkMenuItemStatus(final GtkMenu parent) {
|
||||||
super();
|
super(Gtk.gtk_image_menu_item_new_with_mnemonic(""));
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
|
||||||
// need that extra space so it matches windows/mac
|
// need that extra space so it matches windows/mac
|
||||||
@ -66,25 +63,12 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Gtk.gtk_container_remove(parent._nativeMenu, _native);
|
Gtk.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||||
Gtk.gtk_menu_shell_deactivate(parent._nativeMenu, _native);
|
|
||||||
|
|
||||||
GtkMenuItemStatus.super.remove();
|
GtkMenuItemStatus.super.remove();
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(_native);
|
|
||||||
|
|
||||||
parent.remove(GtkMenuItemStatus.this);
|
parent.remove(GtkMenuItemStatus.this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void onDeleteMenu(final Pointer parentNative) {
|
|
||||||
onDeleteMenu(parentNative, _native);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void onCreateMenu(final Pointer parentNative, final boolean hasImagesInMenu) {
|
|
||||||
onCreateMenu(parentNative, _native, hasImagesInMenu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
|
|||||||
Gtk.startGui();
|
Gtk.startGui();
|
||||||
|
|
||||||
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
|
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
|
||||||
final GtkMenu gtkMenu = new GtkMenu(null) {
|
final GtkMenu gtkMenu = new GtkMenu() {
|
||||||
/**
|
/**
|
||||||
* MUST BE AFTER THE ITEM IS ADDED/CHANGED from the menu
|
* MUST BE AFTER THE ITEM IS ADDED/CHANGED from the menu
|
||||||
*
|
*
|
||||||
|
@ -64,7 +64,7 @@ class _GtkStatusIconNativeTray extends Tray implements NativeUI {
|
|||||||
Gtk.startGui();
|
Gtk.startGui();
|
||||||
|
|
||||||
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
|
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
|
||||||
final GtkMenu gtkMenu = new GtkMenu(null) {
|
final GtkMenu gtkMenu = new GtkMenu() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setEnabled(final MenuItem menuItem) {
|
void setEnabled(final MenuItem menuItem) {
|
||||||
|
@ -287,7 +287,7 @@ class _AppIndicatorTray extends Tray implements SwingUI {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void callback(Pointer notUsed, final GdkEventButton event) {
|
void callback(Pointer notUsed, final GdkEventButton event) {
|
||||||
Gtk.gtk_widget_destroy(dummyMenu); // destroy the menu, so it will disappear (and we then have focus on our swing menu)
|
Gtk.gtk_menu_shell_deactivate(dummyMenu);
|
||||||
SwingUtil.invokeLater(popupRunnable);
|
SwingUtil.invokeLater(popupRunnable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user