Re: yiddish input method



On Mon, Apr 07, 2003 at 16:31:39 -0400, Raphael Finkel wrote:
> 
> It's a matter of taste, not function.  I think it is far more pleasant to have
> the currently determined character visible and then to undo it if it turns out
> to be the wrong choice.  Otherwise, there will be cases where more than one
> letter gets delayed, and it will be very annoying.

I agree, but I still think that the backspacing hackery is
unnecessary. Please try my attached version. It doesn't
handle final forms yet, but it will (another day, soon :).
I'm sure it has other bugs, but those should be easy to fix.

Noah
/*
 * Copyright (c) 2003  Noah Levitt <nlevitt аt columbia.edu>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * GTK+ Yiddish input module 
 * modeled after Raphael Finkel's Yiddish input module
 */

#include <gtk/gtkimcontext.h>
#include <gtk/gtkimmodule.h>
#include <gtk/gtkintl.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>


GType type_yiddish = 0;

enum { YIDDISH_MAX_COMPOSE_LEN = 2 };

typedef enum 
{ 
  YIDDISH_POSITION_MEDIAL, 
  YIDDISH_POSITION_INITIAL, 
  YIDDISH_POSITION_FINAL, 
}
YiddishPosition;

/* just like GtkIMContextSimple */
static guint compose_buffer[YIDDISH_MAX_COMPOSE_LEN + 1];
static int n_compose = 0;

/* medial is the sorta default */
typedef struct
{
  guint keys[YIDDISH_MAX_COMPOSE_LEN + 1];  /* 0-terminated */
  gchar *medial;
  gchar *initial;
}
ComposeSequence;

static ComposeSequence yiddish_compose_seqs[] = 
{
  { { GDK_a,          0,              0, }, "אַ",   NULL, }, 
  { { GDK_A,          0,              0, }, "א",   NULL, },
  { { GDK_b,          0,              0, }, "ב",   NULL, },
  { { GDK_B,          0,              0, }, "בֿ",   NULL, },
  { { GDK_c,          0,              0, }, "צ",   NULL, },
  { { GDK_C,          0,              0, }, "צ",   NULL, },
  { { GDK_g,          0,              0, }, "ג",   NULL, },
  { { GDK_d,          0,              0, }, "ד",   NULL, },
  { { GDK_h,          0,              0, }, "ה",   NULL, },
  { { GDK_i,          0,              0, }, "י",   "אי", },
  { { GDK_I,          0,              0, }, "יִ",   "איִ", },
  { { GDK_v,          0,              0, }, "װ",   NULL, },
  { { GDK_z,          0,              0, }, "ז",   NULL, },
  { { GDK_H,          0,              0, }, "ח",   NULL, },
  { { GDK_t,          0,              0, }, "ט",   NULL, },
  { { GDK_T,          0,              0, }, "תּ",   NULL, },
  { { GDK_y,          0,              0, }, "י",   NULL, },
  { { GDK_x,          0,              0, }, "כ",   NULL, },
  { { GDK_X,          0,              0, }, "כ",   NULL, },
  { { GDK_l,          0,              0, }, "ל",   NULL, },
  { { GDK_m,          0,              0, }, "מ",   NULL, },
  { { GDK_M,          0,              0, }, "מ",   NULL, },
  { { GDK_n,          0,              0, }, "נ",   NULL, },
  { { GDK_N,          0,              0, }, "נ",   NULL, },
  { { GDK_s,          0,              0, }, "ס",   NULL, },
  { { GDK_S,          0,              0, }, "ת",   NULL, },
  { { GDK_e,          0,              0, }, "ע",   NULL, },
  { { GDK_E,          0,              0, }, "ײ",   "אײ", },
  { { GDK_o,          0,              0, }, "אָ",   NULL, },
  { { GDK_O,          0,              0, }, "ױ",   "אױ", },
  { { GDK_u,          0,              0, }, "ו",   "או", },
  { { GDK_U,          0,              0, }, "וּ",   "אוּ", },
  { { GDK_p,          0,              0, }, "פּ",   NULL, },
  { { GDK_P,          0,              0, }, "פ",   NULL, },
  { { GDK_w,          0,              0, }, "ש",   NULL, },
  { { GDK_W,          0,              0, }, "שׂ",   NULL, },
  { { GDK_f,          0,              0, }, "פ",   NULL, },
  { { GDK_F,          0,              0, }, "פ",   NULL, },
  { { GDK_k,          0,              0, }, "ק",   NULL, },
  { { GDK_K,          0,              0, }, "כּ",   NULL, },
  { { GDK_r,          0,              0, }, "ר",   NULL, },
  { { GDK_Y,          0,              0, }, "ײַ",   "אײַ", },
  { { GDK_minus,      0,              0, }, "־",   NULL, },
  { { GDK_apostrophe, 0,              0, }, "'",   NULL, },
  { { GDK_comma,      0,              0, }, ",",   NULL, },
  { { GDK_s,          GDK_h,          0, }, "ש",   NULL, },
  { { GDK_t,          GDK_s,          0, }, "צ",   NULL, },
  { { GDK_t,          GDK_z,          0, }, "צ",   NULL, },
  { { GDK_z,          GDK_h,          0, }, "זש",  NULL, },
  { { GDK_a,          GDK_y,          0, }, "ײַ",   "אײַ", },
  { { GDK_o,          GDK_y,          0, }, "ױ",   "אױ", },
  { { GDK_d,          GDK_j,          0, }, "דזש", NULL, },
  { { GDK_e,          GDK_y,          0, }, "ײ",   "אײ", },
  { { GDK_apostrophe, GDK_apostrophe, 0, }, "“",   NULL, },
  { { GDK_comma,      GDK_comma,      0, }, "„",   NULL, },
  { { GDK_k,          GDK_h,          0, }, "כ",   NULL, },
  { { GDK_c,          GDK_h,          0, }, "כ",   NULL, },
  { { GDK_u,          GDK_v,          0, }, "וּװ",  "אוּװ", },
  { { GDK_u,          GDK_u,          0, }, "וּו",  "אוּו", },
  { { GDK_u,          GDK_i,          0, }, "ויִ",  "אויִ", },
  { { GDK_u,          GDK_y,          0, }, "וּי",  "אוּי", },
  { { GDK_v,          GDK_u,          0, }, "װוּ",  NULL, },
  { { GDK_y,          GDK_i,          0, }, "ייִ",  NULL, },
  { { GDK_i,          GDK_i,          0, }, "יִיִ",  "איִיִ", },
  { { GDK_i,          GDK_y,          0, }, "יִי",  "איִי", },
  { { GDK_E,          GDK_i,          0, }, "ײיִ",  "אײיִ", },
  { { GDK_i,          GDK_e,          0, }, "יִע",  "איִע", },
  { { GDK_i,          GDK_a,          0, }, "יִאַ",  "איִאַ", },
  { { GDK_i,          GDK_o,          0, }, "יִאָ",  "איִאָ", },
};


static const guint16 yiddish_compose_ignore[] = 
{
  GDK_Shift_L,
  GDK_Shift_R,
  GDK_Control_L,
  GDK_Control_R,
  GDK_Caps_Lock,
  GDK_Shift_Lock,
  GDK_Meta_L,
  GDK_Meta_R,
  GDK_Alt_L,
  GDK_Alt_R,
  GDK_Super_L,
  GDK_Super_R,
  GDK_Hyper_L,
  GDK_Hyper_R,
  GDK_Mode_switch
};


/* returns the composed string iff keys exactly matches the compose
 * sequence keys */
static ComposeSequence *
find_complete_compose_sequence (guint *keys)
{
  gint i, j;

  for (i = 0;  i < G_N_ELEMENTS (yiddish_compose_seqs);  i++)
    for (j = 0;  j <= YIDDISH_MAX_COMPOSE_LEN;  j++)
      {
        if (keys[j] != yiddish_compose_seqs[i].keys[j])
          break;
        else if (keys[j] == 0 && keys[j] == yiddish_compose_seqs[i].keys[j])
          return yiddish_compose_seqs + i;
      }

  return NULL;
}


/* returns the composed string iff keys is a substring thang of the compose
 * sequence keys */
static ComposeSequence *
find_incomplete_compose_sequence (guint *keys)
{
  gint i, j;

  for (i = 0;  i < G_N_ELEMENTS (yiddish_compose_seqs);  i++)
    for (j = 0;  j <= YIDDISH_MAX_COMPOSE_LEN;  j++)
      {
        if (keys[j] == 0 && yiddish_compose_seqs[i].keys[j] != 0)
          return yiddish_compose_seqs + i;
        else if (keys[j] != yiddish_compose_seqs[i].keys[j])
          break;
      }

  return NULL;
}


static gchar *
get_appropriate_string (ComposeSequence *comp_seq, YiddishPosition position)
{
  if (comp_seq == NULL)
    return NULL;
  else if (position == YIDDISH_POSITION_INITIAL && comp_seq->initial != NULL)
    return comp_seq->initial;
  else
    return comp_seq->medial;
}


/* is this a character that could appear in a yiddish word */
static gboolean
is_yiddish_word_character (gunichar uc)
{
  return (((uc >= 0x0590 && uc <= 0x5ff) || (uc >= 0xfb1d && uc <= 0xfb4f))
          && g_unichar_isdefined (uc) && ! g_unichar_ispunct (uc));

}


/* XXX: does not check for final */
static YiddishPosition
get_yiddish_position (GtkIMContext *context)
{
  gchar *text;
  gchar *prevp;
  gint cursor_index;
  gunichar uc;

  if (! gtk_im_context_get_surrounding (context, &text, &cursor_index))
    return YIDDISH_POSITION_MEDIAL;

  prevp = g_utf8_find_prev_char (text, text + cursor_index);
  if (prevp == NULL)
    return YIDDISH_POSITION_INITIAL;

  uc = g_utf8_get_char_validated (prevp, text + cursor_index - prevp);
  g_return_val_if_fail (uc != (gunichar)(-1) && uc != (gunichar)(-2), 
                        YIDDISH_POSITION_MEDIAL);

  if (is_yiddish_word_character (uc))
    return YIDDISH_POSITION_MEDIAL;
  else
    return YIDDISH_POSITION_INITIAL;
}


static void     
yiddish_get_preedit_string (GtkIMContext   *context,
                            gchar         **str,
                            PangoAttrList **attrs,
                            gint           *cursor_pos)
{
  gchar *string;
  gint len;

  string = get_appropriate_string (find_complete_compose_sequence (compose_buffer),
                                   get_yiddish_position (context));

  if (string == NULL)
    *str = g_strdup ("");
  else
    *str = g_strdup (string);

  len = strlen (*str);

  if (attrs)
    {
      *attrs = pango_attr_list_new ();

      if (len != 0)
        {
          PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
          attr->start_index = 0;
          attr->end_index = len;
          pango_attr_list_insert (*attrs, attr);
        }
    }

  if (cursor_pos)
    *cursor_pos = len;
}


static void
yiddish_reset (GtkIMContext *context)
{
  memset (compose_buffer, 0, sizeof (compose_buffer));
  n_compose = 0;
  g_signal_emit_by_name (context, "preedit-changed");
}


static void
commit_string (GtkIMContext *context, gchar *string)
{
  g_signal_emit_by_name (context, "commit", string);
  yiddish_reset (context);
}


static gboolean
no_sequence_matches (GtkIMContext *context, GdkEventKey *event)
{
  gunichar uc;
  gchar buf[7];

  uc = gdk_keyval_to_unicode (event->keyval);
  if (uc != 0)
    {
      buf[g_unichar_to_utf8 (uc, buf)] = '\0';
      commit_string (context, buf);
      return TRUE;
    }
  else
    return FALSE;
}


static gboolean
yiddish_filter_keypress (GtkIMContext *context,
                         GdkEventKey  *event)
{
  YiddishPosition position;
  gchar *string;
  gint i;

  if (event->type == GDK_KEY_RELEASE)
    return FALSE;

  for (i = 0;  i < G_N_ELEMENTS (yiddish_compose_ignore);  i++)
    if (event->keyval == yiddish_compose_ignore[i])
      return FALSE;

  position = get_yiddish_position (context);

  compose_buffer[n_compose] = event->keyval;
  n_compose++;

  if (find_incomplete_compose_sequence (compose_buffer) != NULL)
    {
      g_signal_emit_by_name (context, "preedit-changed");
      return TRUE;
    }
  else if ((string = get_appropriate_string (find_complete_compose_sequence (compose_buffer), position)) != NULL)
    {
      commit_string (context, string);
      return TRUE;
    }

  /* the last key shouldn't be in the compose buffer */
  n_compose--;
  compose_buffer[n_compose] = 0;

  /* if we have a sequence in the buffer, commit that, then deal with the
   * character again; otherwise we have a character that doesn't start any
   * sequence, so commit it standardly */
  if (n_compose > 0)
    {
      string = get_appropriate_string (find_complete_compose_sequence (compose_buffer), position);
      commit_string (context, string);

      return yiddish_filter_keypress (context, event);
    }
  else
    return no_sequence_matches (context, event);
}


static void
yiddish_class_init (GtkIMContextClass *clazz)
{
  clazz->filter_keypress = yiddish_filter_keypress;
  clazz->get_preedit_string = yiddish_get_preedit_string;
  clazz->reset = yiddish_reset;
}


void 
im_module_exit ()
{
}


static void
yiddish_init (GtkIMContext *im_context)
{
}


static void
yiddish_register_type (GTypeModule *module)
{
  static const GTypeInfo object_info =
  {
    sizeof (GtkIMContextClass),
    (GBaseInitFunc) NULL,
    (GBaseFinalizeFunc) NULL,
    (GClassInitFunc) yiddish_class_init,
    NULL,           /* class_finalize */
    NULL,           /* class_data */
    sizeof (GtkIMContext),
    0,
    (GtkObjectInitFunc) yiddish_init,
  };

  type_yiddish = 
    g_type_module_register_type (module,
                                 GTK_TYPE_IM_CONTEXT,
                                 "GtkIMContextYiddish",
                                 &object_info, 0);
}


static const GtkIMContextInfo yiddish_info = 
{
  "yiddish",      /* ID */
  N_("Yiddish"),  /* Human readable name */
  "gtk+",         /* Translation domain */
  GTK_LOCALEDIR,  /* Dir for bindtextdomain */
  "yi"            /* Languages for which this module is the default */
};


static const GtkIMContextInfo *info_list[] = 
{
  &yiddish_info,
};


void
im_module_init (GTypeModule *module)
{
  yiddish_register_type (module);
}


void 
im_module_list (const GtkIMContextInfo ***contexts, gint *n_contexts)
{
  *contexts = info_list;
  *n_contexts = G_N_ELEMENTS (info_list);
}


GtkIMContext *
im_module_create (const gchar *context_id)
{
  if (strcmp (context_id, "yiddish") == 0)
    return GTK_IM_CONTEXT (g_object_new (type_yiddish, NULL));
  else
    return NULL;
}





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