Blame modules/input/gtkimcontextmultipress.c

Packit Service fb6fa5
/*
Packit Service fb6fa5
 * Copyright (c) 2006-2009 Openismus GmbH
Packit Service fb6fa5
 *
Packit Service fb6fa5
 * This library is free software; you can redistribute it and/or
Packit Service fb6fa5
 * modify it under the terms of the GNU Lesser General Public
Packit Service fb6fa5
 * License as published by the Free Software Foundation; either
Packit Service fb6fa5
 * version 2 of the License, or (at your option) any later version.
Packit Service fb6fa5
 *
Packit Service fb6fa5
 * This library is distributed in the hope that it will be useful,
Packit Service fb6fa5
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fb6fa5
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service fb6fa5
 * Lesser General Public License for more details.
Packit Service fb6fa5
 *
Packit Service fb6fa5
 * You should have received a copy of the GNU Lesser General Public
Packit Service fb6fa5
 * License along with this library; if not, write to the
Packit Service fb6fa5
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Packit Service fb6fa5
 * Boston, MA 02111-1307, USA.
Packit Service fb6fa5
 */
Packit Service fb6fa5
Packit Service fb6fa5
#include "gtkimcontextmultipress.h"
Packit Service fb6fa5
#include <string.h>
Packit Service fb6fa5
#include <gtk/gtk.h>
Packit Service fb6fa5
#include <gdk/gdkkeysyms.h>
Packit Service fb6fa5
#include <gtk/gtkimmodule.h>
Packit Service fb6fa5
#include <config.h>
Packit Service fb6fa5
Packit Service fb6fa5
#define AUTOMATIC_COMPOSE_TIMEOUT 1 /* seconds */
Packit Service fb6fa5
#define CONFIGURATION_FILENAME MULTIPRESS_CONFDIR G_DIR_SEPARATOR_S "im-multipress.conf"
Packit Service fb6fa5
Packit Service fb6fa5
/* This contains rows of characters that can be entered by pressing
Packit Service fb6fa5
 * a particular key repeatedly.  Each row has one key (such as GDK_a),
Packit Service fb6fa5
 * and an array of character strings, such as "a".
Packit Service fb6fa5
 */
Packit Service fb6fa5
typedef struct
Packit Service fb6fa5
{
Packit Service fb6fa5
  gchar **characters; /* array of strings */
Packit Service fb6fa5
  gsize n_characters; /* number of strings in the array */
Packit Service fb6fa5
}
Packit Service fb6fa5
KeySequence;
Packit Service fb6fa5
Packit Service fb6fa5
static GObjectClass *im_context_multipress_parent_class = NULL;
Packit Service fb6fa5
static GType         im_context_multipress_type = 0;
Packit Service fb6fa5
Packit Service fb6fa5
static void im_context_multipress_class_init (GtkImContextMultipressClass *klass);
Packit Service fb6fa5
static void im_context_multipress_init (GtkImContextMultipress *self);
Packit Service fb6fa5
static void im_context_multipress_finalize (GObject *obj);
Packit Service fb6fa5
Packit Service fb6fa5
static void load_config (GtkImContextMultipress *self);
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean vfunc_filter_keypress (GtkIMContext *context,
Packit Service fb6fa5
                                       GdkEventKey  *event);
Packit Service fb6fa5
static void vfunc_reset (GtkIMContext *context);
Packit Service fb6fa5
static void vfunc_get_preedit_string (GtkIMContext   *context,
Packit Service fb6fa5
                                      gchar         **str,
Packit Service fb6fa5
                                      PangoAttrList **attrs,
Packit Service fb6fa5
                                      gint           *cursor_pos);
Packit Service fb6fa5
Packit Service fb6fa5
/* Notice that we have a *_register_type(GTypeModule*) function instead of a
Packit Service fb6fa5
 * *_get_type() function, because we must use g_type_module_register_type(),
Packit Service fb6fa5
 * providing the GTypeModule* that was provided to im_context_init(). That
Packit Service fb6fa5
 * is also why we are not using G_DEFINE_TYPE().
Packit Service fb6fa5
 */
Packit Service fb6fa5
void
Packit Service fb6fa5
gtk_im_context_multipress_register_type (GTypeModule* type_module)
Packit Service fb6fa5
{
Packit Service fb6fa5
  const GTypeInfo im_context_multipress_info =
Packit Service fb6fa5
    {
Packit Service fb6fa5
      sizeof (GtkImContextMultipressClass),
Packit Service fb6fa5
      (GBaseInitFunc) NULL,
Packit Service fb6fa5
      (GBaseFinalizeFunc) NULL,
Packit Service fb6fa5
      (GClassInitFunc) &im_context_multipress_class_init,
Packit Service fb6fa5
      NULL,
Packit Service fb6fa5
      NULL,
Packit Service fb6fa5
      sizeof (GtkImContextMultipress),
Packit Service fb6fa5
      0,
Packit Service fb6fa5
      (GInstanceInitFunc) &im_context_multipress_init,
Packit Service fb6fa5
      0,
Packit Service fb6fa5
    };
Packit Service fb6fa5
Packit Service fb6fa5
  im_context_multipress_type =
Packit Service fb6fa5
    g_type_module_register_type (type_module,
Packit Service fb6fa5
                                 GTK_TYPE_IM_CONTEXT,
Packit Service fb6fa5
                                 "GtkImContextMultipress",
Packit Service fb6fa5
                                 &im_context_multipress_info, 0);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
GType
Packit Service fb6fa5
gtk_im_context_multipress_get_type (void)
Packit Service fb6fa5
{
Packit Service fb6fa5
  g_assert (im_context_multipress_type != 0);
Packit Service fb6fa5
Packit Service fb6fa5
  return im_context_multipress_type;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
key_sequence_free (gpointer value)
Packit Service fb6fa5
{
Packit Service fb6fa5
  KeySequence *seq = value;
Packit Service fb6fa5
Packit Service fb6fa5
  if (seq != NULL)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      g_strfreev (seq->characters);
Packit Service fb6fa5
      g_slice_free (KeySequence, seq);
Packit Service fb6fa5
    }
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
im_context_multipress_class_init (GtkImContextMultipressClass *klass)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkIMContextClass *im_context_class;
Packit Service fb6fa5
Packit Service fb6fa5
  /* Set this so we can use it later: */
Packit Service fb6fa5
  im_context_multipress_parent_class = g_type_class_peek_parent (klass);
Packit Service fb6fa5
Packit Service fb6fa5
  /* Specify our vfunc implementations: */
Packit Service fb6fa5
  im_context_class = GTK_IM_CONTEXT_CLASS (klass);
Packit Service fb6fa5
  im_context_class->filter_keypress = &vfunc_filter_keypress;
Packit Service fb6fa5
  im_context_class->reset = &vfunc_reset;
Packit Service fb6fa5
  im_context_class->get_preedit_string = &vfunc_get_preedit_string;
Packit Service fb6fa5
Packit Service fb6fa5
  G_OBJECT_CLASS (klass)->finalize = &im_context_multipress_finalize;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
im_context_multipress_init (GtkImContextMultipress *self)
Packit Service fb6fa5
{
Packit Service fb6fa5
  self->key_sequences = g_hash_table_new_full (&g_direct_hash, &g_direct_equal,
Packit Service fb6fa5
                                               NULL, &key_sequence_free);
Packit Service fb6fa5
  load_config (self);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
im_context_multipress_finalize (GObject *obj)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkImContextMultipress *self;
Packit Service fb6fa5
Packit Service fb6fa5
  self = GTK_IM_CONTEXT_MULTIPRESS (obj);
Packit Service fb6fa5
Packit Service fb6fa5
  /* Release the configuration data: */
Packit Service fb6fa5
  if (self->key_sequences != NULL)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      g_hash_table_destroy (self->key_sequences);
Packit Service fb6fa5
      self->key_sequences = NULL;
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  (*im_context_multipress_parent_class->finalize) (obj);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
GtkIMContext *
Packit Service fb6fa5
gtk_im_context_multipress_new (void)
Packit Service fb6fa5
{
Packit Service fb6fa5
  return (GtkIMContext *)g_object_new (GTK_TYPE_IM_CONTEXT_MULTIPRESS, NULL);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
cancel_automatic_timeout_commit (GtkImContextMultipress *multipress_context)
Packit Service fb6fa5
{
Packit Service fb6fa5
  if (multipress_context->timeout_id)
Packit Service fb6fa5
    g_source_remove (multipress_context->timeout_id);
Packit Service fb6fa5
 
Packit Service fb6fa5
  multipress_context->timeout_id = 0;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
Packit Service fb6fa5
/* Clear the compose buffer, so we are ready to compose the next character.
Packit Service fb6fa5
 */
Packit Service fb6fa5
static void
Packit Service fb6fa5
clear_compose_buffer (GtkImContextMultipress *multipress_context)
Packit Service fb6fa5
{
Packit Service fb6fa5
  multipress_context->key_last_entered = 0;
Packit Service fb6fa5
  multipress_context->compose_count = 0;
Packit Service fb6fa5
Packit Service fb6fa5
  cancel_automatic_timeout_commit (multipress_context);
Packit Service fb6fa5
Packit Service fb6fa5
  if (multipress_context->tentative_match)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      multipress_context->tentative_match = NULL;
Packit Service fb6fa5
      g_signal_emit_by_name (multipress_context, "preedit-changed");
Packit Service fb6fa5
      g_signal_emit_by_name (multipress_context, "preedit-end");
Packit Service fb6fa5
    }
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
/* Finish composing, provide the character, and clear our compose buffer.
Packit Service fb6fa5
 */
Packit Service fb6fa5
static void
Packit Service fb6fa5
accept_character (GtkImContextMultipress *multipress_context, const gchar *characters)
Packit Service fb6fa5
{
Packit Service fb6fa5
  /* Clear the compose buffer, so we are ready to compose the next character.
Packit Service fb6fa5
   * Note that if we emit "preedit-changed" after "commit", there's a segfault/
Packit Service fb6fa5
   * invalid-write with GtkTextView in gtk_text_layout_free_line_display(), when
Packit Service fb6fa5
   * destroying a PangoLayout (this can also be avoided by not using any Pango
Packit Service fb6fa5
   * attributes in get_preedit_string(). */
Packit Service fb6fa5
  clear_compose_buffer (multipress_context);
Packit Service fb6fa5
Packit Service fb6fa5
  /* Provide the character to GTK+ */
Packit Service fb6fa5
  g_signal_emit_by_name (multipress_context, "commit", characters);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
on_timeout (gpointer data)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkImContextMultipress *multipress_context;
Packit Service fb6fa5
Packit Service fb6fa5
  GDK_THREADS_ENTER ();
Packit Service fb6fa5
Packit Service fb6fa5
  multipress_context = GTK_IM_CONTEXT_MULTIPRESS (data);
Packit Service fb6fa5
Packit Service fb6fa5
  /* A certain amount of time has passed, so we will assume that the user
Packit Service fb6fa5
   * really wants the currently chosen character */
Packit Service fb6fa5
  accept_character (multipress_context, multipress_context->tentative_match);
Packit Service fb6fa5
Packit Service fb6fa5
  multipress_context->timeout_id = 0;
Packit Service fb6fa5
Packit Service fb6fa5
  GDK_THREADS_LEAVE ();
Packit Service fb6fa5
Packit Service fb6fa5
  return FALSE; /* don't call me again */
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
vfunc_filter_keypress (GtkIMContext *context, GdkEventKey *event)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkIMContextClass      *parent;
Packit Service fb6fa5
  GtkImContextMultipress *multipress_context;
Packit Service fb6fa5
Packit Service fb6fa5
  multipress_context = GTK_IM_CONTEXT_MULTIPRESS (context);
Packit Service fb6fa5
Packit Service fb6fa5
  if (event->type == GDK_KEY_PRESS)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      KeySequence *possible;
Packit Service fb6fa5
Packit Service fb6fa5
      /* Check whether the current key is the same as previously entered, because
Packit Service fb6fa5
       * if it is not then we should accept the previous one, and start a new
Packit Service fb6fa5
       * character. */
Packit Service fb6fa5
      if (multipress_context->compose_count > 0
Packit Service fb6fa5
          && multipress_context->key_last_entered != event->keyval
Packit Service fb6fa5
          && multipress_context->tentative_match != NULL)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          /* Accept the previously chosen character.  This wipes
Packit Service fb6fa5
           * the compose_count and key_last_entered. */
Packit Service fb6fa5
          accept_character (multipress_context,
Packit Service fb6fa5
                            multipress_context->tentative_match);
Packit Service fb6fa5
        } 
Packit Service fb6fa5
Packit Service fb6fa5
      /* Decide what character this key press would choose: */
Packit Service fb6fa5
      possible = g_hash_table_lookup (multipress_context->key_sequences,
Packit Service fb6fa5
                                      GUINT_TO_POINTER (event->keyval));
Packit Service fb6fa5
      if (possible != NULL)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          if (multipress_context->compose_count == 0)
Packit Service fb6fa5
            g_signal_emit_by_name (multipress_context, "preedit-start");
Packit Service fb6fa5
Packit Service fb6fa5
          /* Check whether we are at the end of a compose sequence, with no more
Packit Service fb6fa5
           * possible characters.  Cycle back to the start if necessary. */
Packit Service fb6fa5
          if (multipress_context->compose_count >= possible->n_characters)
Packit Service fb6fa5
            multipress_context->compose_count = 0;
Packit Service fb6fa5
Packit Service fb6fa5
          /* Store the last key pressed in the compose sequence. */
Packit Service fb6fa5
          multipress_context->key_last_entered = event->keyval; 
Packit Service fb6fa5
Packit Service fb6fa5
          /* Get the possible match for this number of presses of the key.
Packit Service fb6fa5
           * compose_count starts at 1, so that 0 can mean not composing. */ 
Packit Service fb6fa5
          multipress_context->tentative_match =
Packit Service fb6fa5
            possible->characters[multipress_context->compose_count++];
Packit Service fb6fa5
Packit Service fb6fa5
          /* Indicate the current possible character.  This will cause our
Packit Service fb6fa5
           * vfunc_get_preedit_string() vfunc to be called, which will provide
Packit Service fb6fa5
           * the current possible character for the user to see. */
Packit Service fb6fa5
          g_signal_emit_by_name (multipress_context, "preedit-changed");
Packit Service fb6fa5
Packit Service fb6fa5
          /* Cancel any outstanding timeout, so we can start the timer again: */
Packit Service fb6fa5
          cancel_automatic_timeout_commit (multipress_context);
Packit Service fb6fa5
Packit Service fb6fa5
          /* Create a timeout that will cause the currently chosen character to
Packit Service fb6fa5
           * be committed, if nothing happens for a certain amount of time: */
Packit Service fb6fa5
          multipress_context->timeout_id =
Packit Service fb6fa5
            g_timeout_add_seconds (AUTOMATIC_COMPOSE_TIMEOUT,
Packit Service fb6fa5
                                   &on_timeout, multipress_context);
Packit Service fb6fa5
Packit Service fb6fa5
          return TRUE; /* key handled */
Packit Service fb6fa5
        }
Packit Service fb6fa5
      else
Packit Service fb6fa5
        {
Packit Service fb6fa5
          guint32 keyval_uchar;
Packit Service fb6fa5
Packit Service fb6fa5
          /* Just accept all other keypresses directly, but commit the
Packit Service fb6fa5
           * current preedit content first. */
Packit Service fb6fa5
          if (multipress_context->compose_count > 0
Packit Service fb6fa5
              && multipress_context->tentative_match != NULL)
Packit Service fb6fa5
            {
Packit Service fb6fa5
              accept_character (multipress_context,
Packit Service fb6fa5
                                multipress_context->tentative_match);
Packit Service fb6fa5
            }
Packit Service fb6fa5
          keyval_uchar = gdk_keyval_to_unicode (event->keyval);
Packit Service fb6fa5
Packit Service fb6fa5
          /* Convert to a string for accept_character(). */
Packit Service fb6fa5
          if (keyval_uchar != 0)
Packit Service fb6fa5
            {
Packit Service fb6fa5
              /* max length of UTF-8 sequence = 6 + 1 for NUL termination */
Packit Service fb6fa5
              gchar keyval_utf8[7];
Packit Service fb6fa5
              gint  length;
Packit Service fb6fa5
Packit Service fb6fa5
              length = g_unichar_to_utf8 (keyval_uchar, keyval_utf8);
Packit Service fb6fa5
              keyval_utf8[length] = '\0';
Packit Service fb6fa5
Packit Service fb6fa5
              accept_character (multipress_context, keyval_utf8);
Packit Service fb6fa5
Packit Service fb6fa5
              return TRUE; /* key handled */
Packit Service fb6fa5
            }
Packit Service fb6fa5
        }
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  parent = (GtkIMContextClass *)im_context_multipress_parent_class;
Packit Service fb6fa5
Packit Service fb6fa5
  /* The default implementation just returns FALSE, but it is generally
Packit Service fb6fa5
   * a good idea to call the base class implementation: */
Packit Service fb6fa5
  if (parent->filter_keypress)
Packit Service fb6fa5
    return (*parent->filter_keypress) (context, event);
Packit Service fb6fa5
Packit Service fb6fa5
  return FALSE;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
vfunc_reset (GtkIMContext *context)
Packit Service fb6fa5
{
Packit Service fb6fa5
  clear_compose_buffer (GTK_IM_CONTEXT_MULTIPRESS (context));
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
vfunc_get_preedit_string (GtkIMContext   *context,
Packit Service fb6fa5
                          gchar         **str,
Packit Service fb6fa5
                          PangoAttrList **attrs,
Packit Service fb6fa5
                          gint           *cursor_pos)
Packit Service fb6fa5
{
Packit Service fb6fa5
  gsize len_bytes = 0;
Packit Service fb6fa5
  gsize len_utf8_chars = 0;
Packit Service fb6fa5
Packit Service fb6fa5
  /* Show the user what character he will get if he accepts: */
Packit Service fb6fa5
  if (str != NULL)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      const gchar *match;
Packit Service fb6fa5
Packit Service fb6fa5
      match = GTK_IM_CONTEXT_MULTIPRESS (context)->tentative_match;
Packit Service fb6fa5
Packit Service fb6fa5
      if (match == NULL)
Packit Service fb6fa5
        match = ""; /* *str must not be NUL */
Packit Service fb6fa5
Packit Service fb6fa5
      len_bytes = strlen (match); /* byte count */
Packit Service fb6fa5
      len_utf8_chars = g_utf8_strlen (match, len_bytes); /* character count */
Packit Service fb6fa5
Packit Service fb6fa5
      *str = g_strndup (match, len_bytes);
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  /* Underline it, to show the user that he is in compose mode: */
Packit Service fb6fa5
  if (attrs != NULL)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      *attrs = pango_attr_list_new ();
Packit Service fb6fa5
Packit Service fb6fa5
      if (len_bytes > 0)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          PangoAttribute *attr;
Packit Service fb6fa5
Packit Service fb6fa5
          attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
Packit Service fb6fa5
          attr->start_index = 0;
Packit Service fb6fa5
          attr->end_index = len_bytes;
Packit Service fb6fa5
          pango_attr_list_insert (*attrs, attr);
Packit Service fb6fa5
        }
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  if (cursor_pos)
Packit Service fb6fa5
    *cursor_pos = len_utf8_chars;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
/* Open the configuration file and fill in the key_sequences hash table
Packit Service fb6fa5
 * with key/character-list pairs taken from the [keys] group of the file.
Packit Service fb6fa5
 */
Packit Service fb6fa5
static void
Packit Service fb6fa5
load_config (GtkImContextMultipress *self)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GKeyFile *key_file;
Packit Service fb6fa5
  GError   *error = NULL;
Packit Service fb6fa5
  gchar   **keys;
Packit Service fb6fa5
  gsize     n_keys = 0;
Packit Service fb6fa5
  gsize     i;
Packit Service fb6fa5
Packit Service fb6fa5
  key_file = g_key_file_new ();
Packit Service fb6fa5
Packit Service fb6fa5
  if (!g_key_file_load_from_file (key_file, CONFIGURATION_FILENAME,
Packit Service fb6fa5
                                  G_KEY_FILE_NONE, &error))
Packit Service fb6fa5
    {
Packit Service fb6fa5
      g_warning ("Error while trying to open the %s configuration file: %s",
Packit Service fb6fa5
                 CONFIGURATION_FILENAME, error->message);
Packit Service fb6fa5
      g_error_free (error);
Packit Service fb6fa5
      g_key_file_free (key_file);
Packit Service fb6fa5
      return;
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  keys = g_key_file_get_keys (key_file, "keys", &n_keys, &error);
Packit Service fb6fa5
Packit Service fb6fa5
  if (error != NULL)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      g_warning ("Error while trying to read the %s configuration file: %s",
Packit Service fb6fa5
                 CONFIGURATION_FILENAME, error->message);
Packit Service fb6fa5
      g_error_free (error);
Packit Service fb6fa5
      g_key_file_free (key_file);
Packit Service fb6fa5
      return;
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  for (i = 0; i < n_keys; ++i)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      KeySequence *seq;
Packit Service fb6fa5
      guint        keyval;
Packit Service fb6fa5
Packit Service fb6fa5
      keyval = gdk_keyval_from_name (keys[i]);
Packit Service fb6fa5
Packit Service fb6fa5
      if (keyval == GDK_VoidSymbol)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          g_warning ("Error while trying to read the %s configuration file: "
Packit Service fb6fa5
                     "invalid key name \"%s\"",
Packit Service fb6fa5
                     CONFIGURATION_FILENAME, keys[i]);
Packit Service fb6fa5
          continue;
Packit Service fb6fa5
        }
Packit Service fb6fa5
Packit Service fb6fa5
      seq = g_slice_new (KeySequence);
Packit Service fb6fa5
      seq->characters = g_key_file_get_string_list (key_file, "keys", keys[i],
Packit Service fb6fa5
                                                    &seq->n_characters, &error);
Packit Service fb6fa5
      if (error != NULL)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          g_warning ("Error while trying to read the %s configuration file: %s",
Packit Service fb6fa5
                     CONFIGURATION_FILENAME, error->message);
Packit Service fb6fa5
          g_error_free (error);
Packit Service fb6fa5
          error = NULL;
Packit Service fb6fa5
          g_slice_free (KeySequence, seq);
Packit Service fb6fa5
          continue;
Packit Service fb6fa5
        }
Packit Service fb6fa5
Packit Service fb6fa5
      /* Ownership of the KeySequence is taken over by the hash table */
Packit Service fb6fa5
      g_hash_table_insert (self->key_sequences, GUINT_TO_POINTER (keyval), seq);
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  g_strfreev (keys);
Packit Service fb6fa5
  g_key_file_free (key_file);
Packit Service fb6fa5
}