+ 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);