X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbindings.c;h=4cbbeaf6aadbb67cd9e0c9981f00b256363ef54a;hb=b2340254109d8599244c875fe7bc5e234f64da08;hp=f6e56862a0d0005a96eb2971008220b341daa4f3;hpb=dcf3430e17d3706285e07496f3105df896977e52;p=~andy%2Fgtk diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c index f6e56862a..4cbbeaf6a 100644 --- a/gtk/gtkbindings.c +++ b/gtk/gtkbindings.c @@ -15,9 +15,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -33,10 +31,108 @@ #include "gtkbindingsprivate.h" #include "gtkkeyhash.h" +#include "gtkstylecontext.h" #include "gtkwidget.h" -#include "gtkrc.h" #include "gtkintl.h" +/** + * SECTION:gtkbindings + * @Title: Bindings + * @Short_description: Key bindings for individual widgets + * @See_also: Keyboard Accelerators, Mnemonics, #GtkCssProvider + * + * #GtkBindingSet provides a mechanism for configuring GTK+ key bindings + * through CSS files. This eases key binding adjustments for application + * developers as well as users and provides GTK+ users or administrators + * with high key binding configurability which requires no application + * or toolkit side changes. + * + * + * Installing a key binding + * + * A CSS file binding consists of a 'binding-set' definition and a match + * statement to apply the binding set to specific widget types. Details + * on the matching mechanism are described under + * Selectors + * in the #GtkCssProvider documentation. Inside the binding set definition, + * key combinations are bound to one or more specific signal emissions on + * the target widget. Key combinations are strings consisting of an optional + * #GdkModifierType name and key names + * such as those defined in <gdk/gdkkeysyms.h> + * or returned from gdk_keyval_name(), they have to be parsable by + * gtk_accelerator_parse(). Specifications of signal emissions consist + * of a string identifying the signal name, and a list of signal specific + * arguments in parenthesis. + * + * + * For example for binding Control and the left or right cursor keys + * of a #GtkEntry widget to the #GtkEntry::move-cursor signal (so movement + * occurs in 3-character steps), the following binding can be used: + * + * @binding-set MoveCursor3 + * { + * bind "<Control>Right" { "move-cursor" (visual-positions, 3, 0) }; + * bind "<Control>Left" { "move-cursor" (visual-positions, -3, 0) }; + * }; + * GtkEntry + * { + * gtk-key-bindings: MoveCursor3 + * } + * + * + * + * + * Unbinding existing key bindings + * + * GTK+ already defines a number of useful bindings for the widgets + * it provides. Because custom bindings set up in CSS files take + * precedence over the default bindings shipped with GTK+, overriding + * existing bindings as demonstrated in + * Installing a key binding + * works as expected. The same mechanism can not be used to "unbind" + * existing bindings, however. + * + * @binding-set MoveCursor3 + * { + * bind "<Control>Right" { }; + * bind "<Control>Left" { }; + * }; + * GtkEntry + * { + * gtk-key-bindings: MoveCursor3 + * } + * + * The above example will not have the desired effect of causing + * "<Control>Right" and "<Control>Left" key presses to + * be ignored by GTK+. Instead, it just causes any existing bindings + * from the bindings set "MoveCursor3" to be deleted, so when + * "<Control>Right" or "<Control>Left" are pressed, no + * binding for these keys is found in binding set "MoveCursor3". + * GTK+ will thus continue to search for matching key bindings, and will + * eventually lookup and find the default GTK+ bindings for entries which + * implement word movement. To keep GTK+ from activating its default + * bindings, the "unbind" keyword can be used like this: + * + * @binding-set MoveCursor3 + * { + * unbind "<Control>Right"; + * unbind "<Control>Left"; + * }; + * GtkEntry + * { + * gtk-key-bindings: MoveCursor3 + * } + * + * Now, GTK+ will find a match when looking up "<Control>Right" + * and "<Control>Left" key presses before it resorts to its default + * bindings, and the match instructs it to abort ("unbind") the search, + * so the key presses are not consumed by this widget. As usual, further + * processing of the key presses, e.g. by an entry's parent widget, is + * now possible. + * + * + */ + /* --- defines --- */ #define BINDING_MOD_MASK() (gtk_accelerator_get_default_mod_mask () | GDK_RELEASE_MASK) @@ -100,7 +196,7 @@ binding_signal_new (const gchar *signal_name, signal->signal_name = (gchar *)g_intern_string (signal_name); signal->n_args = n_args; signal->args = (GtkBindingArg *)(signal + 1); - + return signal; } @@ -108,7 +204,7 @@ static void binding_signal_free (GtkBindingSignal *sig) { guint i; - + for (i = 0; i < sig->n_args; i++) { if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING) @@ -144,7 +240,7 @@ binding_key_hash_insert_entry (GtkKeyHash *key_hash, GtkBindingEntry *entry) { guint keyval = entry->keyval; - + /* We store lowercased accelerators. To deal with this, if * was specified, uppercase. */ @@ -155,7 +251,7 @@ binding_key_hash_insert_entry (GtkKeyHash *key_hash, else keyval = gdk_keyval_to_upper (keyval); } - + _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers & ~GDK_RELEASE_MASK, entry); } @@ -163,7 +259,7 @@ static void binding_key_hash_destroy (gpointer data) { GtkKeyHash *key_hash = data; - + binding_key_hashes = g_slist_remove (binding_key_hashes, key_hash); _gtk_key_hash_free (key_hash); } @@ -188,7 +284,7 @@ binding_key_hash_for_keymap (GdkKeymap *keymap) if (!key_hash_quark) key_hash_quark = g_quark_from_static_string ("gtk-binding-key-hash"); - + key_hash = g_object_get_qdata (G_OBJECT (keymap), key_hash_quark); if (!key_hash) @@ -215,7 +311,7 @@ binding_entry_new (GtkBindingSet *binding_set, { GSList *tmp_list; GtkBindingEntry *entry; - + if (!binding_entry_hash_table) binding_entry_hash_table = g_hash_table_new (binding_entry_hash, binding_entries_compare); @@ -256,12 +352,12 @@ binding_entry_free (GtkBindingEntry *entry) entry->destroyed == TRUE); entry->destroyed = FALSE; - + sig = entry->signals; while (sig) { GtkBindingSignal *prev; - + prev = sig; sig = prev->next; binding_signal_free (prev); @@ -296,7 +392,7 @@ binding_entry_destroy (GtkBindingEntry *entry) tmp = last->set_next; } entry->set_next = NULL; - + o_entry = g_hash_table_lookup (binding_entry_hash_table, entry); begin = o_entry; last = NULL; @@ -315,7 +411,7 @@ binding_entry_destroy (GtkBindingEntry *entry) tmp = last->hash_next; } entry->hash_next = NULL; - + if (!begin) g_hash_table_remove (binding_entry_hash_table, entry); else if (begin != o_entry) @@ -343,13 +439,13 @@ binding_ht_lookup_entry (GtkBindingSet *set, { GtkBindingEntry lookup_entry = { 0 }; GtkBindingEntry *entry; - + if (!binding_entry_hash_table) return NULL; - + lookup_entry.keyval = keyval; lookup_entry.modifiers = modifiers; - + entry = g_hash_table_lookup (binding_entry_hash_table, &lookup_entry); for (; entry; entry = entry->hash_next) if (entry->binding_set == set) @@ -368,7 +464,7 @@ binding_compose_params (GObject *object, const GType *types; guint i; gboolean valid; - + params = g_new0 (GValue, query->n_params + 1); *params_p = params; @@ -377,12 +473,12 @@ binding_compose_params (GObject *object, g_value_init (params, G_TYPE_OBJECT); g_value_set_object (params, G_OBJECT (object)); params++; - + types = query->param_types; valid = TRUE; for (i = 1; i < query->n_params + 1 && valid; i++) { - GValue tmp_value = { 0, }; + GValue tmp_value = G_VALUE_INIT; g_value_init (params, *types); @@ -466,23 +562,23 @@ binding_compose_params (GObject *object, g_value_unset (&tmp_value); } - + types++; params++; args++; } - + if (!valid) { guint j; for (j = 0; j < i; j++) g_value_unset (&(*params_p)[j]); - + g_free (*params_p); *params_p = NULL; } - + return valid; } @@ -494,20 +590,20 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, gboolean old_emission; gboolean handled = FALSE; gint i; - + old_emission = entry->in_emission; entry->in_emission = TRUE; - + g_object_ref (object); - + for (sig = entry->signals; sig; sig = sig->next) { GSignalQuery query; guint signal_id; GValue *params = NULL; - GValue return_val = { 0, }; + GValue return_val = G_VALUE_INIT; gchar *accelerator = NULL; - + signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object)); if (!signal_id) { @@ -551,7 +647,7 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, if (query.return_type == G_TYPE_BOOLEAN) g_value_init (&return_val, G_TYPE_BOOLEAN); - + g_signal_emitv (params, signal_id, 0, &return_val); if (query.return_type == G_TYPE_BOOLEAN) @@ -562,15 +658,15 @@ gtk_binding_entry_activate (GtkBindingEntry *entry, } else handled = TRUE; - + for (i = 0; i < query.n_params + 1; i++) g_value_unset (¶ms[i]); g_free (params); - + if (entry->destroyed) break; } - + g_object_unref (object); entry->in_emission = old_emission; @@ -593,9 +689,9 @@ GtkBindingSet* gtk_binding_set_new (const gchar *set_name) { GtkBindingSet *binding_set; - + g_return_val_if_fail (set_name != NULL, NULL); - + binding_set = g_new (GtkBindingSet, 1); binding_set->set_name = (gchar *) g_intern_string (set_name); binding_set->widget_path_pspecs = NULL; @@ -604,9 +700,9 @@ gtk_binding_set_new (const gchar *set_name) binding_set->entries = NULL; binding_set->current = NULL; binding_set->parsed = FALSE; - + binding_set_list = g_slist_prepend (binding_set_list, binding_set); - + return binding_set; } @@ -638,41 +734,45 @@ gtk_binding_set_by_class (gpointer object_class) return binding_set; binding_set = gtk_binding_set_new (g_type_name (G_OBJECT_CLASS_TYPE (class))); - gtk_binding_set_add_path (binding_set, - GTK_PATH_CLASS, - g_type_name (G_OBJECT_CLASS_TYPE (class)), - GTK_PATH_PRIO_GTK); g_dataset_id_set_data (class, key_id_class_binding_set, binding_set); return binding_set; } +static GtkBindingSet* +gtk_binding_set_find_interned (const gchar *set_name) +{ + GSList *slist; + + for (slist = binding_set_list; slist; slist = slist->next) + { + GtkBindingSet *binding_set; + + binding_set = slist->data; + if (binding_set->set_name == set_name) + return binding_set; + } + + return NULL; +} + /** * gtk_binding_set_find: * @set_name: unique binding set name * - * Find a binding set by its globally unique name. The @set_name can - * either be a name used for gtk_binding_set_new() or the type name of - * a class used in gtk_binding_set_by_class(). + * Find a binding set by its globally unique name. + * + * The @set_name can either be a name used for gtk_binding_set_new() + * or the type name of a class used in gtk_binding_set_by_class(). * * Return value: (transfer none): %NULL or the specified binding set */ GtkBindingSet* gtk_binding_set_find (const gchar *set_name) { - GSList *slist; - g_return_val_if_fail (set_name != NULL, NULL); - - for (slist = binding_set_list; slist; slist = slist->next) - { - GtkBindingSet *binding_set; - - binding_set = slist->data; - if (g_str_equal (binding_set->set_name, (gpointer) set_name)) - return binding_set; - } - return NULL; + + return gtk_binding_set_find_interned (g_intern_string (set_name)); } /** @@ -694,17 +794,17 @@ gtk_binding_set_activate (GtkBindingSet *binding_set, GObject *object) { GtkBindingEntry *entry; - + g_return_val_if_fail (binding_set != NULL, FALSE); g_return_val_if_fail (G_IS_OBJECT (object), FALSE); - + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (entry) return gtk_binding_entry_activate (entry, object); - + return FALSE; } @@ -772,12 +872,12 @@ gtk_binding_entry_remove (GtkBindingSet *binding_set, GdkModifierType modifiers) { GtkBindingEntry *entry; - + g_return_if_fail (binding_set != NULL); - + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (entry) binding_entry_destroy (entry); @@ -819,20 +919,20 @@ _gtk_binding_entry_add_signall (GtkBindingSet *binding_set, GSList *slist; guint n = 0; GtkBindingArg *arg; - + g_return_if_fail (binding_set != NULL); g_return_if_fail (signal_name != NULL); - + keyval = gdk_keyval_to_lower (keyval); modifiers = modifiers & BINDING_MOD_MASK (); - + signal = binding_signal_new (signal_name, g_slist_length (binding_args)); - + arg = signal->args; for (slist = binding_args; slist; slist = slist->next) { GtkBindingArg *tmp_arg; - + tmp_arg = slist->data; if (!tmp_arg) { @@ -872,7 +972,7 @@ _gtk_binding_entry_add_signall (GtkBindingSet *binding_set, arg++; n++; } - + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); if (!entry) { @@ -892,7 +992,7 @@ _gtk_binding_entry_add_signall (GtkBindingSet *binding_set, * @modifiers: key modifier of binding to install * @signal_name: signal to execute upon activation * @n_args: number of arguments to @signal_name - * @Varargs: arguments to @signal_name + * @...: arguments to @signal_name * * Override or install a new key binding for @keyval with @modifiers on * @binding_set. When the binding is activated, @signal_name will be @@ -913,7 +1013,7 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, g_return_if_fail (binding_set != NULL); g_return_if_fail (signal_name != NULL); - + va_start (args, n_args); slist = NULL; for (i = 0; i < n_args; i++) @@ -1254,18 +1354,18 @@ create_signal_scanner (void) * Parses a signal description from @signal_desc and incorporates * it into @binding_set. * - * signal descriptions may either bind a key combination to - * a signal: + * Signal descriptions may either bind a key combination to + * one or more signals: * - * bind key { - * signalname (param, ...) + * bind "key" { + * "signalname" (param, ...) * ... * } * * * Or they may also unbind a key combination: * - * unbind key + * unbind "key" * * * Key combinations must be in a format that can be parsed by @@ -1302,32 +1402,34 @@ gtk_binding_entry_add_signal_from_string (GtkBindingSet *binding_set, /** * gtk_binding_set_add_path: - * @binding_set: a #GtkBindingSet to add a path to - * @path_type: path type the pattern applies to + * @binding_set: a #GtkBindingSet to add a path to + * @path_type: path type the pattern applies to * @path_pattern: the actual match pattern - * @priority: binding priority + * @priority: binding priority * - * This function is used internally by the GtkRC parsing mechanism to - * assign match patterns to #GtkBindingSet structures. + * This function was used internally by the GtkRC parsing mechanism + * to assign match patterns to #GtkBindingSet structures. + * + * In GTK+ 3, these match patterns are unused. * * Deprecated: 3.0 */ void -gtk_binding_set_add_path (GtkBindingSet *binding_set, - GtkPathType path_type, - const gchar *path_pattern, - GtkPathPriorityType priority) +gtk_binding_set_add_path (GtkBindingSet *binding_set, + GtkPathType path_type, + const gchar *path_pattern, + GtkPathPriorityType priority) { PatternSpec *pspec; GSList **slist_p, *slist; static guint seq_id = 0; - + g_return_if_fail (binding_set != NULL); g_return_if_fail (path_pattern != NULL); g_return_if_fail (priority <= GTK_PATH_PRIO_MASK); priority &= GTK_PATH_PRIO_MASK; - + switch (path_type) { case GTK_PATH_WIDGET: @@ -1344,7 +1446,7 @@ gtk_binding_set_add_path (GtkBindingSet *binding_set, slist_p = NULL; break; } - + pspec = g_new (PatternSpec, 1); pspec->type = path_type; if (path_type == GTK_PATH_WIDGET_CLASS) @@ -1354,15 +1456,15 @@ gtk_binding_set_add_path (GtkBindingSet *binding_set, pspec->seq_id = priority << 28; pspec->user_data = binding_set; - + slist = *slist_p; while (slist) { PatternSpec *tmp_pspec; - + tmp_pspec = slist->data; slist = slist->next; - + if (g_pattern_spec_equal (tmp_pspec->pspec, pspec->pspec)) { GtkPathPriorityType lprio = tmp_pspec->seq_id >> 28; @@ -1425,9 +1527,9 @@ binding_activate (GtkBindingSet *binding_set, } static gboolean -gtk_bindings_activate_list (GObject *object, - GSList *entries, - gboolean is_release) +gtk_bindings_activate_list (GObject *object, + GSList *entries, + gboolean is_release) { GtkStyleContext *context; GtkBindingSet *binding_set; @@ -1473,7 +1575,7 @@ gtk_bindings_activate_list (GObject *object, while (class_type && !handled) { - binding_set = gtk_binding_set_find (g_type_name (class_type)); + binding_set = gtk_binding_set_find_interned (g_type_name (class_type)); class_type = g_type_parent (class_type); if (!binding_set) @@ -1521,7 +1623,7 @@ gtk_bindings_activate (GObject *object, display = gtk_widget_get_display (GTK_WIDGET (object)); key_hash = binding_key_hash_for_keymap (gdk_keymap_get_for_display (display)); - + entries = _gtk_key_hash_lookup_keyval (key_hash, keyval, modifiers); handled = gtk_bindings_activate_list (object, entries, is_release); @@ -1535,10 +1637,10 @@ gtk_bindings_activate (GObject *object, * gtk_bindings_activate_event: * @object: a #GObject (generally must be a widget) * @event: a #GdkEventKey - * + * * Looks up key bindings for @object to find one matching * @event, and if one was found, activate it. - * + * * Return value: %TRUE if a matching key binding was found * * Since: 2.4 @@ -1563,7 +1665,7 @@ gtk_bindings_activate_event (GObject *object, event->state, BINDING_MOD_MASK () & ~GDK_RELEASE_MASK, event->group); - + handled = gtk_bindings_activate_list (object, entries, event->type == GDK_KEY_RELEASE);