gtk+ r22125 - in trunk: . modules/input



Author: daniel
Date: Fri Jan 16 15:02:06 2009
New Revision: 22125
URL: http://svn.gnome.org/viewvc/gtk+?rev=22125&view=rev

Log:
* 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.

Modified:
   trunk/ChangeLog
   trunk/modules/input/gtkimcontextmultipress.c
   trunk/modules/input/gtkimcontextmultipress.h
   trunk/modules/input/im-multipress.conf

Modified: trunk/modules/input/gtkimcontextmultipress.c
==============================================================================
--- trunk/modules/input/gtkimcontextmultipress.c	(original)
+++ trunk/modules/input/gtkimcontextmultipress.c	Fri Jan 16 15:02:06 2009
@@ -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 <config.h>
+#include "gtkimcontextmultipress.h"
 #include <string.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtkimmodule.h>
-#include "gtkimcontextmultipress.h"
+#include <config.h>
 
 #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 @@
 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,
-  };
+    {
+      sizeof (GtkImContextMultipressClass),
+      (GBaseInitFunc) NULL,
+      (GBaseFinalizeFunc) NULL,
+      (GClassInitFunc) &im_context_multipress_class_init,
+      NULL,
+      NULL,
+      sizeof (GtkImContextMultipress),
+      0,
+      (GInstanceInitFunc) &im_context_multipress_init,
+      0,
+    };
 
-  gtk_im_multi_press_im_context_type =
+  im_context_multipress_type =
     g_type_module_register_type (type_module,
                                  GTK_TYPE_IM_CONTEXT,
                                  "GtkImContextMultipress",
@@ -80,167 +87,75 @@
 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);
+  KeySequence *seq = value;
 
-    g_return_val_if_fail (toplevel != NULL && GTK_IS_WINDOW (toplevel), FALSE);
-
-    focus_widget = gtk_window_get_focus ((GtkWindow *)toplevel);
-
-    if (focus_widget)
+  if (seq != NULL)
     {
-      static GQuark quark_passthrough_flag = 0;
-
-      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;
+      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;
+
+  self = GTK_IM_CONTEXT_MULTIPRESS (obj);
 
-  /* Free each item: */
-  gsize i = 0;
-  for (i = 0; i < self->key_sequences_count; ++i)
-  {
-    KeySequence *item = self->key_sequences[i];
-
-    /* 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 @@
 {
   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 @@
 
   multipress_context->timeout_id = 0;
 
-  GDK_THREADS_LEAVE();
+  GDK_THREADS_LEAVE ();
 
   return FALSE; /* don't call me again */
 }
@@ -304,283 +217,224 @@
 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");
+      KeySequence *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)
-        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);
+      /* 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");
 
-      return TRUE; /* TRUE means that the event was handled. */
-    }
-    else
-    {
-      guint32 keyval_uchar;
+          /* 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)
-        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;
+          /* 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';
+              length = g_unichar_to_utf8 (keyval_uchar, keyval_utf8);
+              keyval_utf8[length] = '\0';
 
-        accept_character (multipress_context, keyval_utf8);
+              accept_character (multipress_context, keyval_utf8);
 
-        return TRUE; /* key handled */
-      }
+              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 */);
-
-  /* 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)
-    {
-      /* 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);
-      }
+  keys = g_key_file_get_keys (key_file, "keys", &n_keys, &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_key_file_free (key_file);
+      return;
     }
 
-    if (!values)
-    {
-       /* 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
+  for (i = 0; i < n_keys; ++i)
     {
-      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); */
 }

Modified: trunk/modules/input/gtkimcontextmultipress.h
==============================================================================
--- trunk/modules/input/gtkimcontextmultipress.h	(original)
+++ trunk/modules/input/gtkimcontextmultipress.h	Fri Jan 16 15:02:06 2009
@@ -30,26 +30,25 @@
 #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 @@
   /* 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;
 };
 
 

Modified: trunk/modules/input/im-multipress.conf
==============================================================================
--- trunk/modules/input/im-multipress.conf	(original)
+++ trunk/modules/input/im-multipress.conf	Fri Jan 16 15:02:06 2009
@@ -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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]