Notification of accelerator changes
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list gtk org
- Cc: timj gtk org
- Subject: Notification of accelerator changes
- Date: 03 Mar 2001 22:50:33 -0500
Another problem that I ran into while working on plug/socket was a
need to keep track of what accelerators were registered for a given
toplevel.
This needs to be tracked for GtkPlug to keep track of what
accelerators it needs to pass to its parent GtkSocket and from there
to the actual toplevel.
[
The way that the nascent XEMBED spec works is that all
accelerators are registered with the toplevel and when
a keystroke for one of the accelerators is detected,
it sends it to the appriopriate child, bypassing normal
key event propagation.
]
My first attempt at this is appended.
What I did was:
* Made GtkAccelGroup a GObject
* Added a ::changed signal to GtkAccelGroup for notification
when the set of accelerators for a GtkAccelGroup changes.
In order to keep this reasonably efficient, I emit the
signal out of high-priority (GDK_PRIORITY_EVENTS - 1)
idle handler.
* Added a method:
void gtk_accel_group_get_entries (GtkAccelGroup *accel_group,
GtkAccelEntry **entries,
gint *n_entries);
* Add a ->accel_entries_changed() virtual function (not
signal) to GtkWindow that is called when accelerator
groups are added and removed from the window, and
when any of the attached accelerator groups emits
::changed.
The code also includes a change to check accelerators _before_
sending events to the focused widget, instead of after. The
rational for this is:
- Accelerators are visibly documented, while keybindings beyond
the most basic are invisible and known only to power users.
Hence, the user's expectation that accelerators work is
stronger; it looks more like a bug for an accelerator not
to work than for a keybinding to be overriden by an accelerator.
- It corresponds to what other toolkits do, so provides more
opportunity for integration.
In a properly designed application, this change should be make no
difference, since there should be no collisions. But in case of
collisions, I believe it is correct for the accelerators to win.
In other words, the accelerator shalt smite the key bindings and
through its puissance render them vanquished. (*)
I'm not entirely comfortable with notification part of the patch,
since it seems hacky, and I see little general application for this
facility. There is some argument that it should be documented as an
internal implementation detail of GTK+ for use with GtkPlug alone.
If people (especially Tim) have ideas about how to better structure
this, I'd be glad to hear them.
Regards,
Owen
(*) This paragraph dedicated to jrb
Index: gtk/gtk-boxed.defs
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtk-boxed.defs,v
retrieving revision 1.12
diff -u -r1.12 gtk-boxed.defs
--- gtk/gtk-boxed.defs 2001/01/01 20:26:10 1.12
+++ gtk/gtk-boxed.defs 2001/03/03 18:50:31
@@ -9,10 +9,6 @@
;;; Gtk boxed types
-(define-boxed GtkAccelGroup
- gtk_accel_group_ref
- gtk_accel_group_unref)
-
(define-boxed GtkSelectionData
gtk_selection_data_copy
gtk_selection_data_free)
Index: gtk/gtkaccelgroup.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkaccelgroup.c,v
retrieving revision 1.20
diff -u -r1.20 gtkaccelgroup.c
--- gtk/gtkaccelgroup.c 2000/10/25 22:34:11 1.20
+++ gtk/gtkaccelgroup.c 2001/03/03 18:50:31
@@ -31,9 +31,14 @@
#include <string.h>
#include "gtkaccelgroup.h"
#include "gdk/gdkkeysyms.h"
+#include "gtkmarshal.h"
#include "gtksignal.h"
#include "gtkwidget.h"
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
/* --- signals --- */
typedef void (*GtkSignalAddAccelerator) (GtkObject *object,
@@ -59,9 +64,10 @@
static const gchar *accel_entries_key = "gtk-accel-entries";
static guint accel_entries_key_id = 0;
static GHashTable *accel_entry_hash_table = NULL;
-static GMemChunk *accel_tables_mem_chunk = NULL;
static GMemChunk *accel_entries_mem_chunk = NULL;
+static GObjectClass *parent_class;
+static guint signals[LAST_SIGNAL] = { 0 };
/* --- functions --- */
static gint
@@ -95,11 +101,9 @@
return h;
}
-GtkAccelGroup*
-gtk_accel_group_new (void)
+void
+gtk_accel_group_init (GtkAccelGroup *accel_group)
{
- GtkAccelGroup *accel_group;
-
if (!accel_groups_key_id)
{
accel_groups_key_id = g_quark_from_static_string (accel_groups_key);
@@ -107,22 +111,78 @@
accel_entry_hash_table = g_hash_table_new (gtk_accel_entries_hash,
gtk_accel_entries_equal);
-
- accel_tables_mem_chunk = g_mem_chunk_create (GtkAccelGroup, 8, G_ALLOC_AND_FREE);
+
accel_entries_mem_chunk = g_mem_chunk_create (GtkAccelEntry, 64, G_ALLOC_AND_FREE);
}
- accel_group = g_chunk_new (GtkAccelGroup, accel_tables_mem_chunk);
-
- accel_group->ref_count = 1;
accel_group->lock_count = 0;
accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
accel_group->attach_objects = NULL;
+}
+
+void
+gtk_accel_group_finalize (GObject *object)
+{
+ GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
+
+ if (accel_group->changed_idle)
+ {
+ g_source_remove (accel_group->changed_idle);
+ accel_group->changed_idle = 0;
+ }
+}
+
+void
+gtk_accel_group_class_init (GtkAccelGroupClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = gtk_accel_group_finalize;
+
+ signals[CHANGED] = g_signal_newc ("changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkAccelGroupClass, changed),
+ NULL,
+ gtk_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+}
+
+GType
+gtk_accel_group_get_type (void)
+{
+ static GtkType accel_group_type = 0;
+
+ if (!accel_group_type)
+ {
+ static const GTypeInfo accel_group_info =
+ {
+ sizeof (GtkAccelGroupClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_accel_group_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkAccelGroup),
+ 8, /* n_preallocs */
+ (GInstanceInitFunc) gtk_accel_group_init,
+ };
+
+ accel_group_type = g_type_register_static (G_TYPE_OBJECT, "GtkAccelGroup", &accel_group_info, 0);
+ }
- return accel_group;
+ return accel_group_type;
}
GtkAccelGroup*
+gtk_accel_group_new (void)
+{
+ return GTK_ACCEL_GROUP (g_object_new (GTK_TYPE_ACCEL_GROUP, NULL));
+}
+
+GtkAccelGroup*
gtk_accel_group_get_default (void)
{
if (!default_accel_group)
@@ -134,26 +194,40 @@
GtkAccelGroup*
gtk_accel_group_ref (GtkAccelGroup *accel_group)
{
- g_return_val_if_fail (accel_group != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
- accel_group->ref_count += 1;
-
- return accel_group;
+ return (GtkAccelGroup *)g_object_ref (G_OBJECT (accel_group));
}
void
gtk_accel_group_unref (GtkAccelGroup *accel_group)
{
- g_return_if_fail (accel_group != NULL);
- g_return_if_fail (accel_group->ref_count > 0);
-
- accel_group->ref_count -= 1;
- if (accel_group->ref_count == 0)
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+
+ g_object_unref (G_OBJECT (accel_group));
+}
+
+static gboolean
+gtk_accel_group_changed_idle (gpointer data)
+{
+ GtkAccelGroup *accel_group = data;
+
+ accel_group->changed_idle = 0;
+
+ g_signal_emit (accel_group, signals[CHANGED], 0);
+
+ return FALSE;
+}
+
+static void
+gtk_accel_group_entries_changed (GtkAccelGroup *accel_group)
+{
+ if (!accel_group->changed_idle)
{
- g_return_if_fail (accel_group != default_accel_group);
- g_return_if_fail (accel_group->attach_objects == NULL);
-
- g_chunk_free (accel_group, accel_tables_mem_chunk);
+ accel_group->changed_idle = g_idle_add_full (GDK_PRIORITY_EVENTS - 1,
+ gtk_accel_group_changed_idle,
+ accel_group,
+ NULL);
}
}
@@ -495,6 +569,8 @@
entry = slist->data;
g_hash_table_remove (accel_entry_hash_table, entry);
+
+ gtk_accel_group_entries_changed (entry->accel_group);
gtk_accel_group_unref (entry->accel_group);
g_chunk_free (entry, accel_entries_mem_chunk);
}
@@ -544,6 +620,8 @@
NULL);
slist = g_slist_prepend (slist, entry);
gtk_object_set_data_by_id (object, accel_entries_key_id, slist);
+
+ gtk_accel_group_entries_changed (accel_group);
}
}
@@ -634,7 +712,8 @@
GTK_SIGNAL_FUNC (gtk_accel_group_delete_entries),
NULL);
gtk_object_set_data_by_id (object, accel_entries_key_id, slist);
-
+
+ gtk_accel_group_entries_changed (accel_group);
gtk_accel_group_unref (accel_group);
g_chunk_free (entry, accel_entries_mem_chunk);
@@ -702,6 +781,46 @@
return gtk_object_get_data_by_id (object, accel_entries_key_id);
}
+
+
+typedef struct {
+ GtkAccelGroup *accel_group;
+ GArray *out;
+} GetEntriesInfo;
+
+static void
+get_entries_foreach (gpointer key, gpointer val, gpointer user_data)
+{
+ GetEntriesInfo *info = user_data;
+ GtkAccelEntry *entry = val;
+
+ if (entry->accel_group == info->accel_group)
+ g_array_append_val (info->out, *entry);
+}
+
+void
+gtk_accel_group_get_entries (GtkAccelGroup *accel_group,
+ GtkAccelEntry **entries,
+ gint *n_entries)
+{
+ GetEntriesInfo info;
+
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+
+ info.accel_group = accel_group;
+ info.out = g_array_new (FALSE, FALSE, sizeof (GtkAccelEntry));
+
+ g_hash_table_foreach (accel_entry_hash_table, get_entries_foreach, &info);
+
+ if (n_entries)
+ *n_entries = info.out->len;
+
+ if (entries)
+ *entries = (GtkAccelEntry *)info.out->data;
+
+ g_array_free (info.out, entries == NULL);
+}
+
gboolean
gtk_accelerator_valid (guint keyval,
Index: gtk/gtkaccelgroup.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkaccelgroup.h,v
retrieving revision 1.9
diff -u -r1.9 gtkaccelgroup.h
--- gtk/gtkaccelgroup.h 2000/08/30 00:33:36 1.9
+++ gtk/gtkaccelgroup.h 2001/03/03 18:50:31
@@ -41,8 +41,16 @@
#endif /* __cplusplus */
-typedef struct _GtkAccelGroup GtkAccelGroup;
-typedef struct _GtkAccelEntry GtkAccelEntry;
+#define GTK_TYPE_ACCEL_GROUP (gtk_accel_group_get_type ())
+#define GTK_ACCEL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ACCEL_GROUP, GtkAccelGroup))
+#define GTK_ACCEL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ACCEL_GROUP, GtkAccelGroupClass))
+#define GTK_IS_ACCEL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ACCEL_GROUP))
+#define GTK_IS_ACCEL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ACCEL_GROUP))
+#define GTK_ACCEL_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ACCEL_GROUP, GtkAccelGroupClass))
+
+typedef struct _GtkAccelGroup GtkAccelGroup;
+typedef struct _GtkAccelGroupClass GtkAccelGroupClass;
+typedef struct _GtkAccelEntry GtkAccelEntry;
typedef enum
{
@@ -63,10 +71,19 @@
struct _GtkAccelGroup
{
- guint ref_count;
+ GObject parent_instance;
+
guint lock_count;
GdkModifierType modifier_mask;
GSList *attach_objects;
+ guint changed_idle;
+};
+
+struct _GtkAccelGroupClass
+{
+ GObjectClass parent_class;
+
+ void (*changed) (GtkAccelGroup *accel_group);
};
struct _GtkAccelEntry
@@ -98,6 +115,7 @@
/* Accelerator Groups
*/
+GType gtk_accel_group_get_type (void);
GtkAccelGroup* gtk_accel_group_new (void);
GtkAccelGroup* gtk_accel_group_get_default (void);
GtkAccelGroup* gtk_accel_group_ref (GtkAccelGroup *accel_group);
@@ -163,8 +181,10 @@
*/
GSList* gtk_accel_groups_from_object (GtkObject *object);
GSList* gtk_accel_group_entries_from_object (GtkObject *object);
-
+void gtk_accel_group_get_entries (GtkAccelGroup *accel_group,
+ GtkAccelEntry **entries,
+ gint *n_entries);
#ifdef __cplusplus
}
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.99
diff -u -r1.99 gtkwindow.c
--- gtk/gtkwindow.c 2001/02/28 19:07:46 1.99
+++ gtk/gtkwindow.c 2001/03/03 18:50:31
@@ -617,15 +617,46 @@
gtk_widget_queue_resize (GTK_WIDGET (window));
}
+static void
+accel_entries_changed (GtkAccelGroup *accel_group, GtkWindow *window)
+{
+ GtkWindowClass *class = GTK_WINDOW_GET_CLASS (window);
+
+ if (class->accel_entries_changed)
+ class->accel_entries_changed (window);
+}
+
+static void
+disconnect_accel_entries_changed (GtkWindow *window, GtkAccelGroup *accel_group)
+{
+ GtkWindowClass *class = GTK_WINDOW_GET_CLASS (window);
+
+ g_signal_handlers_disconnect_matched (accel_group, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, accel_entries_changed, window);
+
+ if (class->accel_entries_changed)
+ class->accel_entries_changed (window);
+}
+
void
gtk_window_add_accel_group (GtkWindow *window,
GtkAccelGroup *accel_group)
{
+ GtkWindowClass *class;
+
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (accel_group != NULL);
+ g_signal_connect_data (accel_group, "changed", accel_entries_changed, window, NULL, FALSE, FALSE);
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (disconnect_accel_entries_changed), accel_group);
+
gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+
+ class = GTK_WINDOW_GET_CLASS (window);
+ if (class->accel_entries_changed)
+ class->accel_entries_changed (window);
}
void
@@ -636,6 +667,8 @@
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (accel_group != NULL);
+ disconnect_accel_entries_changed (window, accel_group);
+
gtk_accel_group_detach (accel_group, GTK_OBJECT (window));
}
@@ -1642,9 +1675,10 @@
window = GTK_WINDOW (widget);
- handled = FALSE;
-
- if (window->focus_widget &&
+ handled = gtk_accel_groups_activate (GTK_OBJECT (window), event->keyval, event->state);
+
+ if (!handled &&
+ window->focus_widget &&
window->focus_widget != widget &&
GTK_WIDGET_IS_SENSITIVE (window->focus_widget))
{
@@ -1652,9 +1686,6 @@
}
if (!handled)
- handled = gtk_accel_groups_activate (GTK_OBJECT (window), event->keyval, event->state);
-
- if (!handled)
{
switch (event->keyval)
{
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.26
diff -u -r1.26 gtkwindow.h
--- gtk/gtkwindow.h 2001/02/27 20:40:14 1.26
+++ gtk/gtkwindow.h 2001/03/03 18:50:31
@@ -104,6 +104,7 @@
GtkWidget *focus);
gboolean (* frame_event) (GtkWidget *widget,
GdkEvent *event);
+ void (* accel_entries_changed) (GtkWindow *window);
};
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]