+\f
+
+static int take_action_list (MInputContext *ic, MPlist *action_list);
+static void preedit_commit (MInputContext *ic);
+
+static void
+shift_state (MInputContext *ic, MSymbol state_name)
+{
+ MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+ MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+ MIMState *orig_state = ic_info->state, *state;
+
+ /* Find a state to shift to. If not found, shift to the initial
+ state. */
+ if (state_name == Mt)
+ {
+ if (! ic_info->prev_state)
+ return;
+ state = ic_info->prev_state;
+ }
+ else if (state_name == Mnil)
+ {
+ state = (MIMState *) MPLIST_VAL (im_info->states);
+ }
+ else
+ {
+ state = (MIMState *) mplist_get (im_info->states, state_name);
+ if (! state)
+ state = (MIMState *) MPLIST_VAL (im_info->states);
+ }
+
+ MDEBUG_PRINT1 ("\n [IM] (shift %s)", MSYMBOL_NAME (state->name));
+
+ /* Enter the new state. */
+ ic_info->state = state;
+ ic_info->map = state->map;
+ ic_info->state_key_head = ic_info->key_head;
+ if (state == (MIMState *) MPLIST_VAL (im_info->states)
+ && orig_state)
+ /* We have shifted to the initial state. */
+ preedit_commit (ic);
+ mtext_cpy (ic_info->preedit_saved, ic->preedit);
+ ic_info->state_pos = ic->cursor_pos;
+ if (state != orig_state)
+ {
+ if (state == (MIMState *) MPLIST_VAL (im_info->states))
+ {
+ /* Shifted to the initial state. */
+ ic_info->prev_state = NULL;
+ M17N_OBJECT_UNREF (ic_info->vars_saved);
+ ic_info->vars_saved = mplist_copy (ic_info->vars);
+ }
+ else
+ ic_info->prev_state = orig_state;
+
+ if (state->title)
+ ic->status = state->title;
+ else
+ ic->status = im_info->title;
+ ic->status_changed = 1;
+ if (ic_info->map == ic_info->state->map
+ && ic_info->map->map_actions)
+ {
+ MDEBUG_PRINT (" init-actions:");
+ take_action_list (ic, ic_info->map->map_actions);
+ }
+ }
+}
+
+/* Find a candidate group that contains a candidate number INDEX from
+ PLIST. Set START_INDEX to the first candidate number of the group,
+ END_INDEX to the last candidate number plus 1, GROUP_INDEX to the
+ candidate group number if they are non-NULL. If INDEX is -1, find
+ the last candidate group. */
+
+static MPlist *
+find_candidates_group (MPlist *plist, int index,
+ int *start_index, int *end_index, int *group_index)
+{
+ int i = 0, gidx = 0, len;
+
+ MPLIST_DO (plist, plist)
+ {
+ if (MPLIST_MTEXT_P (plist))
+ len = mtext_nchars (MPLIST_MTEXT (plist));
+ else
+ len = mplist_length (MPLIST_PLIST (plist));
+ if (index < 0 ? MPLIST_TAIL_P (MPLIST_NEXT (plist))
+ : i + len > index)
+ {
+ if (start_index)
+ *start_index = i;
+ if (end_index)
+ *end_index = i + len;
+ if (group_index)
+ *group_index = gidx;
+ return plist;
+ }
+ i += len;
+ gidx++;
+ }
+ return NULL;
+}
+
+/* Adjust markers for the change of preedit text.
+ If FROM == TO, the change is insertion of INS chars.
+ If FROM < TO and INS == 0, the change is deletion of the range.
+ If FROM < TO and INS > 0, the change is replacement. */
+
+static void
+adjust_markers (MInputContext *ic, int from, int to, int ins)
+{
+ MInputContextInfo *ic_info = ((MInputContext *) ic)->info;
+ MPlist *markers;
+
+ if (from == to)
+ {
+ MPLIST_DO (markers, ic_info->markers)
+ if (MPLIST_INTEGER (markers) > from)
+ MPLIST_VAL (markers) = (void *) (MPLIST_INTEGER (markers) + ins);
+ if (ic->cursor_pos >= from)
+ ic->cursor_pos += ins;
+ }
+ else
+ {
+ MPLIST_DO (markers, ic_info->markers)
+ {
+ if (MPLIST_INTEGER (markers) >= to)
+ MPLIST_VAL (markers)
+ = (void *) (MPLIST_INTEGER (markers) + ins - (to - from));
+ else if (MPLIST_INTEGER (markers) > from)
+ MPLIST_VAL (markers) = (void *) from;
+ }
+ if (ic->cursor_pos >= to)
+ ic->cursor_pos += ins - (to - from);
+ else if (ic->cursor_pos > from)
+ ic->cursor_pos = from;
+ }
+}
+
+
+static void
+preedit_insert (MInputContext *ic, int pos, MText *mt, int c)
+{
+ int nchars = mt ? mtext_nchars (mt) : 1;
+
+ if (mt)
+ mtext_ins (ic->preedit, pos, mt);
+ else
+ mtext_ins_char (ic->preedit, pos, c, 1);
+ adjust_markers (ic, pos, pos, nchars);
+ ic->preedit_changed = 1;
+}
+
+
+static void
+preedit_delete (MInputContext *ic, int from, int to)
+{
+ mtext_del (ic->preedit, from, to);
+ adjust_markers (ic, from, to, 0);
+ ic->preedit_changed = 1;
+}
+
+static void
+preedit_replace (MInputContext *ic, int from, int to, MText *mt, int c)
+{
+ int ins;
+
+ mtext_del (ic->preedit, from, to);
+ if (mt)
+ {
+ mtext_ins (ic->preedit, from, mt);
+ ins = mtext_nchars (mt);
+ }
+ else
+ {
+ mtext_ins_char (ic->preedit, from, c, 1);
+ ins = 1;
+ }
+ adjust_markers (ic, from, to, ins);
+ ic->preedit_changed = 1;
+}
+
+
+static void
+preedit_commit (MInputContext *ic)
+{
+ MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+ int preedit_len = mtext_nchars (ic->preedit);
+
+ if (preedit_len > 0)
+ {
+ MPlist *p;
+
+ mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit),
+ Mcandidate_list, NULL, 0);
+ mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit),
+ Mcandidate_index, NULL, 0);
+ mtext_cat (ic->produced, ic->preedit);
+ if (mdebug__flag & mdebug_mask)
+ {
+ int i;
+
+ MDEBUG_PRINT (" (commit");
+ for (i = 0; i < mtext_nchars (ic->preedit); i++)
+ MDEBUG_PRINT1 (" U+%04X", mtext_ref_char (ic->preedit, i));
+ MDEBUG_PRINT (")");
+ }
+
+ mtext_reset (ic->preedit);
+ mtext_reset (ic_info->preedit_saved);
+ MPLIST_DO (p, ic_info->markers)
+ MPLIST_VAL (p) = 0;
+ ic->cursor_pos = ic_info->state_pos = 0;
+ ic->preedit_changed = 1;
+ ic_info->commit_key_head = ic_info->key_head;
+ }
+ if (ic->candidate_list)
+ {
+ M17N_OBJECT_UNREF (ic->candidate_list);
+ ic->candidate_list = NULL;
+ ic->candidate_index = 0;
+ ic->candidate_from = ic->candidate_to = 0;
+ ic->candidates_changed = MINPUT_CANDIDATES_LIST_CHANGED;
+ if (ic->candidate_show)
+ {
+ ic->candidate_show = 0;
+ ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
+ }
+ }
+}
+
+static int
+new_index (MInputContext *ic, int current, int limit, MSymbol sym, MText *mt)
+{
+ int code = marker_code (sym, 0);
+
+ if (mt && (code == '[' || code == ']'))
+ {
+ int pos = current;
+
+ if (code == '[' && current > 0)
+ {
+ if (mtext_prop_range (mt, Mcandidate_list, pos - 1, &pos, NULL, 1)
+ && pos > 0)
+ current = pos;
+ }
+ else if (code == ']' && current < mtext_nchars (mt))
+ {
+ if (mtext_prop_range (mt, Mcandidate_list, pos, NULL, &pos, 1))
+ current = pos;
+ }
+ return current;
+ }
+ if (code >= 0)
+ return (code == '<' ? 0
+ : code == '>' ? limit
+ : code == '-' ? current - 1
+ : code == '+' ? current + 1
+ : code == '=' ? current
+ : code - '0' > limit ? limit
+ : code - '0');
+ if (! ic)
+ return 0;
+ return (int) mplist_get (((MInputContextInfo *) ic->info)->markers, sym);
+}
+
+static void
+update_candidate (MInputContext *ic, MTextProperty *prop, int idx)
+{
+ int from = mtext_property_start (prop);
+ int to = mtext_property_end (prop);
+ int start;
+ MPlist *candidate_list = mtext_property_value (prop);
+ MPlist *group = find_candidates_group (candidate_list, idx, &start,
+ NULL, NULL);
+ int ingroup_index = idx - start;
+ MText *mt;
+
+ if (MPLIST_MTEXT_P (group))
+ {
+ mt = MPLIST_MTEXT (group);
+ preedit_replace (ic, from, to, NULL, mtext_ref_char (mt, ingroup_index));
+ to = from + 1;
+ }
+ else
+ {
+ int i;
+ MPlist *plist;
+
+ for (i = 0, plist = MPLIST_PLIST (group); i < ingroup_index;
+ i++, plist = MPLIST_NEXT (plist));
+ mt = MPLIST_MTEXT (plist);
+ preedit_replace (ic, from, to, mt, 0);
+ to = from + mtext_nchars (mt);
+ }
+ mtext_put_prop (ic->preedit, from, to, Mcandidate_list, candidate_list);
+ mtext_put_prop (ic->preedit, from, to, Mcandidate_index, (void *) idx);
+ ic->cursor_pos = to;
+}
+
+static MCharset *
+get_select_charset (MInputContextInfo * ic_info)
+{
+ MPlist *plist = resolve_variable (ic_info, Mcandidates_charset);
+ MSymbol sym;
+
+ if (! MPLIST_VAL (plist))
+ return NULL;
+ sym = MPLIST_SYMBOL (plist);
+ if (sym == Mnil)
+ return NULL;
+ return MCHARSET (sym);
+}
+
+static MPlist *
+adjust_candidates (MPlist *plist, MCharset *charset)
+{
+ MPlist *pl;
+
+ /* plist ::= MTEXT ... | PLIST ... */
+ plist = mplist_copy (plist);
+ if (MPLIST_MTEXT_P (plist))
+ {
+ pl = plist;
+ while (! MPLIST_TAIL_P (pl))
+ {
+ /* pl ::= MTEXT ... */
+ MText *mt = MPLIST_MTEXT (pl);
+ int mt_copied = 0;
+ int i, c;
+
+ for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+ {
+ c = mtext_ref_char (mt, i);
+ if (ENCODE_CHAR (charset, c) == MCHAR_INVALID_CODE)
+ {
+ if (! mt_copied)
+ {
+ mt = mtext_dup (mt);
+ mplist_set (pl, Mtext, mt);
+ M17N_OBJECT_UNREF (mt);
+ mt_copied = 1;
+ }
+ mtext_del (mt, i, i + 1);
+ }
+ }
+ if (mtext_len (mt) > 0)
+ pl = MPLIST_NEXT (pl);
+ else
+ {
+ mplist_pop (pl);
+ M17N_OBJECT_UNREF (mt);
+ }
+ }
+ }
+ else /* MPLIST_PLIST_P (plist) */
+ {
+ pl = plist;
+ while (! MPLIST_TAIL_P (pl))
+ {
+ /* pl ::= (MTEXT ...) ... */
+ MPlist *p = MPLIST_PLIST (pl);
+ int p_copied = 0;
+ /* p ::= MTEXT ... */
+ MPlist *p0 = p;
+ int n = 0;
+
+ while (! MPLIST_TAIL_P (p0))
+ {
+ MText *mt = MPLIST_MTEXT (p0);
+ int i, c;
+
+ for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+ {
+ c = mtext_ref_char (mt, i);
+ if (ENCODE_CHAR (charset, c) == MCHAR_INVALID_CODE)
+ break;
+ }
+ if (i < 0)
+ {
+ p0 = MPLIST_NEXT (p0);
+ n++;
+ }
+ else
+ {
+ if (! p_copied)
+ {
+ p = mplist_copy (p);
+ mplist_set (pl, Mplist, p);
+ M17N_OBJECT_UNREF (p);
+ p_copied = 1;
+ p0 = p;
+ while (n-- > 0)
+ p0 = MPLIST_NEXT (p0);
+ }
+ mplist_pop (p0);
+ M17N_OBJECT_UNREF (mt);
+ }
+ }
+ if (! MPLIST_TAIL_P (p))
+ pl = MPLIST_NEXT (pl);
+ else
+ {
+ mplist_pop (pl);
+ M17N_OBJECT_UNREF (p);
+ }
+ }
+ }
+ if (MPLIST_TAIL_P (plist))
+ {
+ M17N_OBJECT_UNREF (plist);
+ return NULL;
+ }
+ return plist;
+}
+
+static MPlist *
+get_candidate_list (MInputContextInfo *ic_info, MPlist *args)
+{
+ MCharset *charset = get_select_charset (ic_info);
+ MPlist *plist;
+ int column;
+ int i, len;
+
+ plist = resolve_variable (ic_info, Mcandidates_group_size);
+ column = MPLIST_INTEGER (plist);
+
+ plist = MPLIST_PLIST (args);
+ if (charset)
+ plist = adjust_candidates (plist, charset);
+
+ if (plist && column > 0)
+ {
+ if (MPLIST_MTEXT_P (plist))
+ {
+ MText *mt = MPLIST_MTEXT (plist);
+ MPlist *next = MPLIST_NEXT (plist);
+
+ if (MPLIST_TAIL_P (next))
+ M17N_OBJECT_REF (mt);
+ else
+ {
+ mt = mtext_dup (mt);
+ while (! MPLIST_TAIL_P (next))
+ {
+ mt = mtext_cat (mt, MPLIST_MTEXT (next));
+ next = MPLIST_NEXT (next);
+ }
+ }
+ if (charset)
+ M17N_OBJECT_UNREF (plist);
+ plist = mplist ();
+ len = mtext_nchars (mt);
+ if (len <= column)
+ mplist_add (plist, Mtext, mt);
+ else
+ {
+ for (i = 0; i < len; i += column)
+ {
+ int to = (i + column < len ? i + column : len);
+ MText *sub = mtext_copy (mtext (), 0, mt, i, to);
+
+ mplist_add (plist, Mtext, sub);
+ M17N_OBJECT_UNREF (sub);
+ }
+ }
+ M17N_OBJECT_UNREF (mt);
+ }
+ else if (! MPLIST_TAIL_P (plist))
+ {
+ MPlist *tail = plist;
+ MPlist *new = mplist ();
+ MPlist *this = mplist ();
+ int count = 0;
+
+ MPLIST_DO (tail, tail)
+ {
+ MPlist *p = MPLIST_PLIST (tail);
+
+ MPLIST_DO (p, p)
+ {
+ MText *mt = MPLIST_MTEXT (p);
+
+ if (count == column)
+ {
+ mplist_add (new, Mplist, this);
+ M17N_OBJECT_UNREF (this);
+ this = mplist ();
+ count = 0;
+ }
+ mplist_add (this, Mtext, mt);
+ count++;
+ }
+ }
+ mplist_add (new, Mplist, this);
+ M17N_OBJECT_UNREF (this);
+ mplist_set (plist, Mnil, NULL);
+ MPLIST_DO (tail, new)
+ {
+ MPlist *elt = MPLIST_PLIST (tail);
+
+ mplist_add (plist, Mplist, elt);
+ }
+ M17N_OBJECT_UNREF (new);
+ }
+ }
+
+ return plist;
+}
+
+
+static MPlist *
+regularize_action (MPlist *action_list, MInputContextInfo *ic_info)
+{
+ MPlist *action = NULL;
+ MSymbol name;
+ MPlist *args;
+
+ if (MPLIST_SYMBOL_P (action_list))
+ {
+ MSymbol var = MPLIST_SYMBOL (action_list);
+ MPlist *p;
+
+ MPLIST_DO (p, ic_info->vars)
+ if (MPLIST_SYMBOL (MPLIST_PLIST (p)) == var)
+ break;
+ if (MPLIST_TAIL_P (p))
+ return NULL;
+ action = MPLIST_NEXT (MPLIST_PLIST (p));
+ mplist_set (action_list, MPLIST_KEY (action), MPLIST_VAL (action));
+ }
+
+ if (MPLIST_PLIST_P (action_list))
+ {
+ action = MPLIST_PLIST (action_list);
+ if (MPLIST_SYMBOL_P (action))
+ {
+ name = MPLIST_SYMBOL (action);
+ args = MPLIST_NEXT (action);
+ if (name == Minsert
+ && MPLIST_PLIST_P (args))
+ mplist_set (action, Msymbol, M_candidates);
+ }
+ else if (MPLIST_MTEXT_P (action) || MPLIST_PLIST_P (action))
+ {
+ action = mplist ();
+ mplist_push (action, Mplist, MPLIST_VAL (action_list));
+ mplist_push (action, Msymbol, M_candidates);
+ mplist_set (action_list, Mplist, action);
+ M17N_OBJECT_UNREF (action);
+ }
+ }
+ else if (MPLIST_MTEXT_P (action_list) || MPLIST_INTEGER_P (action_list))
+ {
+ action = mplist ();
+ mplist_push (action, MPLIST_KEY (action_list), MPLIST_VAL (action_list));
+ mplist_push (action, Msymbol, Minsert);
+ mplist_set (action_list, Mplist, action);
+ M17N_OBJECT_UNREF (action);
+ }
+ return action;
+}
+
+/* Perform list of actions in ACTION_LIST for the current input
+ context IC. If unhandle action was not performed, return 0.
+ Otherwise, return -1. */
+
+static int
+take_action_list (MInputContext *ic, MPlist *action_list)
+{
+ MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+ MPlist *candidate_list = ic->candidate_list;
+ int candidate_index = ic->candidate_index;
+ int candidate_show = ic->candidate_show;
+ MTextProperty *prop;
+
+ MPLIST_DO (action_list, action_list)
+ {
+ MPlist *action = regularize_action (action_list, ic_info);
+ MSymbol name;
+ MPlist *args;
+
+ if (! action)
+ continue;
+ name = MPLIST_SYMBOL (action);
+ args = MPLIST_NEXT (action);
+
+ MDEBUG_PRINT1 (" %s", MSYMBOL_NAME (name));
+ if (name == Minsert)
+ {
+ if (MPLIST_SYMBOL_P (args))
+ {
+ args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
+ if (! MPLIST_MTEXT_P (args) && ! MPLIST_INTEGER_P (args))
+ continue;
+ }
+ if (MPLIST_MTEXT_P (args))
+ preedit_insert (ic, ic->cursor_pos, MPLIST_MTEXT (args), 0);
+ else /* MPLIST_INTEGER_P (args)) */
+ preedit_insert (ic, ic->cursor_pos, NULL, MPLIST_INTEGER (args));
+ }
+ else if (name == M_candidates)
+ {
+ MPlist *plist = get_candidate_list (ic_info, args);
+ int len;
+
+ if (! plist || (MPLIST_PLIST_P (plist) && MPLIST_TAIL_P (plist)))
+ continue;
+ if (MPLIST_MTEXT_P (plist))
+ {
+ preedit_insert (ic, ic->cursor_pos, NULL,
+ mtext_ref_char (MPLIST_MTEXT (plist), 0));
+ len = 1;
+ }
+ else if (MPLIST_TAIL_P (MPLIST_PLIST (plist)))
+ continue;
+ else
+ {
+ MText * mt = MPLIST_MTEXT (MPLIST_PLIST (plist));
+
+ preedit_insert (ic, ic->cursor_pos, mt, 0);
+ len = mtext_nchars (mt);
+ }
+ mtext_put_prop (ic->preedit,
+ ic->cursor_pos - len, ic->cursor_pos,
+ Mcandidate_list, plist);
+ mtext_put_prop (ic->preedit,
+ ic->cursor_pos - len, ic->cursor_pos,
+ Mcandidate_index, (void *) 0);
+ }
+ else if (name == Mselect)
+ {
+ int start, end;
+ int code, idx, gindex;
+ int pos = ic->cursor_pos;
+ MPlist *group;
+ int idx_decided = 0;
+
+ if (pos == 0
+ || ! (prop = mtext_get_property (ic->preedit, pos - 1,
+ Mcandidate_list)))
+ continue;
+ idx = (int) mtext_get_prop (ic->preedit, pos - 1, Mcandidate_index);
+ group = find_candidates_group (mtext_property_value (prop), idx,
+ &start, &end, &gindex);
+ if (MPLIST_SYMBOL_P (args))
+ {
+ code = marker_code (MPLIST_SYMBOL (args), 0);
+ if (code < 0)
+ {
+ args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
+ if (! MPLIST_INTEGER_P (args))
+ continue;
+ idx = start + MPLIST_INTEGER (args);
+ if (idx < start || idx >= end)
+ continue;
+ idx_decided = 1;
+ }
+ }
+ else
+ code = -1;
+
+ if (code != '[' && code != ']')
+ {
+ if (! idx_decided)
+ idx = (start
+ + (code >= 0
+ ? new_index (NULL, ic->candidate_index - start,
+ end - start - 1, MPLIST_SYMBOL (args),
+ NULL)
+ : MPLIST_INTEGER (args)));
+ if (idx < 0)
+ {
+ find_candidates_group (mtext_property_value (prop), -1,
+ NULL, &end, NULL);
+ idx = end - 1;
+ }
+ else if (idx >= end
+ && MPLIST_TAIL_P (MPLIST_NEXT (group)))
+ idx = 0;
+ }
+ else
+ {
+ int ingroup_index = idx - start;
+ int len;
+
+ group = mtext_property_value (prop);
+ len = mplist_length (group);
+ if (code == '[')
+ {
+ gindex--;
+ if (gindex < 0)
+ gindex = len - 1;;
+ }
+ else
+ {
+ gindex++;
+ if (gindex >= len)
+ gindex = 0;
+ }
+ for (idx = 0; gindex > 0; gindex--, group = MPLIST_NEXT (group))
+ idx += (MPLIST_MTEXT_P (group)
+ ? mtext_nchars (MPLIST_MTEXT (group))
+ : mplist_length (MPLIST_PLIST (group)));
+ len = (MPLIST_MTEXT_P (group)
+ ? mtext_nchars (MPLIST_MTEXT (group))
+ : mplist_length (MPLIST_PLIST (group)));
+ if (ingroup_index >= len)
+ ingroup_index = len - 1;
+ idx += ingroup_index;
+ }
+ update_candidate (ic, prop, idx);
+ MDEBUG_PRINT1 ("(%d)", idx);
+ }
+ else if (name == Mshow)
+ ic->candidate_show = 1;
+ else if (name == Mhide)
+ ic->candidate_show = 0;
+ else if (name == Mdelete)
+ {
+ int len = mtext_nchars (ic->preedit);
+ int pos;
+ int to;
+
+ if (MPLIST_SYMBOL_P (args)
+ && (pos = surrounding_pos (MPLIST_SYMBOL (args))) != 0)
+ {
+ to = ic->cursor_pos + pos;
+ if (to < 0)
+ {
+ delete_surrounding_text (ic, to);
+ to = 0;
+ }
+ else if (to > len)
+ {
+ delete_surrounding_text (ic, to - len);
+ to = len;
+ }
+ }
+ else
+ {
+ to = (MPLIST_SYMBOL_P (args)
+ ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),
+ ic->preedit)
+ : MPLIST_INTEGER (args));
+ if (to < 0)
+ to = 0;
+ else if (to > len)
+ to = len;
+ }
+ MDEBUG_PRINT1 ("(%d)", to - ic->cursor_pos);
+ if (to < ic->cursor_pos)
+ preedit_delete (ic, to, ic->cursor_pos);
+ else if (to > ic->cursor_pos)
+ preedit_delete (ic, ic->cursor_pos, to);
+ }
+ else if (name == Mmove)
+ {
+ int len = mtext_nchars (ic->preedit);
+ int pos
+ = (MPLIST_SYMBOL_P (args)
+ ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),
+ ic->preedit)
+ : MPLIST_INTEGER (args));
+
+ if (pos < 0)
+ pos = 0;
+ else if (pos > len)
+ pos = len;
+ if (pos != ic->cursor_pos)
+ {
+ ic->cursor_pos = pos;
+ ic->preedit_changed = 1;
+ }
+ MDEBUG_PRINT1 ("(%d)", ic->cursor_pos);
+ }
+ else if (name == Mmark)
+ {
+ int code = marker_code (MPLIST_SYMBOL (args), 0);
+
+ if (code < 0)
+ {
+ mplist_put (ic_info->markers, MPLIST_SYMBOL (args),
+ (void *) ic->cursor_pos);
+ MDEBUG_PRINT1 ("(%d)", ic->cursor_pos);
+ }
+ }
+ else if (name == Mpushback)
+ {
+ if (MPLIST_INTEGER_P (args) || MPLIST_SYMBOL_P (args))
+ {
+ int num;
+
+ if (MPLIST_SYMBOL_P (args))
+ {
+ args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
+ if (MPLIST_INTEGER_P (args))
+ num = MPLIST_INTEGER (args);
+ else
+ num = 0;
+ }
+ else
+ num = MPLIST_INTEGER (args);
+
+ if (num > 0)
+ ic_info->key_head -= num;
+ else if (num == 0)
+ ic_info->key_head = 0;
+ else
+ ic_info->key_head = - num;
+ if (ic_info->key_head > ic_info->used)
+ ic_info->key_head = ic_info->used;
+ }
+ else if (MPLIST_MTEXT_P (args))
+ {
+ MText *mt = MPLIST_MTEXT (args);
+ int i, len = mtext_nchars (mt);
+ MSymbol key;
+
+ ic_info->key_head--;
+ for (i = 0; i < len; i++)
+ {
+ key = one_char_symbol[MTEXT_DATA (mt)[i]];
+ if (ic_info->key_head + i < ic_info->used)
+ ic_info->keys[ic_info->key_head + i] = key;
+ else
+ MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
+ }
+ }
+ else
+ {
+ MPlist *plist = MPLIST_PLIST (args), *pl;
+ int i = 0;
+ MSymbol key;
+
+ ic_info->key_head--;
+
+ MPLIST_DO (pl, plist)
+ {
+ key = MPLIST_SYMBOL (pl);
+ if (ic_info->key_head < ic_info->used)
+ ic_info->keys[ic_info->key_head + i] = key;
+ else
+ MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
+ i++;
+ }
+ }
+ }
+ else if (name == Mpop)
+ {
+ if (ic_info->key_head < ic_info->used)
+ MLIST_DELETE1 (ic_info, keys, ic_info->key_head, 1);
+ }
+ else if (name == Mcall)
+ {
+ MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+ MIMExternalFunc func = NULL;
+ MSymbol module, func_name;
+ MPlist *func_args, *val;
+ int ret = 0;
+
+ module = MPLIST_SYMBOL (args);
+ args = MPLIST_NEXT (args);
+ func_name = MPLIST_SYMBOL (args);
+
+ if (im_info->externals)
+ {
+ MIMExternalModule *external
+ = (MIMExternalModule *) mplist_get (im_info->externals,
+ module);
+ if (external)
+ func = ((MIMExternalFunc)
+ mplist_get_func (external->func_list, func_name));
+ }
+ if (! func)
+ continue;
+ func_args = mplist ();
+ mplist_add (func_args, Mt, ic);
+ MPLIST_DO (args, MPLIST_NEXT (args))
+ {
+ int code;
+
+ if (MPLIST_KEY (args) == Msymbol
+ && MPLIST_KEY (args) != Mnil
+ && (code = marker_code (MPLIST_SYMBOL (args), 0)) >= 0)
+ {
+ code = new_index (ic, ic->cursor_pos,
+ mtext_nchars (ic->preedit),
+ MPLIST_SYMBOL (args), ic->preedit);
+ mplist_add (func_args, Minteger, (void *) code);
+ }
+ else
+ mplist_add (func_args, MPLIST_KEY (args), MPLIST_VAL (args));
+ }
+ val = (func) (func_args);
+ M17N_OBJECT_UNREF (func_args);
+ if (val && ! MPLIST_TAIL_P (val))
+ ret = take_action_list (ic, val);
+ M17N_OBJECT_UNREF (val);
+ if (ret < 0)
+ return ret;
+ }
+ else if (name == Mshift)
+ {
+ shift_state (ic, MPLIST_SYMBOL (args));
+ }
+ else if (name == Mundo)
+ {
+ int intarg = (MPLIST_TAIL_P (args)
+ ? ic_info->used - 2
+ : integer_value (ic, args, NULL, 0));
+
+ mtext_reset (ic->preedit);
+ mtext_reset (ic_info->preedit_saved);
+ mtext_reset (ic->produced);
+ M17N_OBJECT_UNREF (ic_info->vars);
+ ic_info->vars = mplist_copy (ic_info->vars_saved);
+ ic->cursor_pos = ic_info->state_pos = 0;
+ ic_info->state_key_head = ic_info->key_head
+ = ic_info->commit_key_head = 0;
+
+ shift_state (ic, Mnil);
+ if (intarg < 0)
+ {
+ if (MPLIST_TAIL_P (args))
+ {
+ ic_info->used = 0;
+ return -1;
+ }
+ ic_info->used += intarg;
+ }
+ else
+ ic_info->used = intarg;
+ break;
+ }
+ else if (name == Mset || name == Madd || name == Msub
+ || name == Mmul || name == Mdiv)
+ {
+ MSymbol sym = MPLIST_SYMBOL (args);
+ int val1, val2;
+ MPlist *value;
+ char *op;
+
+ val1 = integer_value (ic, args, &value, 0);
+ args = MPLIST_NEXT (args);
+ val2 = resolve_expression (ic, args);
+ if (name == Mset)
+ val1 = val2, op = "=";
+ else if (name == Madd)
+ val1 += val2, op = "+=";
+ else if (name == Msub)
+ val1 -= val2, op = "-=";
+ else if (name == Mmul)
+ val1 *= val2, op = "*=";
+ else
+ val1 /= val2, op = "/=";
+ MDEBUG_PRINT4 ("(%s %s 0x%X(%d))",
+ MSYMBOL_NAME (sym), op, val1, val1);
+ if (value)
+ mplist_set (value, Minteger, (void *) val1);
+ }
+ else if (name == Mequal || name == Mless || name == Mgreater
+ || name == Mless_equal || name == Mgreater_equal)
+ {
+ int val1, val2;
+ MPlist *actions1, *actions2;
+ int ret = 0;
+
+ val1 = resolve_expression (ic, args);
+ args = MPLIST_NEXT (args);
+ val2 = resolve_expression (ic, args);
+ args = MPLIST_NEXT (args);
+ actions1 = MPLIST_PLIST (args);
+ args = MPLIST_NEXT (args);
+ if (MPLIST_TAIL_P (args))
+ actions2 = NULL;
+ else
+ actions2 = MPLIST_PLIST (args);
+ MDEBUG_PRINT3 ("(%d %s %d)? ", val1, MSYMBOL_NAME (name), val2);
+ if (name == Mequal ? val1 == val2
+ : name == Mless ? val1 < val2
+ : name == Mgreater ? val1 > val2
+ : name == Mless_equal ? val1 <= val2
+ : val1 >= val2)
+ {
+ MDEBUG_PRINT ("ok");
+ ret = take_action_list (ic, actions1);
+ }
+ else
+ {
+ MDEBUG_PRINT ("no");
+ if (actions2)
+ ret = take_action_list (ic, actions2);
+ }
+ if (ret < 0)
+ return ret;
+ }
+ else if (name == Mcond)
+ {
+ int idx = 0;
+
+ MPLIST_DO (args, args)
+ {
+ MPlist *cond;
+
+ idx++;
+ if (! MPLIST_PLIST (args))
+ continue;
+ cond = MPLIST_PLIST (args);
+ if (resolve_expression (ic, cond) != 0)
+ {
+ MDEBUG_PRINT1 ("(%dth)", idx);
+ if (take_action_list (ic, MPLIST_NEXT (cond)) < 0)
+ return -1;;
+ break;
+ }
+ }
+ }
+ else if (name == Mcommit)
+ {
+ preedit_commit (ic);
+ }
+ else if (name == Munhandle)
+ {
+ preedit_commit (ic);
+ return -1;
+ }
+ else
+ {
+ MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+ MPlist *actions;
+
+ if (im_info->macros
+ && (actions = mplist_get (im_info->macros, name)))
+ {
+ if (take_action_list (ic, actions) < 0)
+ return -1;
+ };
+ }
+ }
+
+ if (ic->candidate_list)
+ {
+ M17N_OBJECT_UNREF (ic->candidate_list);
+ ic->candidate_list = NULL;
+ }
+ if (ic->cursor_pos > 0
+ && (prop = mtext_get_property (ic->preedit, ic->cursor_pos - 1,
+ Mcandidate_list)))
+ {
+ ic->candidate_list = mtext_property_value (prop);
+ M17N_OBJECT_REF (ic->candidate_list);
+ ic->candidate_index
+ = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1,
+ Mcandidate_index);
+ ic->candidate_from = mtext_property_start (prop);
+ ic->candidate_to = mtext_property_end (prop);
+ }
+
+ if (candidate_list != ic->candidate_list)
+ ic->candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
+ if (candidate_index != ic->candidate_index)
+ ic->candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
+ if (candidate_show != ic->candidate_show)
+ ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
+ return 0;
+}
+
+
+/* Handle the input key KEY in the current state and map specified in
+ the input context IC. If KEY is handled correctly, return 0.
+ Otherwise, return -1. */
+
+static int
+handle_key (MInputContext *ic)
+{
+ MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+ MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+ MIMMap *map = ic_info->map;
+ MIMMap *submap = NULL;
+ MSymbol key = ic_info->keys[ic_info->key_head];
+ MSymbol alias = Mnil;
+ int i;
+
+ MDEBUG_PRINT2 (" [IM] handle `%s' in state %s",
+ msymbol_name (key), MSYMBOL_NAME (ic_info->state->name));
+
+ if (map->submaps)
+ {
+ submap = mplist_get (map->submaps, key);
+ alias = key;
+ while (! submap
+ && (alias = msymbol_get (alias, M_key_alias))
+ && alias != key)
+ submap = mplist_get (map->submaps, alias);
+ }
+
+ if (submap)
+ {
+ if (! alias || alias == key)
+ MDEBUG_PRINT (" submap-found");
+ else
+ MDEBUG_PRINT1 (" submap-found (by alias `%s')", MSYMBOL_NAME (alias));
+ mtext_cpy (ic->preedit, ic_info->preedit_saved);
+ ic->preedit_changed = 1;
+ ic->cursor_pos = ic_info->state_pos;
+ ic_info->key_head++;
+ ic_info->map = map = submap;
+ if (map->map_actions)
+ {
+ MDEBUG_PRINT (" map-actions:");
+ if (take_action_list (ic, map->map_actions) < 0)
+ {
+ MDEBUG_PRINT ("\n");
+ return -1;
+ }
+ }
+ else if (map->submaps)
+ {
+ for (i = ic_info->state_key_head; i < ic_info->key_head; i++)
+ {
+ MSymbol key = ic_info->keys[i];
+ char *name = msymbol_name (key);
+
+ if (! name[0] || ! name[1])
+ mtext_ins_char (ic->preedit, ic->cursor_pos++, name[0], 1);
+ }
+ }
+
+ /* If this is the terminal map or we have shifted to another
+ state, perform branch actions (if any). */
+ if (! map->submaps || map != ic_info->map)
+ {
+ if (map->branch_actions)
+ {
+ MDEBUG_PRINT (" branch-actions:");
+ if (take_action_list (ic, map->branch_actions) < 0)
+ {
+ MDEBUG_PRINT ("\n");
+ return -1;
+ }
+ }
+ /* If MAP is still not the root map, shift to the current
+ state. */
+ if (ic_info->map != ic_info->state->map)
+ shift_state (ic, ic_info->state->name);
+ }
+ }
+ else
+ {
+ /* MAP can not handle KEY. */
+
+ /* Perform branch actions if any. */
+ if (map->branch_actions)
+ {
+ MDEBUG_PRINT (" branch-actions:");
+ if (take_action_list (ic, map->branch_actions) < 0)
+ {
+ MDEBUG_PRINT ("\n");
+ return -1;
+ }
+ }
+
+ if (map == ic_info->map)
+ {
+ /* The above branch actions didn't change the state. */
+
+ /* If MAP is the root map of the initial state, and there
+ still exist an unhandled key, it means that the current
+ input method can not handle it. */
+ if (map == ((MIMState *) MPLIST_VAL (im_info->states))->map
+ && ic_info->key_head < ic_info->used)
+ {
+ MDEBUG_PRINT (" unhandled\n");
+ return -1;
+ }
+
+ if (map != ic_info->state->map)
+ {
+ /* MAP is not the root map. Shift to the root map of the
+ current state. */
+ shift_state (ic, ic_info->state->name);
+ }
+ else if (! map->branch_actions)
+ {
+ /* MAP is the root map without any default branch
+ actions. Shift to the initial state. */
+ shift_state (ic, Mnil);
+ }
+ }
+ }
+ MDEBUG_PRINT ("\n");
+ return 0;
+}
+
+/* Initialize IC->ic_info. */
+
+static void
+init_ic_info (MInputContext *ic)
+{
+ MInputMethodInfo *im_info = ic->im->info;
+ MInputContextInfo *ic_info = ic->info;
+ MPlist *plist;
+
+ MLIST_INIT1 (ic_info, keys, 8);;
+
+ ic_info->markers = mplist ();
+
+ ic_info->vars = mplist ();
+ if (im_info->configured_vars)
+ MPLIST_DO (plist, im_info->configured_vars)
+ {
+ MPlist *pl = MPLIST_PLIST (plist);
+ MSymbol name = MPLIST_SYMBOL (pl);
+
+ pl = MPLIST_NEXT (MPLIST_NEXT (MPLIST_NEXT (pl)));
+ if (MPLIST_KEY (pl) != Mt)
+ {
+ MPlist *p = mplist ();
+
+ mplist_push (ic_info->vars, Mplist, p);
+ M17N_OBJECT_UNREF (p);
+ mplist_add (p, Msymbol, name);
+ mplist_add (p, MPLIST_KEY (pl), MPLIST_VAL (pl));
+ }
+ }
+ ic_info->vars_saved = mplist_copy (ic_info->vars);
+
+ if (im_info->externals)
+ {
+ MPlist *func_args = mplist (), *plist;
+
+ mplist_add (func_args, Mt, ic);
+ MPLIST_DO (plist, im_info->externals)
+ {
+ MIMExternalModule *external = MPLIST_VAL (plist);
+ MIMExternalFunc func
+ = (MIMExternalFunc) mplist_get_func (external->func_list, Minit);
+
+ if (func)
+ (func) (func_args);
+ }
+ M17N_OBJECT_UNREF (func_args);
+ }
+
+ ic_info->preedit_saved = mtext ();
+ ic_info->tick = im_info->tick;
+}
+
+/* Finalize IC->ic_info. */
+
+static void
+fini_ic_info (MInputContext *ic)
+{
+ MInputMethodInfo *im_info = ic->im->info;
+ MInputContextInfo *ic_info = ic->info;
+
+ if (im_info->externals)
+ {
+ MPlist *func_args = mplist (), *plist;
+
+ mplist_add (func_args, Mt, ic);
+ MPLIST_DO (plist, im_info->externals)
+ {
+ MIMExternalModule *external = MPLIST_VAL (plist);
+ MIMExternalFunc func
+ = (MIMExternalFunc) mplist_get_func (external->func_list, Mfini);
+
+ if (func)
+ (func) (func_args);
+ }
+ M17N_OBJECT_UNREF (func_args);
+ }
+
+ MLIST_FREE1 (ic_info, keys);
+ M17N_OBJECT_UNREF (ic_info->preedit_saved);
+ M17N_OBJECT_UNREF (ic_info->markers);
+ M17N_OBJECT_UNREF (ic_info->vars);
+ M17N_OBJECT_UNREF (ic_info->vars_saved);
+ M17N_OBJECT_UNREF (ic_info->preceding_text);
+ M17N_OBJECT_UNREF (ic_info->following_text);
+
+ memset (ic_info, 0, sizeof (MInputContextInfo));
+}
+
+static void
+re_init_ic (MInputContext *ic, int reload)
+{
+ MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+ MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+ int status_changed, preedit_changed, cursor_pos_changed, candidates_changed;
+
+ status_changed = ic_info->state != (MIMState *) MPLIST_VAL (im_info->states);
+ preedit_changed = mtext_nchars (ic->preedit) > 0;
+ cursor_pos_changed = ic->cursor_pos > 0;
+ candidates_changed = 0;
+ if (ic->candidate_list)
+ {
+ candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
+ M17N_OBJECT_UNREF (ic->candidate_list);
+ ic->candidate_list = NULL;
+ }
+ if (ic->candidate_show)
+ {
+ candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
+ ic->candidate_show = 0;
+ }
+ if (ic->candidate_index > 0)
+ {
+ candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
+ ic->candidate_index = 0;
+ ic->candidate_from = ic->candidate_to = 0;
+ }
+ if (mtext_nchars (ic->produced) > 0)
+ mtext_reset (ic->produced);
+ if (mtext_nchars (ic->preedit) > 0)
+ mtext_reset (ic->preedit);
+ ic->cursor_pos = 0;
+ M17N_OBJECT_UNREF (ic->plist);
+ ic->plist = mplist ();
+
+ fini_ic_info (ic);
+ if (reload)
+ reload_im_info (im_info);
+ init_ic_info (ic);
+ shift_state (ic, Mnil);
+ ic->status_changed = status_changed;
+ ic->preedit_changed = preedit_changed;
+ ic->cursor_pos_changed = cursor_pos_changed;
+ ic->candidates_changed = candidates_changed;
+}
+
+static void
+reset_ic (MInputContext *ic, MSymbol ignore)
+{
+ MDEBUG_PRINT ("\n [IM] reset\n");
+ re_init_ic (ic, 0);
+}
+
+static int
+open_im (MInputMethod *im)
+{
+ MInputMethodInfo *im_info = get_im_info (im->language, im->name, Mnil, Mnil);
+
+ if (! im_info)
+ MERROR (MERROR_IM, -1);
+ im->info = im_info;
+
+ return 0;
+}
+
+static void
+close_im (MInputMethod *im)
+{
+ im->info = NULL;
+}
+
+static int
+create_ic (MInputContext *ic)
+{
+ MInputContextInfo *ic_info;
+
+ MSTRUCT_CALLOC (ic_info, MERROR_IM);
+ ic->info = ic_info;
+ init_ic_info (ic);
+ shift_state (ic, Mnil);
+ return 0;
+}
+
+static void
+destroy_ic (MInputContext *ic)
+{
+ fini_ic_info (ic);
+ free (ic->info);
+}
+
+static int
+check_reload (MInputContext *ic, MSymbol key)
+{
+ MInputMethodInfo *im_info = ic->im->info;
+ MPlist *plist = resolve_command (im_info->configured_cmds, Mat_reload);
+
+ if (! plist)
+ {
+ plist = resolve_command (global_info->configured_cmds, Mat_reload);
+ if (! plist)
+ return 0;
+ }
+ MPLIST_DO (plist, plist)
+ {
+ MSymbol this_key, alias;
+
+ if (MPLIST_MTEXT_P (plist))
+ {
+ MText *mt = MPLIST_MTEXT (plist);
+ int c = mtext_ref_char (mt, 0);
+
+ if (c >= 256)
+ continue;
+ this_key = one_char_symbol[c];
+ }
+ else
+ {
+ MPlist *pl = MPLIST_PLIST (plist);
+
+ this_key = MPLIST_SYMBOL (pl);
+ }
+ alias = this_key;
+ while (alias != key
+ && (alias = msymbol_get (alias, M_key_alias))
+ && alias != this_key);
+ if (alias == key)
+ break;
+ }
+ if (MPLIST_TAIL_P (plist))
+ return 0;
+
+ MDEBUG_PRINT ("\n [IM] reload");
+ re_init_ic (ic, 1);
+ return 1;
+}
+
+
+/** Handle the input key KEY in the current state and map of IC->info.
+ If KEY is handled but no text is produced, return 0, otherwise
+ return 1.
+
+ Ignore ARG. */
+
+static int
+filter (MInputContext *ic, MSymbol key, void *arg)
+{
+ MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+ MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+ int i = 0;
+
+ if (check_reload (ic, key))
+ return 0;
+
+ if (! ic_info->state)
+ {
+ ic_info->key_unhandled = 1;
+ return 0;
+ }
+ mtext_reset (ic->produced);
+ ic->status_changed = ic->preedit_changed = ic->candidates_changed = 0;
+ M17N_OBJECT_UNREF (ic_info->preceding_text);
+ M17N_OBJECT_UNREF (ic_info->following_text);
+ ic_info->preceding_text = ic_info->following_text = NULL;
+ MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
+ ic_info->key_unhandled = 0;
+
+ do {
+ if (handle_key (ic) < 0)
+ {
+ /* KEY was not handled. Delete it from the current key sequence. */
+ if (ic_info->used > 0)
+ {
+ memmove (ic_info->keys, ic_info->keys + 1,
+ sizeof (int) * (ic_info->used - 1));
+ ic_info->used--;
+ if (ic_info->state_key_head > 0)
+ ic_info->state_key_head--;
+ if (ic_info->commit_key_head > 0)
+ ic_info->commit_key_head--;
+ }
+ /* This forces returning 1. */
+ ic_info->key_unhandled = 1;
+ break;
+ }
+ if (i++ == 100)
+ {
+ mdebug_hook ();
+ reset_ic (ic, Mnil);
+ ic_info->key_unhandled = 1;
+ break;
+ }
+ /* Break the loop if all keys were handled. */
+ } while (ic_info->key_head < ic_info->used);
+
+ /* If the current map is the root of the initial state, we should
+ produce any preedit text in ic->produced. */
+ if (ic_info->map == ((MIMState *) MPLIST_VAL (im_info->states))->map)
+ preedit_commit (ic);
+
+ if (mtext_nchars (ic->produced) > 0)
+ {
+ MSymbol lang = msymbol_get (ic->im->language, Mlanguage);
+
+ if (mdebug__flag & mdebug_mask)
+ {
+ MDEBUG_PRINT (" (produced");
+ for (i = 0; i < mtext_nchars (ic->produced); i++)
+ MDEBUG_PRINT1 (" U+%04X", mtext_ref_char (ic->produced, i));
+ MDEBUG_PRINT (")");
+ }
+
+ if (lang != Mnil)
+ mtext_put_prop (ic->produced, 0, mtext_nchars (ic->produced),
+ Mlanguage, ic->im->language);
+ }
+ if (ic_info->commit_key_head > 0)
+ {
+ memmove (ic_info->keys, ic_info->keys + ic_info->commit_key_head,
+ sizeof (int) * (ic_info->used - ic_info->commit_key_head));
+ ic_info->used -= ic_info->commit_key_head;
+ ic_info->key_head -= ic_info->commit_key_head;
+ ic_info->state_key_head -= ic_info->commit_key_head;
+ ic_info->commit_key_head = 0;
+ }
+ if (ic_info->key_unhandled)
+ {
+ ic_info->used = 0;
+ ic_info->key_head = ic_info->state_key_head
+ = ic_info->commit_key_head = 0;
+ }
+
+ return (! ic_info->key_unhandled && mtext_nchars (ic->produced) == 0);
+}
+
+
+/** Return 1 if the last event or key was not handled, otherwise
+ return 0.
+
+ There is no need of looking up because ic->produced should already
+ contain the produced text (if any).
+
+ Ignore KEY. */
+
+static int
+lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt)
+{
+ mtext_cat (mt, ic->produced);
+ mtext_reset (ic->produced);
+ return (((MInputContextInfo *) ic->info)->key_unhandled ? -1 : 0);
+}
+
+\f
+/* Input method command handler. */
+
+/* List of all (global and local) commands.
+ (LANG:(IM-NAME:(COMMAND ...) ...) ...) ...
+ COMMAND is CMD-NAME:(mtext:DESCRIPTION plist:KEYSEQ ...))
+ Global commands are stored as (t (t COMMAND ...)) */
+
+\f
+/* Input method variable handler. */
+
+
+/* Support functions for mdebug_dump_im. */
+
+static void
+dump_im_map (MPlist *map_list, int indent)
+{
+ char *prefix;
+ MSymbol key = MPLIST_KEY (map_list);
+ MIMMap *map = (MIMMap *) MPLIST_VAL (map_list);
+
+ prefix = (char *) alloca (indent + 1);
+ memset (prefix, 32, indent);
+ prefix[indent] = '\0';
+
+ fprintf (stderr, "(\"%s\" ", msymbol_name (key));
+ if (map->map_actions)
+ mdebug_dump_plist (map->map_actions, indent + 2);
+ if (map->submaps)
+ {
+ MPLIST_DO (map_list, map->submaps)
+ {
+ fprintf (stderr, "\n%s ", prefix);
+ dump_im_map (map_list, indent + 2);
+ }
+ }
+ if (map->branch_actions)
+ {
+ fprintf (stderr, "\n%s (branch\n%s ", prefix, prefix);
+ mdebug_dump_plist (map->branch_actions, indent + 4);
+ fprintf (stderr, ")");
+ }
+ fprintf (stderr, ")");
+}
+
+
+static void
+dump_im_state (MIMState *state, int indent)
+{
+ char *prefix;
+ MPlist *map_list;
+
+ prefix = (char *) alloca (indent + 1);
+ memset (prefix, 32, indent);
+ prefix[indent] = '\0';
+
+ fprintf (stderr, "(%s", msymbol_name (state->name));
+ if (state->map->submaps)
+ {
+ MPLIST_DO (map_list, state->map->submaps)
+ {
+ fprintf (stderr, "\n%s ", prefix);
+ dump_im_map (map_list, indent + 2);
+ }
+ }
+ fprintf (stderr, ")");
+}
+
+\f
+
+int
+minput__init ()
+{
+ Minput_driver = msymbol ("input-driver");
+
+ Minput_preedit_start = msymbol ("input-preedit-start");
+ Minput_preedit_done = msymbol ("input-preedit-done");
+ Minput_preedit_draw = msymbol ("input-preedit-draw");
+ Minput_status_start = msymbol ("input-status-start");
+ Minput_status_done = msymbol ("input-status-done");
+ Minput_status_draw = msymbol ("input-status-draw");
+ Minput_candidates_start = msymbol ("input-candidates-start");
+ Minput_candidates_done = msymbol ("input-candidates-done");
+ Minput_candidates_draw = msymbol ("input-candidates-draw");
+ Minput_set_spot = msymbol ("input-set-spot");
+ Minput_focus_move = msymbol ("input-focus-move");
+ Minput_focus_in = msymbol ("input-focus-in");
+ Minput_focus_out = msymbol ("input-focus-out");
+ Minput_toggle = msymbol ("input-toggle");
+ Minput_reset = msymbol ("input-reset");
+ Minput_get_surrounding_text = msymbol ("input-get-surrounding-text");
+ Minput_delete_surrounding_text = msymbol ("input-delete-surrounding-text");
+ Mcustomized = msymbol ("customized");
+ Mconfigured = msymbol ("configured");
+ Minherited = msymbol ("inherited");
+
+ minput_default_driver.open_im = open_im;
+ minput_default_driver.close_im = close_im;
+ minput_default_driver.create_ic = create_ic;
+ minput_default_driver.destroy_ic = destroy_ic;
+ minput_default_driver.filter = filter;
+ minput_default_driver.lookup = lookup;
+ minput_default_driver.callback_list = mplist ();
+ mplist_put_func (minput_default_driver.callback_list, Minput_reset,
+ M17N_FUNC (reset_ic));
+ minput_driver = &minput_default_driver;
+
+ fully_initialized = 0;
+ return 0;
+}
+
+void
+minput__fini ()
+{
+ if (fully_initialized)
+ {
+ free_im_list (im_info_list);
+ if (im_custom_list)
+ free_im_list (im_custom_list);
+ if (im_config_list)
+ free_im_list (im_config_list);
+ M17N_OBJECT_UNREF (load_im_info_keys);
+ }
+
+ M17N_OBJECT_UNREF (minput_default_driver.callback_list);
+ M17N_OBJECT_UNREF (minput_driver->callback_list);
+
+}
+
+MSymbol
+minput__char_to_key (int c)
+{
+ if (c < 0 || c >= 0x100)
+ return Mnil;
+
+ return one_char_symbol[c];
+}
+
+/*** @} */
+#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
+
+\f
+/* External API */
+
+/*** @addtogroup m17nInputMethod */
+/*** @{ */
+/*=*/
+
+/***en
+ @name Variables: Predefined symbols for callback commands.
+
+ These are the predefined symbols that are used as the @c COMMAND
+ argument of callback functions of an input method driver (see
+ #MInputDriver::callback_list).
+
+ Most of them do not require extra argument nor return any value;
+ exceptions are these:
+
+ Minput_get_surrounding_text: When a callback function assigned for
+ this command is called, the first element of #MInputContext::plist
+ has key #Minteger and the value specifies which portion of the
+ surrounding text should be retrieved. If the value is positive,
+ it specifies the number of characters following the current cursor
+ position. If the value is negative, the absolute value specifies
+ the number of characters preceding the current cursor position.
+ If the value is zero, it means that the caller just wants to know
+ if the surrounding text is currently supported or not.
+
+ If the surrounding text is currently supported, the callback
+ function must set the key of this element to #Mtext and the value
+ to the retrieved M-text. The length of the M-text may be shorter
+ than the requested number of characters, if the available text is
+ not that long. The length can be zero in the worst case. Or, the
+ length may be longer if an application thinks it is more efficient
+ to return that length.
+
+ If the surrounding text is not currently supported, the callback
+ function should return without changing the first element of
+ #MInputContext::plist.
+
+ Minput_delete_surrounding_text: When a callback function assigned
+ for this command is called, the first element of
+ #MInputContext::plist has key #Minteger and the value specifies
+ which portion of the surrounding text should be deleted in the
+ same way as the case of Minput_get_surrounding_text. The callback
+ function must delete the specified text. It should not alter
+ #MInputContext::plist. */
+/***ja
+ @name ÊÑ¿ô¡§ ¥³¡¼¥ë¥Ð¥Ã¥¯¥³¥Þ¥ó¥ÉÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.
+
+ ÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Î¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Ë¤ª¤¤¤Æ @c COMMAND
+ °ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë (#MInputDriver::callback_list »²¾È)¡£
+
+ ¤Û¤È¤ó¤É¤ÏÄɲäΰú¿ô¤òɬÍפȤ·¤Ê¤¤¤·ÃͤòÊÖ¤µ¤Ê¤¤¤¬¡¢°Ê²¼¤ÏÎã³°¤Ç¤¢¤ë¡£
+
+ Minput_get_surrounding_text: ¤³¤Î¥³¥Þ¥ó¥É¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥³¡¼¥ë¥Ð¥Ã
+ ¥¯´Ø¿ô¤¬¸Æ¤Ð¤ì¤¿ºÝ¤Ë¤Ï¡¢ #MInputContext::plist ¤ÎÂè°ìÍ×ÁǤϥ¡¼¤È¤·
+ ¤Æ#Minteger ¤ò¤È¤ê¡¢¤½¤ÎÃͤϥµ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥¥¹¥È¤Î¤¦¤Á¤É¤ÎÉôʬ
+ ¤ò¼è¤Ã¤ÆÍè¤ë¤«¤ò»ØÄꤹ¤ë¡£Ãͤ¬Àµ¤Ç¤¢¤ì¤Ð¡¢¸½ºß¤Î¥«¡¼¥½¥ë°ÌÃ֤˳¤¯
+ ÃͤθĿôʬ¤Îʸ»ú¤ò¼è¤ë¡£Éé¤Ç¤¢¤ì¤Ð¡¢¥«¡¼¥½¥ë°ÌÃÖ¤ËÀè¹Ô¤¹¤ëÃͤÎÀäÂÐ
+ ÃÍʬ¤Îʸ»ú¤ò¼è¤ë¡£¸½ºß¥µ¥é¥¦¥ó¥É¥Æ¥¥¹¥È¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë¤«¤É¤¦
+ ¤«¤òÃΤꤿ¤¤¤À¤±¤Ç¤¢¤ì¤Ð¡¢¤³¤ÎÃͤϥ¼¥í¤Ç¤âÎɤ¤¡£
+
+ ¥µ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥¥¹¥È¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Ï
+ ¤³¤ÎÍ×ÁǤΥ¡¼¤ò #Mtext ¤Ë¡¢Ãͤò¼è¤ê¹þ¤ó¤ÀM-text ¤ËÀßÄꤷ¤Ê¤¯¤Æ¤Ï¤Ê
+ ¤é¤Ê¤¤¡£¤â¤·¥Æ¥¥¹¥È¤ÎŤµ¤¬½¼Ê¬¤Ç¤Ê¤±¤ì¤Ð¡¢¤³¤Î M-text ¤ÎŤµ¤ÏÍ×
+ µá¤µ¤ì¤Æ¤¤¤ëʸ»ú¿ô¤è¤êû¤¯¤ÆÎɤ¤¡£ºÇ°¤Î¾ì¹ç 0 ¤Ç¤â¤è¤¤¤·¡¢¥¢¥×¥ê¥±¡¼
+ ¥·¥ç¥ó¦¤ÇɬÍפǸúΨŪ¤À¤È»×¤¨¤ÐŤ¯¤Æ¤âÎɤ¤¡£
+
+ ¥µ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥¥¹¥È¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¡¢¥³¡¼¥ë¥Ð¥Ã¥¯´Ø
+ ¿ô¤Ï #MInputContext::plist ¤ÎÂè°ìÍ×ÁǤòÊѹ¹¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
+
+ Minput_delete_surrounding_text: ¤³¤Î¥³¥Þ¥ó¥É¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥³¡¼¥ë
+ ¥Ð¥Ã¥¯´Ø¿ô¤¬¸Æ¤Ð¤ì¤¿ºÝ¤Ë¤Ï¡¢#MInputContext::plist ¤ÎÂè°ìÍ×ÁǤϡ¢¥¡¼
+ ¤È¤·¤Æ#Minteger ¤ò¤È¤ê¡¢ÃͤϺï½ü¤¹¤ë¤Ù¤¥µ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥¥¹¥È¤ò
+ Minput_get_surrounding_text ¤ÈƱÍͤΤä¤êÊý¤Ç»ØÄꤹ¤ë¡£¥³¡¼¥ë¥Ð¥Ã¥¯
+ ´Ø¿ô¤Ï»ØÄꤵ¤ì¤¿¥Æ¥¥¹¥È¤òºï½ü¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤Þ¤¿
+ #MInputContext::plist ¤òÊѤ¨¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ */
+/*** @{ */
+/*=*/
+
+MSymbol Minput_preedit_start;
+MSymbol Minput_preedit_done;
+MSymbol Minput_preedit_draw;
+MSymbol Minput_status_start;
+MSymbol Minput_status_done;
+MSymbol Minput_status_draw;
+MSymbol Minput_candidates_start;
+MSymbol Minput_candidates_done;
+MSymbol Minput_candidates_draw;
+MSymbol Minput_set_spot;
+MSymbol Minput_toggle;
+MSymbol Minput_reset;
+MSymbol Minput_get_surrounding_text;
+MSymbol Minput_delete_surrounding_text;
+/*** @} */
+
+/*=*/
+
+/***en
+ @name Variables: Predefined symbols for special input events.
+
+ These are the predefined symbols that are used as the @c KEY
+ argument of minput_filter (). */
+/***ja
+ @name ÊÑ¿ô: ÆÃÊ̤ÊÆþÎÏ¥¤¥Ù¥ó¥ÈÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.
+
+ minput_filter () ¤Î @c KEY °ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë¡£ */
+
+/*** @{ */
+/*=*/
+
+MSymbol Minput_focus_out;
+MSymbol Minput_focus_in;
+MSymbol Minput_focus_move;
+
+/*** @} */
+
+/*=*/
+/***en
+ @name Variables: Predefined symbols used in input method information.
+
+ These are the predefined symbols describing status of input method
+ command and variable, and are used in a return value of
+ minput_get_command () and minput_get_variable (). */
+/***ja
+ @name ÊÑ¿ô: ÆþÎϥ᥽¥Ã¥É¾ðÊóÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.
+
+ ÆþÎϥ᥽¥Ã¥É¤Î¥³¥Þ¥ó¥É¤äÊÑ¿ô¤Î¾õÂÖ¤òɽ¤·¡¢minput_get_command () ¤È
+ minput_get_variable () ¤ÎÌá¤êÃͤȤ·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë¡£ */
+/*** @{ */
+/*=*/
+MSymbol Minherited;
+MSymbol Mcustomized;
+MSymbol Mconfigured;
+/*** @} */
+
+/*=*/
+
+/***en
+ @brief The default driver for internal input methods.
+
+ The variable #minput_default_driver is the default driver for
+ internal input methods.
+
+ The member MInputDriver::open_im () searches the m17n database for
+ an input method that matches the tag \< #Minput_method, $LANGUAGE,
+ $NAME\> and loads it.
+
+ The member MInputDriver::callback_list () is @c NULL. Thus, it is
+ programmers responsibility to set it to a plist of proper callback
+ functions. Otherwise, no feedback information (e.g. preedit text)
+ can be shown to users.
+
+ The macro M17N_INIT () sets the variable #minput_driver to the
+ pointer to this driver so that all internal input methods use it.
+
+ Therefore, unless @c minput_driver is set differently, the driver
+ dependent arguments $ARG of the functions whose name begins with
+ "minput_" are all ignored. */
+/***ja
+ @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥǥե©¥ë¥È¥É¥é¥¤¥Ð.
+
+ ÊÑ¿ô #minput_default_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤΥǥե©¥ë¥È¤Î¥É¥é¥¤¥Ð¤òɽ¤¹¡£
+
+ ¥á¥ó¥Ð MInputDriver::open_im () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é¥¿¥°
+ \< #Minput_method, $LANGUAGE, $NAME\>
+ ¤Ë¹çÃפ¹¤ëÆþÎϥ᥽¥Ã¥É¤òõ¤·¡¢¤½¤ì¤ò¥í¡¼¥É¤¹¤ë¡£
+
+ ¥á¥ó¥Ð MInputDriver::callback_list () ¤Ï @c NULL ¤Ç¤¢¤ê¡¢
+ ¤·¤¿¤¬¤Ã¤Æ¡¢¥×¥í¥°¥é¥Þ¦¤ÇÀÕǤ¤ò»ý¤Ã¤Æ ŬÀڤʥ³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Î plist
+ ¤ËÀßÄꤷ¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¤µ¤â¤Ê¤¤¤È¡¢preedit
+ ¥Æ¥¥¹¥È¤Ê¤É¤Î¥Õ¥£¡¼¥É¥Ð¥Ã¥¯¾ðÊ󤬥桼¥¶¤Ëɽ¼¨¤µ¤ì¤Ê¤¤¡£
+
+ ¥Þ¥¯¥í M17N_INIT () ¤ÏÊÑ¿ô #minput_driver
+ ¤ò¤³¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤ËÀßÄꤷ¡¢Á´¤Æ¤ÎÆâÉôÆþÎϥ᥽¥Ã¥É¤¬¤³¤Î¥É¥é¥¤¥Ð¤ò»È¤¦¤è¤¦¤Ë¤¹¤ë¡£
+
+ ¤·¤¿¤¬¤Ã¤Æ¡¢@c minput_driver ¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤΤޤޤǤ¢¤ì¤Ð¡¢minput_
+ ¤Ç»Ï¤Þ¤ë´Ø¿ô¤Î¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë°ú¿ô $ARG ¤Ï¤¹¤Ù¤Æ̵»ë¤µ¤ì¤ë¡£ */
+
+MInputDriver minput_default_driver;
+/*=*/
+
+/***en
+ @brief The driver for internal input methods.
+
+ The variable #minput_driver is a pointer to the input method
+ driver that is used by internal input methods. The macro
+ M17N_INIT () initializes it to a pointer to #minput_default_driver
+ if <m17n<EM></EM>.h> is included. */
+/***ja
+ @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥɥ饤¥Ð.
+
+ ÊÑ¿ô #minput_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤Æ»ÈÍѤµ¤ì¤Æ¤¤¤ëÆþÎÏ¥á
+ ¥½¥Ã¥É¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¥Þ¥¯¥í M17N_INIT () ¤Ï¤³¤Î¥Ý¥¤¥ó
+ ¥¿¤ò#minput_default_driver (<m17n<EM></EM>.h> ¤¬ include ¤µ¤ì¤Æ¤¤¤ë
+ »þ) ¤Ë½é´ü²½¤¹¤ë¡£ */
+
+MInputDriver *minput_driver;
+
+MSymbol Minput_driver;
+
+/*=*/
+
+/***en
+ @name Functions
+*/
+/***ja
+ @name ´Ø¿ô
+*/
+/*** @{ */
+
+/*=*/
+
+/***en
+ @brief Open an input method.
+
+ The minput_open_im () function opens an input method whose
+ language and name match $LANGUAGE and $NAME, and returns a pointer
+ to the input method object newly allocated.
+
+ This function at first decides a driver for the input method as
+ described below.
+
+ If $LANGUAGE is not #Mnil, the driver pointed by the variable
+ #minput_driver is used.
+
+ If $LANGUAGE is #Mnil and $NAME has the property #Minput_driver, the
+ driver pointed to by the property value is used to open the input
+ method. If $NAME has no such a property, @c NULL is returned.
+
+ Then, the member MInputDriver::open_im () of the driver is
+ called.
+
+ $ARG is set in the member @c arg of the structure MInputMethod so
+ that the driver can refer to it. */
+/***ja
+ @brief ÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë.
+
+ ´Ø¿ô minput_open_im () ¤Ï¸À¸ì $LANGUAGE ¤È̾Á° $NAME
+ ¤Ë¹çÃפ¹¤ëÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤·¡¢¿·¤¿¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ÆþÎϥ᥽¥Ã¥É¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
+
+ ¤³¤Î´Ø¿ô¤Ï¡¢¤Þ¤ºÆþÎϥ᥽¥Ã¥ÉÍѤΥɥ饤¥Ð¤ò°Ê²¼¤Î¤è¤¦¤Ë¤·¤Æ·èÄꤹ¤ë¡£
+
+ $LANGUAGE ¤¬ #Mnil ¤Ç¤Ê¤±¤ì¤Ð¡¢ÊÑ¿ô #minput_driver
+ ¤Ç»Ø¤µ¤ì¤Æ¤¤¤ë¥É¥é¥¤¥Ð¤òÍѤ¤¤ë¡£
+
+ $LANGUAGE ¤¬ #Mnil ¤Ç¤¢¤ê¡¢$NAME ¤¬ #Minput_driver
+ ¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¾ì¹ç¤Ë¤Ï¡¢¤½¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤǻؤµ¤ì¤Æ¤¤¤ëÆþÎϥɥ饤¥Ð¤òÍѤ¤¤ÆÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë¡£
+ $NAME ¤Ë¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬Ìµ¤«¤Ã¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£
+
+ ¼¡¤¤¤Ç¡¢¥É¥é¥¤¥Ð¤Î¥á¥ó¥Ð MInputDriver::open_im () ¤¬¸Æ¤Ð¤ì¤ë¡£
+
+ $ARG ¤Ï¹½Â¤ÂÎ MInputMethod ¤Î¥á¥ó¥Ð @c arg ¤ËÀßÄꤵ¤ì¡¢¥É¥é¥¤¥Ð¤«¤é»²¾È¤Ç¤¤ë¡£
+
+ @latexonly \IPAlabel{minput_open} @endlatexonly
+
+*/
+
+MInputMethod *
+minput_open_im (MSymbol language, MSymbol name, void *arg)
+{
+ MInputMethod *im;
+ MInputDriver *driver;
+
+ MINPUT__INIT ();
+
+ MDEBUG_PRINT2 (" [IM] opening (%s %s) ... ",
+ msymbol_name (language), msymbol_name (name));
+ if (language)
+ driver = minput_driver;
+ else
+ {
+ driver = (MInputDriver *) msymbol_get (name, Minput_driver);
+ if (! driver)
+ MERROR (MERROR_IM, NULL);
+ }
+
+ MSTRUCT_CALLOC (im, MERROR_IM);
+ im->language = language;
+ im->name = name;
+ im->arg = arg;
+ im->driver = *driver;
+ if ((*im->driver.open_im) (im) < 0)
+ {
+ MDEBUG_PRINT (" failed\n");
+ free (im);
+ return NULL;
+ }
+ MDEBUG_PRINT (" ok\n");
+ return im;
+}
+
+/*=*/
+
+/***en
+ @brief Close an input method.
+
+ The minput_close_im () function closes the input method $IM, which
+ must have been created by minput_open_im (). */
+
+/***ja
+ @brief ÆþÎϥ᥽¥Ã¥É¤ò¥¯¥í¡¼¥º¤¹¤ë.
+
+ ´Ø¿ô minput_close_im () ¤Ï¡¢ÆþÎϥ᥽¥Ã¥É $IM ¤ò¥¯¥í¡¼¥º¤¹¤ë¡£
+ ¤³¤ÎÆþÎϥ᥽¥Ã¥É $IM ¤Ï minput_open_im () ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ */
+
+void
+minput_close_im (MInputMethod *im)
+{
+ MDEBUG_PRINT2 (" [IM] closing (%s %s) ... ",
+ msymbol_name (im->name), msymbol_name (im->language));
+ (*im->driver.close_im) (im);
+ free (im);
+ MDEBUG_PRINT (" done\n");
+}
+
+/*=*/
+
+/***en
+ @brief Create an input context.
+
+ The minput_create_ic () function creates an input context object
+ associated with input method $IM, and calls callback functions
+ corresponding to #Minput_preedit_start, #Minput_status_start, and
+ #Minput_status_draw in this order.
+
+ @return
+ If an input context is successfully created, minput_create_ic ()
+ returns a pointer to it. Otherwise it returns @c NULL. */
+
+/***ja
+ @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÀ¸À®¤¹¤ë.
+
+ ´Ø¿ô minput_create_ic () ¤ÏÆþÎϥ᥽¥Ã¥É $IM
+ ¤ËÂбþ¤¹¤ëÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¡¢
+ #Minput_preedit_start, #Minput_status_start, #Minput_status_draw
+ ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¤³¤Î½ç¤Ë¸Æ¤Ö¡£
+
+ @return
+ ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤¬À¸À®¤µ¤ì¤¿¾ì¹ç¡¢minput_create_ic ()
+ ¤Ï¤½¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£
+ */