From 687035c3f1ef882231fa0a3c6813f26b829f3ad8 Mon Sep 17 00:00:00 2001 From: nathan Date: Sat, 21 Jan 2017 14:58:49 +0100 Subject: [PATCH] Proper fix for checkbox state activating callbacks in GTK. --- src/dorkbox/systemTray/jna/linux/Gobject.java | 7 ++++- .../nativeUI/GtkMenuItemCheckbox.java | 31 +++++-------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/dorkbox/systemTray/jna/linux/Gobject.java b/src/dorkbox/systemTray/jna/linux/Gobject.java index 413e45e..1b01385 100644 --- a/src/dorkbox/systemTray/jna/linux/Gobject.java +++ b/src/dorkbox/systemTray/jna/linux/Gobject.java @@ -43,6 +43,7 @@ class Gobject { } } + // objdump -T /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 | grep block public static native void g_object_get(Pointer object, String objectName, PointerByReference objectVal, Pointer nullValue); @@ -51,5 +52,9 @@ class Gobject { public static native void g_object_force_floating(Pointer object); public static native void g_object_ref_sink(Pointer object); - public static native void g_signal_connect_object(Pointer instance, String detailed_signal, Callback c_handler, Pointer object, int connect_flags); + // 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); } diff --git a/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java b/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java index 24ce52a..2227df7 100644 --- a/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java +++ b/src/dorkbox/systemTray/nativeUI/GtkMenuItemCheckbox.java @@ -17,7 +17,6 @@ package dorkbox.systemTray.nativeUI; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.concurrent.atomic.AtomicBoolean; import com.sun.jna.Pointer; @@ -41,6 +40,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall // AppIndicators will only show if you use the keyboard to navigate // GtkStatusIconTray will show on mouse+keyboard movement private volatile char mnemonicKey = 0; + private final long handlerId; /** * called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it! @@ -50,7 +50,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall super(Gtk.gtk_check_menu_item_new_with_mnemonic("")); this.parent = parent; - Gobject.g_signal_connect_object(_native, "activate", this, null, 0); + handlerId = Gobject.g_signal_connect_object(_native, "activate", this, null, 0); } // called by native code ONLY @@ -120,10 +120,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall }); } - AtomicBoolean triggeredSetChecked = new AtomicBoolean(false); - - - @SuppressWarnings({"Duplicates", "StatementWithEmptyBody"}) @Override public @@ -135,14 +131,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall @Override public void actionPerformed(ActionEvent e) { - if (triggeredSetChecked.getAndSet(false)) { - // note: changing the state of the checkbox will trigger "activate", which will then trigger the callback. We don't want that. - // we assume this is consistent across ALL versions and variants of GTK - // https://github.com/GNOME/gtk/blob/master/gtk/gtkcheckmenuitem.c#L317 - return; - } - - // this will run on the EDT, since we are calling it from the EDT + // 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) @@ -156,7 +145,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall } } }; - } } @@ -173,18 +161,13 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall @Override public void run() { - // note: this will trigger "activate", which will then trigger the callback. We don't want to do that, so we hack around it. - // BECAUSE we are on the GTK thread, the "set_active" call is QUEUED... so we check the callback state AFTER this. + // note: this will trigger "activate", which will then trigger the callback. // we assume this is consistent across ALL versions and variants of GTK // 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); - - - if (callback != null) { - // only need to worry about this if we have a callback specified. - // This is because of how GTK handles setting the checkbox state + callbacks - triggeredSetChecked.set(true); - } + Gobject.g_signal_handler_unblock(_native, handlerId); } }); }