+ /* Loading a variable customization. */
+ p = MPLIST_NEXT (pl); /* P ::= (nil VALUE) */
+ if (MFAILP (! MPLIST_TAIL_P (p)))
+ continue;
+ p = MPLIST_NEXT (p); /* P ::= (VALUE) */
+ if (MFAILP (MPLIST_INTEGER_P (p) || MPLIST_SYMBOL_P (p)
+ || MPLIST_MTEXT_P (p)))
+ continue;
+ }
+ tail = mplist_add (tail, Mplist, pl);
+ }
+}
+
+static MPlist *
+config_variable (MPlist *plist, MPlist *global_vars, MPlist *custom_vars,
+ MPlist *config_vars)
+{
+ MPlist *global = NULL, *custom = NULL, *config = NULL;
+ MSymbol name = MPLIST_SYMBOL (plist);
+ MText *description = NULL;
+ MSymbol status;
+ MPlist *value, *valids;
+
+ if (global_vars)
+ {
+ global = mplist__assq (global_vars, name);
+ if (global)
+ global = MPLIST_NEXT (MPLIST_PLIST (global)); /* (DESC VALUE ...) */
+ }
+
+ plist = MPLIST_NEXT (plist);
+ if (MPLIST_MTEXT_P (plist))
+ description = MPLIST_MTEXT (plist);
+ else if (global && MPLIST_MTEXT (global))
+ description = MPLIST_MTEXT (global);
+ if (global)
+ global = MPLIST_NEXT (global); /* (VALUE VALIDS ...) */
+
+ if (MPLIST_TAIL_P (plist))
+ {
+ /* Inherit from global (if any). */
+ if (global)
+ {
+ value = global;
+ if (MPLIST_KEY (value) == Mt)
+ value = NULL;
+ valids = MPLIST_NEXT (global);
+ status = Minherited;
+ }
+ else
+ {
+ value = NULL;
+ valids = NULL;
+ status = Mnil;
+ plist = NULL;
+ }
+ }
+ else
+ {
+ value = plist = MPLIST_NEXT (plist);
+ valids = MPLIST_NEXT (value);
+ if (MPLIST_KEY (value) == Mt)
+ value = NULL;
+ if (! MPLIST_TAIL_P (valids))
+ global = NULL;
+ else if (global)
+ valids = MPLIST_NEXT (global);
+ status = Mnil;
+ }
+
+ if (config_vars && (config = mplist__assq (config_vars, name)))
+ {
+ config = MPLIST_NEXT (MPLIST_PLIST (config));
+ if (! MPLIST_TAIL_P (config))
+ {
+ value = MPLIST_NEXT (config);
+ if (MFAILP (check_variable_value (value, global ? global : plist)))
+ value = NULL;
+ status = Mconfigured;
+ }
+ }
+ else if (custom_vars && (custom = mplist__assq (custom_vars, name)))
+ {
+ custom = MPLIST_NEXT (MPLIST_PLIST (custom));
+ if (! MPLIST_TAIL_P (custom))
+ {
+ value = MPLIST_NEXT (custom);
+ if (MFAILP (check_variable_value (value, global ? global : plist)))
+ value = NULL;
+ status = Mcustomized;
+ }
+ }
+
+ plist = mplist ();
+ mplist_add (plist, Msymbol, name);
+ if (description)
+ mplist_add (plist, Mtext, description);
+ else
+ mplist_add (plist, Msymbol, Mnil);
+ mplist_add (plist, Msymbol, status);
+ if (value)
+ mplist_add (plist, MPLIST_KEY (value), MPLIST_VAL (value));
+ else
+ mplist_add (plist, Mt, NULL);
+ if (valids && ! MPLIST_TAIL_P (valids))
+ mplist__conc (plist, valids);
+ return plist;
+}
+
+/* Return a configured variable definition list based on
+ IM_INFO->vars. If a variable in it doesn't contain a value, try to
+ get it from global_info->vars. */
+
+static void
+config_all_variables (MInputMethodInfo *im_info)
+{
+ MPlist *global_vars, *custom_vars, *config_vars;
+ MInputMethodInfo *temp;
+ MPlist *tail, *plist;
+
+ M17N_OBJECT_UNREF (im_info->configured_vars);
+
+ if (MPLIST_TAIL_P (im_info->vars)
+ || ! im_info->mdb)
+ return;
+
+ global_vars = im_info != global_info ? global_info->vars : NULL;
+ custom_vars = ((temp = get_custom_info (im_info)) ? temp->vars : NULL);
+ config_vars = ((temp = get_config_info (im_info)) ? temp->vars : NULL);
+
+ im_info->configured_vars = tail = mplist ();
+ MPLIST_DO (plist, im_info->vars)
+ {
+ MPlist *pl = config_variable (MPLIST_PLIST (plist),
+ global_vars, custom_vars, config_vars);
+ if (pl)
+ tail = mplist_add (tail, Mplist, pl);
+ }
+}
+
+/* Load an input method (LANGUAGE NAME) from PLIST into IM_INFO.
+ CONFIG contains configuration information of the input method. */
+
+static void
+load_im_info (MPlist *plist, MInputMethodInfo *im_info)
+{
+ MPlist *pl, *p;
+
+ if (! im_info->cmds && (pl = mplist__assq (plist, Mcommand)))
+ {
+ load_commands (im_info, MPLIST_PLIST (pl));
+ config_all_commands (im_info);
+ pl = mplist_pop (pl);
+ M17N_OBJECT_UNREF (pl);
+ }
+
+ if (! im_info->vars && (pl = mplist__assq (plist, Mvariable)))
+ {
+ load_variables (im_info, MPLIST_PLIST (pl));
+ config_all_variables (im_info);
+ pl = mplist_pop (pl);
+ M17N_OBJECT_UNREF (pl);
+ }
+
+ MPLIST_DO (plist, plist)
+ if (MPLIST_PLIST_P (plist))
+ {
+ MPlist *elt = MPLIST_PLIST (plist);
+ MSymbol key;
+
+ if (MFAILP (MPLIST_SYMBOL_P (elt)))
+ continue;
+ key = MPLIST_SYMBOL (elt);
+ if (key == Mtitle)
+ {
+ if (im_info->title)
+ continue;
+ elt = MPLIST_NEXT (elt);
+ if (MFAILP (MPLIST_MTEXT_P (elt)))
+ continue;
+ im_info->title = MPLIST_MTEXT (elt);
+ M17N_OBJECT_REF (im_info->title);
+ }
+ else if (key == Mmap)
+ {
+ pl = mplist__from_alist (MPLIST_NEXT (elt));
+ if (MFAILP (pl))
+ continue;
+ if (! im_info->maps)
+ im_info->maps = pl;
+ else
+ {
+ mplist__conc (im_info->maps, pl);
+ M17N_OBJECT_UNREF (pl);
+ }
+ }
+ else if (key == Mmacro)
+ {
+ if (! im_info->macros)
+ im_info->macros = mplist ();
+ MPLIST_DO (elt, MPLIST_NEXT (elt))
+ {
+ if (MFAILP (MPLIST_PLIST_P (elt)))
+ continue;
+ load_macros (im_info, MPLIST_PLIST (elt));
+ }
+ }
+ else if (key == Mmodule)
+ {
+ if (! im_info->externals)
+ im_info->externals = mplist ();
+ MPLIST_DO (elt, MPLIST_NEXT (elt))
+ {
+ if (MFAILP (MPLIST_PLIST_P (elt)))
+ continue;
+ load_external_module (im_info, MPLIST_PLIST (elt));
+ }
+ }
+ else if (key == Mstate)
+ {
+ MPLIST_DO (elt, MPLIST_NEXT (elt))
+ {
+ MIMState *state;
+
+ if (MFAILP (MPLIST_PLIST_P (elt)))
+ continue;
+ pl = MPLIST_PLIST (elt);
+ if (! im_info->states)
+ im_info->states = mplist ();
+ state = load_state (im_info, MPLIST_PLIST (elt));
+ if (MFAILP (state))
+ continue;
+ mplist_put (im_info->states, state->name, state);
+ }
+ }
+ else if (key == Minclude)
+ {
+ /* elt ::= include (tag1 tag2 ...) key item ... */
+ MSymbol key;
+ MInputMethodInfo *temp;
+
+ elt = MPLIST_NEXT (elt);
+ if (MFAILP (MPLIST_PLIST_P (elt)))
+ continue;
+ temp = get_im_info_by_tags (MPLIST_PLIST (elt));
+ if (MFAILP (temp))
+ continue;
+ elt = MPLIST_NEXT (elt);
+ if (MFAILP (MPLIST_SYMBOL_P (elt)))
+ continue;
+ key = MPLIST_SYMBOL (elt);
+ elt = MPLIST_NEXT (elt);
+ if (key == Mmap)
+ {
+ if (! temp->maps || MPLIST_TAIL_P (temp->maps))
+ continue;
+ if (! im_info->maps)
+ im_info->maps = mplist ();
+ MPLIST_DO (pl, temp->maps)
+ {
+ p = MPLIST_VAL (pl);
+ MPLIST_ADD_PLIST (im_info->maps, MPLIST_KEY (pl), p);
+ M17N_OBJECT_REF (p);
+ }
+ }
+ else if (key == Mmacro)
+ {
+ if (! temp->macros || MPLIST_TAIL_P (temp->macros))
+ continue;
+ if (! im_info->macros)
+ im_info->macros = mplist ();
+ MPLIST_DO (pl, temp->macros)
+ {
+ p = MPLIST_VAL (pl);
+ MPLIST_ADD_PLIST (im_info->macros, MPLIST_KEY (pl), p);
+ M17N_OBJECT_REF (p);
+ }
+ }
+ else if (key == Mstate)
+ {
+ if (! temp->states || MPLIST_TAIL_P (temp->states))
+ continue;
+ if (! im_info->states)
+ im_info->states = mplist ();
+ MPLIST_DO (pl, temp->states)
+ {
+ MIMState *state = MPLIST_VAL (pl);
+
+ mplist_add (im_info->states, MPLIST_KEY (pl), state);
+ M17N_OBJECT_REF (state);
+ }
+ }
+ }
+ else if (key == Mdescription)
+ {
+ if (im_info->description)
+ continue;
+ elt = MPLIST_NEXT (elt);
+ if (MFAILP (MPLIST_MTEXT_P (elt)))
+ continue;
+ im_info->description = MPLIST_MTEXT (elt);
+ M17N_OBJECT_REF (im_info->description);
+
+ }
+ }
+ im_info->tick = time (NULL);
+}
+
+\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))
+ ic_info->prev_state = NULL;
+ 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;
+}
+
+static void
+preedit_insert (MInputContext *ic, int pos, MText *mt, int c)
+{
+ MInputContextInfo *ic_info = ((MInputContext *) ic)->info;
+ MPlist *markers;
+ int nchars = mt ? mtext_nchars (mt) : 1;
+
+ if (mt)
+ mtext_ins (ic->preedit, pos, mt);
+ else
+ mtext_ins_char (ic->preedit, pos, c, 1);
+ MPLIST_DO (markers, ic_info->markers)
+ if (MPLIST_INTEGER (markers) > pos)
+ MPLIST_VAL (markers) = (void *) (MPLIST_INTEGER (markers) + nchars);
+ if (ic->cursor_pos >= pos)
+ ic->cursor_pos += nchars;
+ ic->preedit_changed = 1;
+}
+
+
+static void
+preedit_delete (MInputContext *ic, int from, int to)
+{
+ MInputContextInfo *ic_info = ((MInputContext *) ic)->info;
+ MPlist *markers;
+
+ mtext_del (ic->preedit, from, to);
+ MPLIST_DO (markers, ic_info->markers)
+ {
+ if (MPLIST_INTEGER (markers) > to)
+ MPLIST_VAL (markers)
+ = (void *) (MPLIST_INTEGER (markers) - (to - from));
+ else if (MPLIST_INTEGER (markers) > from);
+ MPLIST_VAL (markers) = (void *) from;
+ }
+ if (ic->cursor_pos >= to)
+ ic->cursor_pos -= to - from;
+ else if (ic->cursor_pos > from)
+ ic->cursor_pos = from;
+ 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)
+ && mtext_nchars (ic->preedit) > 0)
+ {
+ int i;
+
+ MDEBUG_PRINT (" (produced");
+ 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;
+ }
+ if (ic->candidate_list)
+ {
+ M17N_OBJECT_UNREF (ic->candidate_list);
+ ic->candidate_list = NULL;
+ ic->candidates_changed = MINPUT_CANDIDATES_LIST_CHANGED;
+ if (ic->candidate_show)
+ {
+ ic->candidate_show = 0;
+ ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
+ }
+ }
+ memmove (ic_info->keys, ic_info->keys + ic_info->key_head,
+ sizeof (int) * (ic_info->used - ic_info->key_head));
+ ic_info->used -= ic_info->key_head;
+ ic_info->state_key_head = ic_info->key_head = 0;
+}
+
+static int
+new_index (MInputContext *ic, int current, int limit, MSymbol sym, MText *mt)
+{
+ int code = marker_code (sym);
+
+ 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;
+
+ preedit_delete (ic, from, to);
+ if (MPLIST_MTEXT_P (group))
+ {
+ mt = MPLIST_MTEXT (group);
+ preedit_insert (ic, from, 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_insert (ic, from, 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)
+ {
+ if (! (plist = adjust_candidates (plist, charset)))
+ return NULL;
+ }
+ else
+ M17N_OBJECT_REF (plist);
+
+ if (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);
+ }
+ }
+ 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 /* MPLIST_PLIST_P (plist) */
+ {
+ MPlist *pl = MPLIST_PLIST (plist), *p;
+ MPlist *next = MPLIST_NEXT (plist);
+ int j;
+
+ if (MPLIST_TAIL_P (next))
+ M17N_OBJECT_REF (pl);
+ else
+ {
+ pl = mplist_copy (pl);
+ while (! MPLIST_TAIL_P (next))
+ {
+ p = mplist_copy (MPLIST_PLIST (next));
+ pl = mplist__conc (pl, p);
+ M17N_OBJECT_UNREF (p);
+ next = MPLIST_NEXT (next);
+ }
+ }
+ M17N_OBJECT_UNREF (plist);
+ plist = mplist ();
+ len = mplist_length (pl);
+ if (len <= column)
+ mplist_add (plist, Mplist, pl);
+ else
+ {
+ MPlist *p0 = pl;
+
+ for (i = 0; i < len; i += column)
+ {
+ p = mplist ();
+ mplist_add (plist, Mplist, p);
+ M17N_OBJECT_UNREF (p);
+ for (j = 0; j < column && i + j < len; j++)
+ {
+ p = mplist_add (p, Mtext, MPLIST_VAL (p0));
+ p0 = MPLIST_NEXT (p0);
+ }
+ }
+ }
+ M17N_OBJECT_UNREF (pl);
+ }
+ }
+
+ 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 all actions are performed without error, 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)
+ continue;
+ if (MPLIST_MTEXT_P (plist))
+ {
+ preedit_insert (ic, ic->cursor_pos, NULL,
+ mtext_ref_char (MPLIST_MTEXT (plist), 0));
+ len = 1;
+ }
+ 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);
+ M17N_OBJECT_UNREF (plist);
+ }
+ else if (name == Mselect)
+ {
+ int start, end;
+ int code, idx, gindex;
+ int pos = ic->cursor_pos;
+ MPlist *group;
+
+ if (pos == 0
+ || ! (prop = mtext_get_property (ic->preedit, pos - 1,
+ Mcandidate_list)))
+ continue;
+ if (MPLIST_SYMBOL_P (args))
+ {
+ code = marker_code (MPLIST_SYMBOL (args));
+ if (code < 0)
+ continue;
+ }
+ else
+ code = -1;
+ idx = (int) mtext_get_prop (ic->preedit, pos - 1, Mcandidate_index);
+ group = find_candidates_group (mtext_property_value (prop), idx,
+ &start, &end, &gindex);
+
+ if (code != '[' && code != ']')
+ {
+ 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);
+ }
+ 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)
+ {
+ delete_surrounding_text (ic, pos);
+ }
+ else
+ {
+ to = (MPLIST_SYMBOL_P (args)
+ ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),