Blame modules/input/gtkimcontextthai.c

Packit Service fb6fa5
/* GTK - The GIMP Toolkit
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
 * Author:  Theppitak Karoonboonyanan <thep@linux.thai.net>
Packit Service fb6fa5
 *
Packit Service fb6fa5
 */
Packit Service fb6fa5
Packit Service fb6fa5
#include <string.h>
Packit Service fb6fa5
Packit Service fb6fa5
#include <gdk/gdkkeysyms.h>
Packit Service fb6fa5
#include "gtkimcontextthai.h"
Packit Service fb6fa5
#include "thai-charprop.h"
Packit Service fb6fa5
Packit Service fb6fa5
static void     gtk_im_context_thai_class_init          (GtkIMContextThaiClass *class);
Packit Service fb6fa5
static void     gtk_im_context_thai_init                (GtkIMContextThai      *im_context_thai);
Packit Service fb6fa5
static gboolean gtk_im_context_thai_filter_keypress     (GtkIMContext          *context,
Packit Service fb6fa5
						         GdkEventKey           *key);
Packit Service fb6fa5
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
static void     forget_previous_chars (GtkIMContextThai *context_thai);
Packit Service fb6fa5
static void     remember_previous_char (GtkIMContextThai *context_thai,
Packit Service fb6fa5
                                        gunichar new_char);
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
Packit Service fb6fa5
static GObjectClass *parent_class;
Packit Service fb6fa5
Packit Service fb6fa5
GType gtk_type_im_context_thai = 0;
Packit Service fb6fa5
Packit Service fb6fa5
void
Packit Service fb6fa5
gtk_im_context_thai_register_type (GTypeModule *type_module)
Packit Service fb6fa5
{
Packit Service fb6fa5
  const GTypeInfo im_context_thai_info =
Packit Service fb6fa5
  {
Packit Service fb6fa5
    sizeof (GtkIMContextThaiClass),
Packit Service fb6fa5
    (GBaseInitFunc) NULL,
Packit Service fb6fa5
    (GBaseFinalizeFunc) NULL,
Packit Service fb6fa5
    (GClassInitFunc) gtk_im_context_thai_class_init,
Packit Service fb6fa5
    NULL,           /* class_finalize */    
Packit Service fb6fa5
    NULL,           /* class_data */
Packit Service fb6fa5
    sizeof (GtkIMContextThai),
Packit Service fb6fa5
    0,
Packit Service fb6fa5
    (GInstanceInitFunc) gtk_im_context_thai_init,
Packit Service fb6fa5
  };
Packit Service fb6fa5
Packit Service fb6fa5
  gtk_type_im_context_thai = 
Packit Service fb6fa5
    g_type_module_register_type (type_module,
Packit Service fb6fa5
                                 GTK_TYPE_IM_CONTEXT,
Packit Service fb6fa5
                                 "GtkIMContextThai",
Packit Service fb6fa5
                                 &im_context_thai_info, 0);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
gtk_im_context_thai_class_init (GtkIMContextThaiClass *class)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
Packit Service fb6fa5
Packit Service fb6fa5
  parent_class = g_type_class_peek_parent (class);
Packit Service fb6fa5
Packit Service fb6fa5
  im_context_class->filter_keypress = gtk_im_context_thai_filter_keypress;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
gtk_im_context_thai_init (GtkIMContextThai *context_thai)
Packit Service fb6fa5
{
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
  forget_previous_chars (context_thai);
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
  context_thai->isc_mode = ISC_BASICCHECK;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
GtkIMContext *
Packit Service fb6fa5
gtk_im_context_thai_new (void)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkIMContextThai *result;
Packit Service fb6fa5
Packit Service fb6fa5
  result = GTK_IM_CONTEXT_THAI (g_object_new (GTK_TYPE_IM_CONTEXT_THAI, NULL));
Packit Service fb6fa5
Packit Service fb6fa5
  return GTK_IM_CONTEXT (result);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
GtkIMContextThaiISCMode
Packit Service fb6fa5
gtk_im_context_thai_get_isc_mode (GtkIMContextThai *context_thai)
Packit Service fb6fa5
{
Packit Service fb6fa5
  return context_thai->isc_mode;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
GtkIMContextThaiISCMode
Packit Service fb6fa5
gtk_im_context_thai_set_isc_mode (GtkIMContextThai *context_thai,
Packit Service fb6fa5
                                  GtkIMContextThaiISCMode mode)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkIMContextThaiISCMode prev_mode = context_thai->isc_mode;
Packit Service fb6fa5
  context_thai->isc_mode = mode;
Packit Service fb6fa5
  return prev_mode;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
is_context_lost_key(guint keyval)
Packit Service fb6fa5
{
Packit Service fb6fa5
  return ((keyval & 0xFF00) == 0xFF00) &&
Packit Service fb6fa5
         (keyval == GDK_BackSpace ||
Packit Service fb6fa5
          keyval == GDK_Tab ||
Packit Service fb6fa5
          keyval == GDK_Linefeed ||
Packit Service fb6fa5
          keyval == GDK_Clear ||
Packit Service fb6fa5
          keyval == GDK_Return ||
Packit Service fb6fa5
          keyval == GDK_Pause ||
Packit Service fb6fa5
          keyval == GDK_Scroll_Lock ||
Packit Service fb6fa5
          keyval == GDK_Sys_Req ||
Packit Service fb6fa5
          keyval == GDK_Escape ||
Packit Service fb6fa5
          keyval == GDK_Delete ||
Packit Service fb6fa5
          (GDK_Home <= keyval && keyval <= GDK_Begin) || /* IsCursorkey */
Packit Service fb6fa5
          (GDK_KP_Space <= keyval && keyval <= GDK_KP_Delete) || /* IsKeypadKey, non-chars only */
Packit Service fb6fa5
          (GDK_Select <= keyval && keyval <= GDK_Break) || /* IsMiscFunctionKey */
Packit Service fb6fa5
          (GDK_F1 <= keyval && keyval <= GDK_F35)); /* IsFunctionKey */
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
is_context_intact_key(guint keyval)
Packit Service fb6fa5
{
Packit Service fb6fa5
  return (((keyval & 0xFF00) == 0xFF00) &&
Packit Service fb6fa5
           ((GDK_Shift_L <= keyval && keyval <= GDK_Hyper_R) || /* IsModifierKey */
Packit Service fb6fa5
            (keyval == GDK_Mode_switch) ||
Packit Service fb6fa5
            (keyval == GDK_Num_Lock))) ||
Packit Service fb6fa5
         (((keyval & 0xFE00) == 0xFE00) &&
Packit Service fb6fa5
          (GDK_ISO_Lock <= keyval && keyval <= GDK_ISO_Last_Group_Lock));
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
thai_is_accept (gunichar new_char, gunichar prev_char, gint isc_mode)
Packit Service fb6fa5
{
Packit Service fb6fa5
  switch (isc_mode)
Packit Service fb6fa5
    {
Packit Service fb6fa5
    case ISC_PASSTHROUGH:
Packit Service fb6fa5
      return TRUE;
Packit Service fb6fa5
Packit Service fb6fa5
    case ISC_BASICCHECK:
Packit Service fb6fa5
      return TAC_compose_input (prev_char, new_char) != 'R';
Packit Service fb6fa5
Packit Service fb6fa5
    case ISC_STRICT:
Packit Service fb6fa5
      {
Packit Service fb6fa5
        int op = TAC_compose_input (prev_char, new_char);
Packit Service fb6fa5
        return op != 'R' && op != 'S';
Packit Service fb6fa5
      }
Packit Service fb6fa5
Packit Service fb6fa5
    default:
Packit Service fb6fa5
      return FALSE;
Packit Service fb6fa5
    }
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
#define thai_is_composible(n,p)  (TAC_compose_input ((p), (n)) == 'C')
Packit Service fb6fa5
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
static void
Packit Service fb6fa5
forget_previous_chars (GtkIMContextThai *context_thai)
Packit Service fb6fa5
{
Packit Service fb6fa5
  memset (context_thai->char_buff, 0, sizeof (context_thai->char_buff));
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static void
Packit Service fb6fa5
remember_previous_char (GtkIMContextThai *context_thai, gunichar new_char)
Packit Service fb6fa5
{
Packit Service fb6fa5
  memmove (context_thai->char_buff + 1, context_thai->char_buff,
Packit Service fb6fa5
           (GTK_IM_CONTEXT_THAI_BUFF_SIZE - 1) * sizeof (context_thai->char_buff[0]));
Packit Service fb6fa5
  context_thai->char_buff[0] = new_char;
Packit Service fb6fa5
}
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
Packit Service fb6fa5
static gunichar
Packit Service fb6fa5
get_previous_char (GtkIMContextThai *context_thai, gint offset)
Packit Service fb6fa5
{
Packit Service fb6fa5
  gchar *surrounding;
Packit Service fb6fa5
  gint  cursor_index;
Packit Service fb6fa5
Packit Service fb6fa5
  if (gtk_im_context_get_surrounding ((GtkIMContext *)context_thai,
Packit Service fb6fa5
                                      &surrounding, &cursor_index))
Packit Service fb6fa5
    {
Packit Service fb6fa5
      gunichar prev_char;
Packit Service fb6fa5
      gchar *p, *q;
Packit Service fb6fa5
Packit Service fb6fa5
      prev_char = 0;
Packit Service fb6fa5
      p = surrounding + cursor_index;
Packit Service fb6fa5
      for (q = p; offset < 0 && q > surrounding; ++offset)
Packit Service fb6fa5
        q = g_utf8_prev_char (q);
Packit Service fb6fa5
      if (offset == 0)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          prev_char = g_utf8_get_char_validated (q, p - q);
Packit Service fb6fa5
          if (prev_char < 0)
Packit Service fb6fa5
            prev_char = 0;
Packit Service fb6fa5
        }
Packit Service fb6fa5
      g_free (surrounding);
Packit Service fb6fa5
      return prev_char;
Packit Service fb6fa5
    }
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
  else
Packit Service fb6fa5
    {
Packit Service fb6fa5
      offset = -offset - 1;
Packit Service fb6fa5
      if (0 <= offset && offset < GTK_IM_CONTEXT_THAI_BUFF_SIZE)
Packit Service fb6fa5
        return context_thai->char_buff[offset];
Packit Service fb6fa5
    }
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
Packit Service fb6fa5
    return 0;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
gtk_im_context_thai_commit_chars (GtkIMContextThai *context_thai,
Packit Service fb6fa5
                                  gunichar *s, gsize len)
Packit Service fb6fa5
{
Packit Service fb6fa5
  gchar *utf8;
Packit Service fb6fa5
Packit Service fb6fa5
  utf8 = g_ucs4_to_utf8 (s, len, NULL, NULL, NULL);
Packit Service fb6fa5
  if (!utf8)
Packit Service fb6fa5
    return FALSE;
Packit Service fb6fa5
Packit Service fb6fa5
  g_signal_emit_by_name (context_thai, "commit", utf8);
Packit Service fb6fa5
Packit Service fb6fa5
  g_free (utf8);
Packit Service fb6fa5
  return TRUE;
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
accept_input (GtkIMContextThai *context_thai, gunichar new_char)
Packit Service fb6fa5
{
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
  remember_previous_char (context_thai, new_char);
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
Packit Service fb6fa5
  return gtk_im_context_thai_commit_chars (context_thai, &new_char, 1);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
reorder_input (GtkIMContextThai *context_thai,
Packit Service fb6fa5
               gunichar prev_char, gunichar new_char)
Packit Service fb6fa5
{
Packit Service fb6fa5
  gunichar buf[2];
Packit Service fb6fa5
Packit Service fb6fa5
  if (!gtk_im_context_delete_surrounding (GTK_IM_CONTEXT (context_thai), -1, 1))
Packit Service fb6fa5
    return FALSE;
Packit Service fb6fa5
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
  forget_previous_chars (context_thai);
Packit Service fb6fa5
  remember_previous_char (context_thai, new_char);
Packit Service fb6fa5
  remember_previous_char (context_thai, prev_char);
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
Packit Service fb6fa5
  buf[0] = new_char;
Packit Service fb6fa5
  buf[1] = prev_char;
Packit Service fb6fa5
  return gtk_im_context_thai_commit_chars (context_thai, buf, 2);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
replace_input (GtkIMContextThai *context_thai, gunichar new_char)
Packit Service fb6fa5
{
Packit Service fb6fa5
  if (!gtk_im_context_delete_surrounding (GTK_IM_CONTEXT (context_thai), -1, 1))
Packit Service fb6fa5
    return FALSE;
Packit Service fb6fa5
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
  forget_previous_chars (context_thai);
Packit Service fb6fa5
  remember_previous_char (context_thai, new_char);
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
Packit Service fb6fa5
  return gtk_im_context_thai_commit_chars (context_thai, &new_char, 1);
Packit Service fb6fa5
}
Packit Service fb6fa5
Packit Service fb6fa5
static gboolean
Packit Service fb6fa5
gtk_im_context_thai_filter_keypress (GtkIMContext *context,
Packit Service fb6fa5
                                     GdkEventKey  *event)
Packit Service fb6fa5
{
Packit Service fb6fa5
  GtkIMContextThai *context_thai = GTK_IM_CONTEXT_THAI (context);
Packit Service fb6fa5
  gunichar prev_char, new_char;
Packit Service fb6fa5
  gboolean is_reject;
Packit Service fb6fa5
  GtkIMContextThaiISCMode isc_mode;
Packit Service fb6fa5
Packit Service fb6fa5
  if (event->type != GDK_KEY_PRESS)
Packit Service fb6fa5
    return FALSE;
Packit Service fb6fa5
Packit Service fb6fa5
  if (event->state & (GDK_MODIFIER_MASK
Packit Service fb6fa5
                      & ~(GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD2_MASK)) ||
Packit Service fb6fa5
      is_context_lost_key (event->keyval))
Packit Service fb6fa5
    {
Packit Service fb6fa5
#ifndef GTK_IM_CONTEXT_THAI_NO_FALLBACK
Packit Service fb6fa5
      forget_previous_chars (context_thai);
Packit Service fb6fa5
#endif /* !GTK_IM_CONTEXT_THAI_NO_FALLBACK */
Packit Service fb6fa5
      return FALSE;
Packit Service fb6fa5
    }
Packit Service fb6fa5
  if (event->keyval == 0 || is_context_intact_key (event->keyval))
Packit Service fb6fa5
    {
Packit Service fb6fa5
      return FALSE;
Packit Service fb6fa5
    }
Packit Service fb6fa5
Packit Service fb6fa5
  prev_char = get_previous_char (context_thai, -1);
Packit Service fb6fa5
  if (!prev_char)
Packit Service fb6fa5
    prev_char = ' ';
Packit Service fb6fa5
  new_char = gdk_keyval_to_unicode (event->keyval);
Packit Service fb6fa5
  is_reject = TRUE;
Packit Service fb6fa5
  isc_mode = gtk_im_context_thai_get_isc_mode (context_thai);
Packit Service fb6fa5
  if (thai_is_accept (new_char, prev_char, isc_mode))
Packit Service fb6fa5
    {
Packit Service fb6fa5
      accept_input (context_thai, new_char);
Packit Service fb6fa5
      is_reject = FALSE;
Packit Service fb6fa5
    }
Packit Service fb6fa5
  else
Packit Service fb6fa5
    {
Packit Service fb6fa5
      gunichar context_char;
Packit Service fb6fa5
Packit Service fb6fa5
      /* rejected, trying to correct */
Packit Service fb6fa5
      context_char = get_previous_char (context_thai, -2);
Packit Service fb6fa5
      if (context_char)
Packit Service fb6fa5
        {
Packit Service fb6fa5
          if (thai_is_composible (new_char, context_char))
Packit Service fb6fa5
            {
Packit Service fb6fa5
              if (thai_is_composible (prev_char, new_char))
Packit Service fb6fa5
                is_reject = !reorder_input (context_thai, prev_char, new_char);
Packit Service fb6fa5
              else if (thai_is_composible (prev_char, context_char))
Packit Service fb6fa5
                is_reject = !replace_input (context_thai, new_char);
Packit Service fb6fa5
              else if ((TAC_char_class (prev_char) == FV1
Packit Service fb6fa5
                        || TAC_char_class (prev_char) == AM)
Packit Service fb6fa5
                       && TAC_char_class (new_char) == TONE)
Packit Service fb6fa5
                is_reject = !reorder_input (context_thai, prev_char, new_char);
Packit Service fb6fa5
            }
Packit Service fb6fa5
	  else if (thai_is_accept (new_char, context_char, isc_mode))
Packit Service fb6fa5
            is_reject = !replace_input (context_thai, new_char);
Packit Service fb6fa5
        }
Packit Service fb6fa5
    }
Packit Service fb6fa5
  if (is_reject)
Packit Service fb6fa5
    {
Packit Service fb6fa5
      /* reject character */
Packit Service fb6fa5
      gdk_beep ();
Packit Service fb6fa5
    }
Packit Service fb6fa5
  return TRUE;
Packit Service fb6fa5
}
Packit Service fb6fa5