From 47aae53285bae0e5409b9976e0e828247f8cd383 Mon Sep 17 00:00:00 2001 From: Daniel Elstner Date: Fri, 16 Jan 2009 15:02:06 +0000 Subject: [PATCH] Remove the namespace prefix from functions defined locally only. Clean up * modules/input/gtkimcontextmultipress.[ch]: Remove the namespace prefix from functions defined locally only. Clean up the code and change indentation to match the GTK+ coding style. (_GtkImContextMultipress::key_sequences): Replace array of pointers by GHashTable. Adapt the implementation accordingly. (passthrough_enabled_for_window): Remove. The passthrough hack is no longer necessary thanks to the recently introduced "im-module" property of GtkEntry and GtkTextView. (load_config): Rework to implement an improved configuration file format. Just fetch all keys of the group instead of expecting the keys to be named a certain way. This also allows interpreting the config key itself as the GDK key name to bind the character sequence to, thereby making it independent of the sequence itself. * modules/input/im-multipress.conf: New example configuration using the new syntax. The example sequences are now bound to the numeric keypad and imitate the behavior of a standard mobile phone. svn path=/trunk/; revision=22125 --- ChangeLog | 20 + modules/input/gtkimcontextmultipress.c | 608 ++++++++++--------------- modules/input/gtkimcontextmultipress.h | 28 +- modules/input/im-multipress.conf | 39 +- 4 files changed, 283 insertions(+), 412 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4b68cb4a6..fc82236d5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2009-01-16 Daniel Elstner + + * modules/input/gtkimcontextmultipress.[ch]: Remove the namespace + prefix from functions defined locally only. Clean up the code and + change indentation to match the GTK+ coding style. + (_GtkImContextMultipress::key_sequences): Replace array of pointers + by GHashTable. Adapt the implementation accordingly. + (passthrough_enabled_for_window): Remove. The passthrough hack is + no longer necessary thanks to the recently introduced "im-module" + property of GtkEntry and GtkTextView. + (load_config): Rework to implement an improved configuration file + format. Just fetch all keys of the group instead of expecting the + keys to be named a certain way. This also allows interpreting the + config key itself as the GDK key name to bind the character sequence + to, thereby making it independent of the sequence itself. + + * modules/input/im-multipress.conf: New example configuration using + the new syntax. The example sequences are now bound to the numeric + keypad and imitate the behavior of a standard mobile phone. + 2009-01-15 Murray Cumming * gtk/gtkimcontext.c: documentation description: Mention the various diff --git a/modules/input/gtkimcontextmultipress.c b/modules/input/gtkimcontextmultipress.c index f0da538b3..947209810 100644 --- a/modules/input/gtkimcontextmultipress.c +++ b/modules/input/gtkimcontextmultipress.c @@ -1,5 +1,6 @@ -/* Copyright (C) 2006 Openismus GmbH - * +/* + * Copyright (c) 2006-2009 Openismus GmbH + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -16,37 +17,43 @@ * Boston, MA 02111-1307, USA. */ -#include +#include "gtkimcontextmultipress.h" #include #include #include #include -#include "gtkimcontextmultipress.h" +#include #define AUTOMATIC_COMPOSE_TIMEOUT 1 /* seconds */ - #define CONFIGURATION_FILENAME MULTIPRESS_CONFDIR G_DIR_SEPARATOR_S "im-multipress.conf" -#define MULTIPRESS_PASSTHROUGH_FLAG "multipress-passthrough-flag" - -/** This contains rows of characters that can be inputed by pressing a particular key repeatedly. - * Each row has one key (such as GDK_A), and an array of characters, such as 'a'. +/* This contains rows of characters that can be entered by pressing + * a particular key repeatedly. Each row has one key (such as GDK_a), + * and an array of character strings, such as "a". */ -struct _KeySequence +typedef struct { - gunichar key_press; /* Such as 'a' (== GDK_a) */ - gchar **characters; /* Array of strings. */ - gsize characters_length; /* size of the array of strings. */ -}; + gchar **characters; /* array of strings */ + gsize n_characters; /* number of strings in the array */ +} +KeySequence; + +static GObjectClass *im_context_multipress_parent_class = NULL; +static GType im_context_multipress_type = 0; -static void gtk_im_context_multipress_class_init (GtkImContextMultipressClass *klass); -static void gtk_im_context_multipress_init (GtkImContextMultipress *self); -static void gtk_im_context_multipress_finalize (GObject *obj); +static void im_context_multipress_class_init (GtkImContextMultipressClass *klass); +static void im_context_multipress_init (GtkImContextMultipress *self); +static void im_context_multipress_finalize (GObject *obj); -static void gtk_im_context_multipress_load_config (GtkImContextMultipress *self); +static void load_config (GtkImContextMultipress *self); -static GObjectClass *gtk_im_context_multipress_parent_class = NULL; -static GType gtk_im_multi_press_im_context_type = 0; +static gboolean vfunc_filter_keypress (GtkIMContext *context, + GdkEventKey *event); +static void vfunc_reset (GtkIMContext *context); +static void vfunc_get_preedit_string (GtkIMContext *context, + gchar **str, + PangoAttrList **attrs, + gint *cursor_pos); /* Notice that we have a *_register_type(GTypeModule*) function instead of a * *_get_type() function, because we must use g_type_module_register_type(), @@ -57,20 +64,20 @@ void gtk_im_context_multipress_register_type (GTypeModule* type_module) { static const GTypeInfo im_context_multipress_info = - { - sizeof (GtkImContextMultipressClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gtk_im_context_multipress_class_init, - NULL, - NULL, - sizeof (GtkImContextMultipress), - 0, - (GInstanceInitFunc) gtk_im_context_multipress_init, - 0, - }; - - gtk_im_multi_press_im_context_type = + { + sizeof (GtkImContextMultipressClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) &im_context_multipress_class_init, + NULL, + NULL, + sizeof (GtkImContextMultipress), + 0, + (GInstanceInitFunc) &im_context_multipress_init, + 0, + }; + + im_context_multipress_type = g_type_module_register_type (type_module, GTK_TYPE_IM_CONTEXT, "GtkImContextMultipress", @@ -80,167 +87,75 @@ gtk_im_context_multipress_register_type (GTypeModule* type_module) GType gtk_im_context_multipress_get_type (void) { - g_assert (gtk_im_multi_press_im_context_type != 0); - return gtk_im_multi_press_im_context_type; + g_assert (im_context_multipress_type != 0); + + return im_context_multipress_type; } -/* - * Returns TRUE if the passthrough flag is set on the currently focused - * child of the widget that owns the GDK window. In order to turn on - * passthrough mode, call: - * g_object_set_data (widget, "multipress-passthrough-flag", GINT_TO_POINTER (1)); - */ -static gboolean -passthrough_enabled_for_window (GdkWindow* window) +static void +key_sequence_free (gpointer value) { - gpointer event_widget = NULL; - - g_return_val_if_fail (window != NULL, FALSE); - /* - * For historical reasons, GTK+ assumes the user data attached to a GdkWindow - * to point to the GtkWidget that owns the window. It's even documented: - * http://developer.gnome.org/doc/API/2.0/gdk/gdk-Windows.html#gdk-window-set-user-data - * So we are really lucky here, as this allows us to attach IM state - * information to a widget in a fairly straightforward manner. - */ - gdk_window_get_user_data (window, &event_widget); - - if (event_widget && GTK_IS_WIDGET (event_widget)) - { - GtkWidget *toplevel; - GtkWidget *focus_widget; - /* - * The event window for key presses will usually belong to the toplevel - * GtkWindow, but that might not be true for synthetic events. In any - * case we need to find the currently focused child widget. - */ - toplevel = gtk_widget_get_toplevel ((GtkWidget *)event_widget); - - g_return_val_if_fail (toplevel != NULL && GTK_IS_WINDOW (toplevel), FALSE); - - focus_widget = gtk_window_get_focus ((GtkWindow *)toplevel); - - if (focus_widget) - { - static GQuark quark_passthrough_flag = 0; + KeySequence *seq = value; - if (!quark_passthrough_flag) - quark_passthrough_flag = g_quark_from_string (MULTIPRESS_PASSTHROUGH_FLAG); - - if (g_object_get_qdata (G_OBJECT (focus_widget), quark_passthrough_flag)) - return TRUE; + if (seq != NULL) + { + g_strfreev (seq->characters); + g_slice_free (KeySequence, seq); } - } - - return FALSE; } -static gboolean vfunc_filter_keypress (GtkIMContext *context, - GdkEventKey *event); -static void vfunc_reset (GtkIMContext *context); -static void vfunc_get_preedit_string (GtkIMContext *context, - gchar **str, - PangoAttrList **attrs, - gint *cursor_pos); - static void -gtk_im_context_multipress_class_init (GtkImContextMultipressClass *klass) +im_context_multipress_class_init (GtkImContextMultipressClass *klass) { - GtkIMContextClass* im_context_class; + GtkIMContextClass *im_context_class; /* Set this so we can use it later: */ - gtk_im_context_multipress_parent_class = g_type_class_peek_parent (klass); + im_context_multipress_parent_class = g_type_class_peek_parent (klass); /* Specify our vfunc implementations: */ im_context_class = GTK_IM_CONTEXT_CLASS (klass); - im_context_class->filter_keypress = vfunc_filter_keypress; - im_context_class->reset = vfunc_reset; - im_context_class->get_preedit_string = vfunc_get_preedit_string; + im_context_class->filter_keypress = &vfunc_filter_keypress; + im_context_class->reset = &vfunc_reset; + im_context_class->get_preedit_string = &vfunc_get_preedit_string; - G_OBJECT_CLASS (klass)->finalize = gtk_im_context_multipress_finalize; + G_OBJECT_CLASS (klass)->finalize = &im_context_multipress_finalize; } static void -gtk_im_context_multipress_init (GtkImContextMultipress *self) +im_context_multipress_init (GtkImContextMultipress *self) { - gtk_im_context_multipress_load_config (self); + self->key_sequences = g_hash_table_new_full (&g_direct_hash, &g_direct_equal, + NULL, &key_sequence_free); + load_config (self); } static void -gtk_im_context_multipress_finalize (GObject *obj) +im_context_multipress_finalize (GObject *obj) { - GtkImContextMultipress *self = GTK_IM_CONTEXT_MULTIPRESS (obj); - - /* Release the configuration data: */ + GtkImContextMultipress *self; - /* Free each item: */ - gsize i = 0; - for (i = 0; i < self->key_sequences_count; ++i) - { - KeySequence *item = self->key_sequences[i]; + self = GTK_IM_CONTEXT_MULTIPRESS (obj); - /* Free the array of strings in the item: */ - /* This is only for null-terminated arrays: g_strfreev(item->characters); */ - gsize i = 0; - for (i = 0; i < item->characters_length; ++i) + /* Release the configuration data: */ + if (self->key_sequences != NULL) { - gchar *str = item->characters[i]; - g_free (str); - item->characters[i] = NULL; + g_hash_table_destroy (self->key_sequences); + self->key_sequences = NULL; } - g_free (item->characters); - item->characters = NULL; - item->characters_length = 0; - - /* Free the item itself: */ - g_free (item); - } - - /* Free the array of pointers: */ - g_free (self->key_sequences); - - self->key_sequences = NULL; - self->key_sequences_count = 0; - - - gtk_im_context_multipress_parent_class->finalize (obj); + (*im_context_multipress_parent_class->finalize) (obj); } -GtkIMContext -*gtk_im_context_multipress_new (void) +GtkIMContext * +gtk_im_context_multipress_new (void) { return (GtkIMContext *)g_object_new (GTK_TYPE_IM_CONTEXT_MULTIPRESS, NULL); } -/* Lookup a compose sequence for the key press, from the table. The result is - * a null-terminated array of gchar*. It should not be freed by the caller. - */ -static KeySequence * -lookup_characters (GtkImContextMultipress *multipress_context, guint keypress) -{ - - /* Find the matching KeySequence, so that the caller can look at the possible - * characters for this keypress: */ - gsize i = 0; - for (i = 0; i < multipress_context->key_sequences_count; ++i) - { - KeySequence *item = multipress_context->key_sequences[i]; - - /* Just compare the first item, to match the keyval: */ - if (keypress == item->key_press) - return item; - } - - return NULL; -} - static void cancel_automatic_timeout_commit (GtkImContextMultipress *multipress_context) { - /* printf("debug: cancelling timeout\n"); */ - if (multipress_context->timeout_id) g_source_remove (multipress_context->timeout_id); @@ -284,9 +199,7 @@ on_timeout (gpointer data) { GtkImContextMultipress *multipress_context; - GDK_THREADS_ENTER(); - - /* printf("debug: on_timeout\n"); */ + GDK_THREADS_ENTER (); multipress_context = GTK_IM_CONTEXT_MULTIPRESS (data); @@ -296,7 +209,7 @@ on_timeout (gpointer data) multipress_context->timeout_id = 0; - GDK_THREADS_LEAVE(); + GDK_THREADS_LEAVE (); return FALSE; /* don't call me again */ } @@ -304,283 +217,224 @@ on_timeout (gpointer data) static gboolean vfunc_filter_keypress (GtkIMContext *context, GdkEventKey *event) { - GtkIMContextClass *parent; + GtkIMContextClass *parent; GtkImContextMultipress *multipress_context; - /* printf("debug: vfunc_filter_keypress:\n"); */ - multipress_context = GTK_IM_CONTEXT_MULTIPRESS (context); if (event->type == GDK_KEY_PRESS) - { - KeySequence *possible = NULL; - - /* printf("debug: multipress_context->compose_count=%d\n", multipress_context->compose_count); */ - - /* Check whether the current key is the same as previously entered, because - * if it is not then we should accept the previous one, and start a new - * character. */ - if (multipress_context->compose_count > 0 - && multipress_context->key_last_entered != event->keyval) - { - /* Accept the previously chosen character: */ - if (multipress_context->tentative_match) - { - /* This wipes the compose_count and key_last_entered. */ - accept_character (multipress_context, multipress_context->tentative_match); - } - } - - /* Decide what character this key press would choose: */ - if (!passthrough_enabled_for_window (event->window)) - possible = lookup_characters (multipress_context, event->keyval); /* Not to be freed. */ - - if (possible) - { - if (multipress_context->compose_count == 0) - g_signal_emit_by_name (multipress_context, "preedit-start"); - - /* Check whether we are at the end of a compose sequence, with no more possible characters: */ - /* Cycle back to the start if necessary: */ - if (multipress_context->compose_count >= possible->characters_length) - multipress_context->compose_count = 0; - - /* Store the last key pressed in the compose sequence. */ - multipress_context->key_last_entered = event->keyval; - - /* Get the possible match for this number of presses of the key. - * compose_count starts at 1, so that 0 can mean not composing. */ - multipress_context->tentative_match = possible->characters[multipress_context->compose_count++]; - - /* Indicate the current possible character. - * This will cause our vfunc_get_preedit_string() vfunc to be called, - * which will provide the current possible character for the user to see. - */ - g_signal_emit_by_name (multipress_context, "preedit-changed"); - - /* Cancel any outstanding timeout, so we can start the timer again: */ - cancel_automatic_timeout_commit (multipress_context); - - /* Create a timeout that will cause the currently chosen character to be committed, - * if nothing happens for a certain amount of time: - */ - multipress_context->timeout_id = g_timeout_add_seconds - (AUTOMATIC_COMPOSE_TIMEOUT, on_timeout, multipress_context); - - return TRUE; /* TRUE means that the event was handled. */ - } - else { - guint32 keyval_uchar; - - /* - * Just accept all other keypresses directly, but commit the - * current preedit content first. - */ - if (multipress_context->compose_count > 0 && multipress_context->tentative_match) - accept_character (multipress_context, multipress_context->tentative_match); - - keyval_uchar = gdk_keyval_to_unicode (event->keyval); - /* - * Convert to a string, because that's what accept_character() needs. - */ - if (keyval_uchar) - { - gchar keyval_utf8[7]; /* max length of UTF-8 sequence + 1 for NUL termination */ - gint length; - - length = g_unichar_to_utf8 (keyval_uchar, keyval_utf8); - keyval_utf8[length] = '\0'; - - accept_character (multipress_context, keyval_utf8); - - return TRUE; /* key handled */ - } + KeySequence *possible; + + /* Check whether the current key is the same as previously entered, because + * if it is not then we should accept the previous one, and start a new + * character. */ + if (multipress_context->compose_count > 0 + && multipress_context->key_last_entered != event->keyval + && multipress_context->tentative_match != NULL) + { + /* Accept the previously chosen character. This wipes + * the compose_count and key_last_entered. */ + accept_character (multipress_context, + multipress_context->tentative_match); + } + + /* Decide what character this key press would choose: */ + possible = g_hash_table_lookup (multipress_context->key_sequences, + GUINT_TO_POINTER (event->keyval)); + if (possible != NULL) + { + if (multipress_context->compose_count == 0) + g_signal_emit_by_name (multipress_context, "preedit-start"); + + /* Check whether we are at the end of a compose sequence, with no more + * possible characters. Cycle back to the start if necessary. */ + if (multipress_context->compose_count >= possible->n_characters) + multipress_context->compose_count = 0; + + /* Store the last key pressed in the compose sequence. */ + multipress_context->key_last_entered = event->keyval; + + /* Get the possible match for this number of presses of the key. + * compose_count starts at 1, so that 0 can mean not composing. */ + multipress_context->tentative_match = + possible->characters[multipress_context->compose_count++]; + + /* Indicate the current possible character. This will cause our + * vfunc_get_preedit_string() vfunc to be called, which will provide + * the current possible character for the user to see. */ + g_signal_emit_by_name (multipress_context, "preedit-changed"); + + /* Cancel any outstanding timeout, so we can start the timer again: */ + cancel_automatic_timeout_commit (multipress_context); + + /* Create a timeout that will cause the currently chosen character to + * be committed, if nothing happens for a certain amount of time: */ + multipress_context->timeout_id = + g_timeout_add_seconds (AUTOMATIC_COMPOSE_TIMEOUT, + &on_timeout, multipress_context); + + return TRUE; /* key handled */ + } + else + { + guint32 keyval_uchar; + + /* Just accept all other keypresses directly, but commit the + * current preedit content first. */ + if (multipress_context->compose_count > 0 + && multipress_context->tentative_match != NULL) + { + accept_character (multipress_context, + multipress_context->tentative_match); + } + keyval_uchar = gdk_keyval_to_unicode (event->keyval); + + /* Convert to a string for accept_character(). */ + if (keyval_uchar != 0) + { + /* max length of UTF-8 sequence = 6 + 1 for NUL termination */ + gchar keyval_utf8[7]; + gint length; + + length = g_unichar_to_utf8 (keyval_uchar, keyval_utf8); + keyval_utf8[length] = '\0'; + + accept_character (multipress_context, keyval_utf8); + + return TRUE; /* key handled */ + } + } } - } - /* The default implementation just returns FALSE, - * but it is generally a good idea to call the base class implementation: - */ - parent = (GtkIMContextClass *)gtk_im_context_multipress_parent_class; + parent = (GtkIMContextClass *)im_context_multipress_parent_class; + + /* The default implementation just returns FALSE, but it is generally + * a good idea to call the base class implementation: */ if (parent->filter_keypress) - return parent->filter_keypress (context, event); - else - return FALSE; + return (*parent->filter_keypress) (context, event); + + return FALSE; } -static void +static void vfunc_reset (GtkIMContext *context) { - GtkImContextMultipress *multipress_context = GTK_IM_CONTEXT_MULTIPRESS (context); - - clear_compose_buffer (multipress_context); + clear_compose_buffer (GTK_IM_CONTEXT_MULTIPRESS (context)); } - -static void +static void vfunc_get_preedit_string (GtkIMContext *context, gchar **str, PangoAttrList **attrs, gint *cursor_pos) { - /* printf("debug: get_preedit_string:\n"); */ - - GtkImContextMultipress *multipress_context = GTK_IM_CONTEXT_MULTIPRESS (context); - - /* Show the user what character he will get if he accepts: */ gsize len_bytes = 0; gsize len_utf8_chars = 0; - if (str) - { - if (multipress_context->tentative_match) - { - /* - printf("debug: vfunc_get_preedit_string(): tentative_match != NULL\n"); - printf("debug: vfunc_get_preedit_string(): tentative_match=%s\n", multipress_context->tentative_match); - */ - *str = g_strdup (multipress_context->tentative_match); - } - else - { - /* *str can never be NULL - that crashes the caller, which doesn't check for it: */ - *str = g_strdup(""); - } - if (*str) + /* Show the user what character he will get if he accepts: */ + if (str != NULL) { - len_utf8_chars = g_utf8_strlen (*str, -1); /* For the cursor pos, which seems need to be UTF-8 characters (GtkEntry clamps it.) */ - len_bytes = strlen (*str); /* The number of bytes, not the number of UTF-8 characters. For the PangoAttribute. */ + const gchar *match; + + match = GTK_IM_CONTEXT_MULTIPRESS (context)->tentative_match; + + if (match == NULL) + match = ""; /* *str must not be NUL */ + + len_bytes = strlen (match); /* byte count */ + len_utf8_chars = g_utf8_strlen (match, len_bytes); /* character count */ + + *str = g_strndup (match, len_bytes); } - } /* Underline it, to show the user that he is in compose mode: */ - if (attrs) + if (attrs != NULL) { *attrs = pango_attr_list_new (); - if (len_bytes) + if (len_bytes > 0) { - PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); + PangoAttribute *attr; + + attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); attr->start_index = 0; attr->end_index = len_bytes; pango_attr_list_insert (*attrs, attr); } - } + } if (cursor_pos) *cursor_pos = len_utf8_chars; } -static void -gtk_im_context_multipress_load_config (GtkImContextMultipress *self) +/* Open the configuration file and fill in the key_sequences hash table + * with key/character-list pairs taken from the [keys] group of the file. + */ +static void +load_config (GtkImContextMultipress *self) { - /* Open the configuration file: */ GKeyFile *key_file; - GError *error = NULL; - GArray *array; - gboolean found; - guint key_suffix_num = 0; - gboolean keep_looking = TRUE; + GError *error = NULL; + gchar **keys; + gsize n_keys = 0; + gsize i; key_file = g_key_file_new (); - found = g_key_file_load_from_file (key_file, CONFIGURATION_FILENAME, G_KEY_FILE_NONE, &error); - if (!found || error) - { - if (error) + + if (!g_key_file_load_from_file (key_file, CONFIGURATION_FILENAME, + G_KEY_FILE_NONE, &error)) { - g_warning ("Error while trying to open the %s configuration file: %s", CONFIGURATION_FILENAME, error->message); + g_warning ("Error while trying to open the %s configuration file: %s", + CONFIGURATION_FILENAME, error->message); g_error_free (error); - error = NULL; - - /*debug_output_possible_data_dirs();*/ + g_key_file_free (key_file); + return; } - g_key_file_free (key_file); - return; - } - - /* Get data from the file: - * Each KP_* key should have a value consiting of ;-separated values: */ - - array = g_array_sized_new (FALSE /* Not zero_terminated */, TRUE /* clear */, - sizeof(KeySequence*), 10 /* reserved size */); + keys = g_key_file_get_keys (key_file, "keys", &n_keys, &error); - /* Look at each KP_* key in sequence, until there are no more KP_* keys: */ - while (keep_looking) - { - gchar* key_name; - gchar** values; - gsize length_values = 0; - - key_name = g_strdup_printf ("KP_%d", key_suffix_num); - values = g_key_file_get_string_list (key_file, "keys", key_name, &length_values, &error); - if (error) + if (error != NULL) { - /* Only show the warning if this was the first key. It's OK to fail when trying to find subsequent keys: */ - if (key_suffix_num == 0) - { - g_warning ("Error while trying to read key values from the configuration file: %s", error->message); - } - + g_warning ("Error while trying to read the %s configuration file: %s", + CONFIGURATION_FILENAME, error->message); g_error_free (error); - error = NULL; + g_key_file_free (key_file); + return; } - if (!values) + for (i = 0; i < n_keys; ++i) { - /* printf("debug: No values found for key %s\n", key_name); */ - keep_looking = FALSE; /* This must be the last in the array of keys. */ - /* debug_output_possible_config_keys(key_file); */ - } - else - { - KeySequence *key_sequence; - GArray *array_characters; - gsize value_index = 0; - - key_sequence = g_new0 (KeySequence, 1); - g_array_append_val (array, key_sequence); - - array_characters = g_array_sized_new (FALSE /* Not zero_terminated */, TRUE /* clear */, - sizeof(gchar*), 10 /* reserved size */); + KeySequence *seq; + guint keyval; - for (value_index = 0; value_index < length_values; ++value_index) - { - gchar *value; - gchar *value_copy; + keyval = gdk_keyval_from_name (keys[i]); - value = values[value_index]; - - if (value_index == 0) + if (keyval == GDK_VoidSymbol) { - g_assert (strlen(value) > 0); - - key_sequence->key_press = g_utf8_get_char (value); + g_warning ("Error while trying to read the %s configuration file: " + "invalid key name \"%s\"", + CONFIGURATION_FILENAME, keys[i]); + continue; } - value_copy = g_strdup (value); - g_array_append_val (array_characters, value_copy); - - /* printf("debug: Key=%s, value=%s\n", key_name, value); */ - } - - g_strfreev (values); + seq = g_slice_new (KeySequence); + seq->characters = g_key_file_get_string_list (key_file, "keys", keys[i], + &seq->n_characters, &error); + if (error != NULL) + { + g_warning ("Error while trying to read the %s configuration file: %s", + CONFIGURATION_FILENAME, error->message); + g_error_free (error); + error = NULL; + g_slice_free (KeySequence, seq); + continue; + } - key_sequence->characters_length = array_characters->len; - key_sequence->characters = (gchar **)g_array_free(array_characters, - FALSE /* Don't free items - return a real array of them. */); + /* Ownership of the KeySequence is taken over by the hash table */ + g_hash_table_insert (self->key_sequences, GUINT_TO_POINTER (keyval), seq); } - g_free (key_name); - ++key_suffix_num; - } - + g_strfreev (keys); g_key_file_free (key_file); - - self->key_sequences_count = array->len; - self->key_sequences = (KeySequence **)g_array_free (array, - FALSE /* Don't free items - return a real array of them. */); - - /* debug_output_key_sequences_array(self); */ } diff --git a/modules/input/gtkimcontextmultipress.h b/modules/input/gtkimcontextmultipress.h index ee03e22ad..036e6fa90 100644 --- a/modules/input/gtkimcontextmultipress.h +++ b/modules/input/gtkimcontextmultipress.h @@ -30,26 +30,25 @@ G_BEGIN_DECLS #define GTK_IS_IM_CONTEXT_MULTIPRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_MULTIPRESS)) #define GTK_IM_CONTEXT_MULTIPRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_MULTIPRESS, GtkImContextMultipressClass)) - -typedef struct _KeySequence KeySequence; - typedef struct _GtkImContextMultipress GtkImContextMultipress; -/** This input method allows multi-press character input, like that found on mobile phones. +/* This input method allows multi-press character input, like that found on + * mobile phones. * - * This is based on GtkImContextSimple, which allows "compose" based on sequences of characters. - * But instead the character sequences are defined by lists of characters for a key, - * so that repeated pressing of the same key can cycle through the possible output characters, - * with automatic choosing of the character after a time delay. - */ + * This is based on GtkImContextSimple, which allows "compose" based on + * sequences of characters. But instead the character sequences are defined + * by lists of characters for a key, so that repeated pressing of the same key + * can cycle through the possible output characters, with automatic choosing + * of the character after a time delay. + */ struct _GtkImContextMultipress { + /*< private >*/ GtkIMContext parent; - /* Sequence information, loading from the configuration file: */ - KeySequence** key_sequences; /* Built when we read the config file. Not null-terminated. */ - gsize key_sequences_count; /* Number of KeySequence struct instances. */ - + /* Sequence information, loaded from the configuration file: */ + GHashTable* key_sequences; + gsize dummy; /* ABI-preserving placeholder */ /* The last character entered so far during a compose. * If this is NULL then we are not composing yet. @@ -59,12 +58,11 @@ struct _GtkImContextMultipress /* The position of the compose in the possible sequence. * For instance, this is 2 if aa has been pressed to show b (from abc0). */ - guint compose_count; guint timeout_id; /* The character(s) that will be used if it the current character(s) is accepted: */ - const gchar* tentative_match; + const gchar *tentative_match; }; diff --git a/modules/input/im-multipress.conf b/modules/input/im-multipress.conf index 6296793a4..d6c8129d9 100644 --- a/modules/input/im-multipress.conf +++ b/modules/input/im-multipress.conf @@ -1,23 +1,22 @@ -# Configuration File for the GTK+ Multipress Input Method, Copyright 2007 Openismus GmbH +# Example configuration file for the GTK+ Multipress Input Method +# Authored by Openismus GmbH, 2009. # -# The first character is the key that you press repeatedly to get that character and the following characters. -# Each character is separated by ; -# Use \ to escape characters - for instance, \; for ";" or \\ for "\" +# This file follows the GKeyFile format. On the left of the equal sign goes +# the key that you press repeatedly to iterate through the text items listed +# on the right-hand side. The list items are separated by semicolons ";" and +# consist of one or more characters each. The backslash "\" is used to escape +# characters; for instance "\;" for a literal semicolon. # -# This is the Glib GKeyFile format. - -# The § item is to test non-ASCII keycodes. - +# The example configuration below imitates the behavior of a standard mobile +# phone by a major manufacturer, with German language setting. [keys] - -KP_0 = .;,;:;/ -KP_1= a;b;c;ä;2 -KP_2 = d;e;f;3 -KP_3 = g;h;i;4 -KP_4 = j;k;l;5 -KP_5 = m;n;o;ö;6 -KP_6 = p;q;r;s;7 -KP_7 = t;u;v;ü;8 -KP_8 = w;x;y;z;9 -KP_9 = §;X;Y;Z - +KP_1 = .;,;?;!;';";1;-;(;);@;/;:;_ +KP_2 = a;b;c;2;ä;à;á;ã;â;å;æ;ç +KP_3 = d;e;f;3;è;é;ë;ê;ð +KP_4 = g;h;i;4;ì;í;î;ï +KP_5 = j;k;l;5;£ +KP_6 = m;n;o;6;ö;ò;ó;ô;õ;ø;ñ +KP_7 = p;q;r;s;7;ß;$ +KP_8 = t;u;v;8;ü;ù;ú;û +KP_9 = w;x;y;z;9;ý;þ +KP_0 = \s;0 -- 2.43.2