-/* 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
* 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 <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-
-#include <string.h>
-
-#include "gtk/gtk.h"
-#include "gdk/gdkkeysyms.h"
-
-#include "gtk/gtkimmodule.h"
#include "gtkimcontextmultipress.h"
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkimmodule.h>
+#include <config.h>
#define AUTOMATIC_COMPOSE_TIMEOUT 1 /* seconds */
-
-/* Just the last part of the name, not the whole path: */
#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 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 GObjectClass *im_context_multipress_parent_class = NULL;
+static GType im_context_multipress_type = 0;
-static void gtk_im_context_multipress_load_config (GtkImContextMultipress *self);
+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 GObjectClass* gtk_im_context_multipress_parent_class = NULL;
-static GType gtk_im_multi_press_im_context_type = 0;
+static void load_config (GtkImContextMultipress *self);
-/** Notice that we have a *_register_type(GTypeModule*) function instead of a *_get_type() function,
- * because we must use g_type_module_register_type(), providing the GTypeModule* that was provided to im_context_init().
- * That is also why we are not using G_DEFINE_TYPE().
+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(),
+ * providing the GTypeModule* that was provided to im_context_init(). That
+ * is also why we are not using G_DEFINE_TYPE().
*/
-void
+void
gtk_im_context_multipress_register_type (GTypeModule* type_module)
{
- if (gtk_im_multi_press_im_context_type == 0)
- {
- static const GTypeInfo im_context_multipress_info =
+ const GTypeInfo im_context_multipress_info =
{
- sizeof(GtkImContextMultipressClass),
+ sizeof (GtkImContextMultipressClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
- (GClassInitFunc) gtk_im_context_multipress_class_init,
+ (GClassInitFunc) &im_context_multipress_class_init,
NULL,
NULL,
- sizeof(GtkImContextMultipress),
+ sizeof (GtkImContextMultipress),
0,
- (GInstanceInitFunc) gtk_im_context_multipress_init,
+ (GInstanceInitFunc) &im_context_multipress_init,
0,
};
- gtk_im_multi_press_im_context_type =
- g_type_module_register_type (type_module,
- GTK_TYPE_IM_CONTEXT,
- "GtkImContextMultipress",
- &im_context_multipress_info, 0);
- }
+ im_context_multipress_type =
+ g_type_module_register_type (type_module,
+ GTK_TYPE_IM_CONTEXT,
+ "GtkImContextMultipress",
+ &im_context_multipress_info, 0);
}
-GType
-gtk_im_context_multipress_get_type(void)
+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;
-
- if (!quark_passthrough_flag)
- quark_passthrough_flag = g_quark_from_string (MULTIPRESS_PASSTHROUGH_FLAG);
+ KeySequence *seq = value;
- 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)
+static void
+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)
+static void
+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)
+static void
+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;
-
-
- G_OBJECT_CLASS (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);
+ 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 an 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
+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); /* This function cancels timeouts, idle handlers, etc. */
+ g_source_remove (multipress_context->timeout_id);
multipress_context->timeout_id = 0;
}
-/** Clear the compose buffer, so we are ready to compose the next character.
+/* Clear the compose buffer, so we are ready to compose the next character.
*/
-static void
+static void
clear_compose_buffer (GtkImContextMultipress *multipress_context)
{
multipress_context->key_last_entered = 0;
multipress_context->compose_count = 0;
multipress_context->tentative_match = NULL;
- cancel_automatic_timeout_commit(multipress_context);
+ cancel_automatic_timeout_commit (multipress_context);
+
+ g_signal_emit_by_name (multipress_context, "preedit-changed");
+ g_signal_emit_by_name (multipress_context, "preedit-end");
}
-/** Finish composing, provide the character, and clear our compose buffer.
+/* Finish composing, provide the character, and clear our compose buffer.
*/
-static void
-accept_character (GtkImContextMultipress *multipress_context, const gchar* characters)
+static void
+accept_character (GtkImContextMultipress *multipress_context, const gchar *characters)
{
- /* printf("debug: accepting character: %c\n", (char)character); */
-
- cancel_automatic_timeout_commit (multipress_context);
-
- /* Accept the character: */
-
- /* Clear the compose buffer, so we are ready to compose the next character. */
+ /* Clear the compose buffer, so we are ready to compose the next character.
+ * Note that if we emit "preedit-changed" after "commit", there's a segfault/
+ * invalid-write with GtkTextView in gtk_text_layout_free_line_display(), when
+ * destroying a PangoLayout (this can also be avoided by not using any Pango
+ * attributes in get_preedit_string(). */
clear_compose_buffer (multipress_context);
- /* We must also signal that the preedit has changed, or we will still see the old
- preedit from the composing of the character that we just committed, hanging around after the cursor.
- */
- g_signal_emit_by_name (multipress_context, "preedit-changed");
-
- /* Provide a character to GTK+: */
+ /* Provide the character to GTK+ */
g_signal_emit_by_name (multipress_context, "commit", characters);
-
- /* Note that if we emit preedit_changed after commit,
- * there's a segfault/invalid-write with GtkTextView in gtk_text_layout_free_line_display(), when destroying a PangoLayout
- * (this can also be avoided by not using any pango attributes in get_preedit_string().
- */
}
-static gboolean
+static gboolean
on_timeout (gpointer data)
{
- GtkImContextMultipress* multipress_context;
-
- GDK_THREADS_ENTER();
+ GtkImContextMultipress *multipress_context;
- /* printf("debug: on_timeout\n"); */
+ gdk_threads_enter ();
- multipress_context = gtk_im_context_multipress(data);
+ multipress_context = GTK_IM_CONTEXT_MULTIPRESS (data);
- /* A certain amount of time has passed,
- * so we will assume that the user really wants the currently chosen character:
- */
+ /* A certain amount of time has passed, so we will assume that the user
+ * really wants the currently chosen character */
accept_character (multipress_context, multipress_context->tentative_match);
multipress_context->timeout_id = 0;
- GDK_THREADS_LEAVE();
+ gdk_threads_leave ();
- return FALSE; /* Don't call this callback again. We only need to call it once. */
+ return G_SOURCE_REMOVE; /* don't call me again */
}
-static gboolean
+static gboolean
vfunc_filter_keypress (GtkIMContext *context, GdkEventKey *event)
{
- GtkIMContextClass* parent;
- GtkImContextMultipress* multipress_context;
-
- /* printf("debug: vfunc_filter_keypress:\n"); */
+ GtkIMContextClass *parent;
+ GtkImContextMultipress *multipress_context;
- multipress_context = gtk_im_context_multipress (context);
+ multipress_context = GTK_IM_CONTEXT_MULTIPRESS (context);
if (event->type == GDK_KEY_PRESS)
- {
- KeySequence* possible = NULL;
+ {
+ 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");
- /* printf("debug: multipress_context->compose_count=%d\n", multipress_context->compose_count); */
+ /* 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;
- /* 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);
- }
- }
+ /* Store the last key pressed in the compose sequence. */
+ multipress_context->key_last_entered = event->keyval;
- /* 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. */
+ /* 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++];
- if (possible)
- {
- /* 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)
- {
- clear_compose_buffer (multipress_context);
- return vfunc_filter_keypress (context, event);
- }
-
- /* Store the last key pressed in the compose sequence. */
- multipress_context->key_last_entered = event->keyval;
- ++(multipress_context->compose_count);
-
- /* Get the possible match for this number of presses of the key: */
- multipress_context->tentative_match = possible->characters[multipress_context->compose_count -1]; /* compose_count starts at 1, so that 0 can mean not composing. */
-
- /* 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 */
- }
+ /* 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)
+ 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)
- {
- PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
- attr->start_index = 0;
+
+ if (len_bytes > 0)
+ {
+ 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);
- }
+ 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;
+ GKeyFile *key_file;
+ 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: */
+ keys = g_key_file_get_keys (key_file, "keys", &n_keys, &error);
- array = g_array_sized_new (FALSE /* Not zero_terminated */, TRUE /* clear */,
- sizeof(KeySequence*), 10 /* reserved size */);
-
- /* 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_KEY_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); */
}