Fixed issues with calling gtk_widget_destroy too much. Fixed

threading issue with isDispatch.
This commit is contained in:
nathan 2016-12-23 01:17:25 +01:00
parent 7c6f3ca77b
commit a3118a07f4
10 changed files with 150 additions and 198 deletions

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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,27 +90,28 @@ 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);
}
Gtk.gtk_widget_destroy(_nativeMenu);
} }
Gtk.gtk_widget_destroy(_nativeMenu);
} }
if (parent != null) { if (parent != null) {
@ -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,62 +144,59 @@ 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 for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
synchronized (menuEntries) { final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) { hasImages |= menuEntry__.hasImage();
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i); }
hasImages |= menuEntry__.hasImage();
}
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) { for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
// the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images // the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i); final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
menuEntry__.onCreateMenu(_nativeMenu, hasImages); menuEntry__.onCreateMenu(_nativeMenu, hasImages);
if (menuEntry__ instanceof GtkMenu) { if (menuEntry__ instanceof GtkMenu) {
GtkMenu subMenu = (GtkMenu) menuEntry__; GtkMenu subMenu = (GtkMenu) menuEntry__;
if (subMenu.getParent() != GtkMenu.this) { if (subMenu.getParent() != GtkMenu.this) {
// we don't want to "createMenu" on our sub-menu that is assigned to us directly, as they are already doing it // we don't want to "createMenu" on our sub-menu that is assigned to us directly, as they are already doing it
subMenu.createMenu(); subMenu.createMenu();
}
} }
} }
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
// do this, errors will be had because indices don't line up anymore.
ArrayList<GtkBaseMenuItem> menuEntriesCopy = new ArrayList<GtkBaseMenuItem>(this.menuEntries);
for (int i = 0, menuEntriesSize = menuEntriesCopy.size(); i < menuEntriesSize; i++) { // a copy is made because sub-menus remove themselves from parents when .remove() is called. If we don't
final GtkBaseMenuItem menuEntry__ = menuEntriesCopy.get(i); // do this, errors will be had because indices don't line up anymore.
menuEntry__.remove(); ArrayList<GtkBaseMenuItem> menuEntriesCopy = new ArrayList<GtkBaseMenuItem>(menuEntries);
} menuEntries.clear();
this.menuEntries.clear();
menuEntriesCopy.clear();
Gtk.gtk_widget_destroy(_nativeMenu); for (int i = 0, menuEntriesSize = menuEntriesCopy.size(); i < menuEntriesSize; i++) {
_nativeMenu = null; final GtkBaseMenuItem menuEntry__ = menuEntriesCopy.get(i);
menuEntry__.remove();
} }
menuEntriesCopy.clear();
obliterateInProgress = false; Gtk.gtk_widget_destroy(_nativeMenu);
_nativeMenu = null;
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
} }
} }
}); });

View File

@ -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);
} }
}); });

View File

@ -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);
} }

View File

@ -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);
}
} }

View File

@ -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);
}
} }

View File

@ -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
* *

View File

@ -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) {

View File

@ -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);
} }
}; };