{
public class MInputMethod
{
- private class MInputAction
+ private static MSymbol Minput_method = MSymbol.Of ("input-method");
+ private static MSymbol Mdescription = MSymbol.Of ("description");
+ private static MSymbol Mvariable = MSymbol.Of ("variable");
+ private static MSymbol Mcommand = MSymbol.Of ("command");
+ private static MSymbol Mtitle = MSymbol.Of ("title");
+ private static MSymbol Minclude = MSymbol.Of ("include");
+ private static MSymbol Mmacro = MSymbol.Of ("macro");
+ private static MSymbol Mmap = MSymbol.Of ("map");
+ private static MSymbol Mstate = MSymbol.Of ("state");
+ internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
+ internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
+ private static MSymbol Mat_less_than = MSymbol.Of ("@<");
+ private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
+ private static MSymbol Mat_minus = MSymbol.Of ("@-");
+ private static MSymbol Mat_plus = MSymbol.Of ("@+");
+ private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
+ private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
+
+
+ internal class Variable
{
- MExpression[] expr_list;
+ public MSymbol name;
+ public MText description;
+ public Type type;
+ public object value;
+ public MPlist candidates;
}
- private class MInputMethodMap
+ internal class Command
{
public MSymbol name;
- private MSymbol[] keys;
- private MInputAction[] actions;
+ public MText description;
+ public MSymbol[][] keys;
+ }
+
+ internal class KeySeq
+ {
+ public MSymbol[] keys;
+
+ private static MSymbol char_to_symbol (int c)
+ {
+ return MSymbol.Of (String.Format ("#%X", c));
+ }
+
+ public KeySeq (MPlist plist)
+ {
+ keys = new MSymbol[plist.Count];
+ int i = 0;
+ foreach (MPlist p in plist)
+ {
+ if (p.IsSymbol)
+ keys[i++] = p.Symbol;
+ else if (p.IsInteger)
+ keys[i++] = char_to_symbol (p.Integer);
+ else
+ keys[i] = null;
+ }
+ }
+
+ public KeySeq (MText mt)
+ {
+ keys = new MSymbol[mt.Length];
+ for (int i = 0; i < mt.Length; i++)
+ keys[i] = char_to_symbol (mt[i]);
+ }
+
+ public MSymbol this[int i] { get { return keys[i]; } }
+
+ public int Length { get { return keys.Length; } }
}
- private class MInputMethodBranch
+ internal class Map
{
public MSymbol name;
- private MInputAction[] actions;
+ public Dictionary<MSymbol, Map> submaps;
+ public MExpression actions;
+
+ public void Add (KeySeq keys, int index, MExpression actions)
+ {
+ Map sub = null;
+
+ if (submaps == null)
+ submaps = new Dictionary<MSymbol, Map> ();
+ else
+ submaps.TryGetValue (keys[index], out sub);
+ if (sub == null)
+ submaps[keys[index]] = sub = new Map ();
+ if (index + 1 < keys.Length)
+ sub.Add (keys, index + 1, actions);
+ else
+ this.actions = actions;
+ }
+
+ public MExpression Lookup (KeySeq keys, int index)
+ {
+ Map sub;
+
+ if (index + 1 == keys.Length)
+ return actions;
+ if (submaps.TryGetValue (keys[index], out sub))
+ return sub.Lookup (keys, index + 1);
+ return null;
+ }
}
- private class MInputMethodState
+ internal class State
{
- public MText title;
public MSymbol name;
- public MInputMethodBranch[] branches;
+ public MText title;
+ public Dictionary<MSymbol, MExpression> branches
+ = new Dictionary<MSymbol, MExpression> ();
}
+ private static Dictionary<MDatabase.Tag, MInputMethod> im_table;
+
+ private static MExpression.FunctionTable global_table
+ = new MExpression.FunctionTable ();
+
public readonly MSymbol language;
public readonly MSymbol name;
public readonly MSymbol subname;
- private MDatabase mdb;
- private MText description;
- private MText title;
- private MPlist commands;
- private MPlist variables;
- private MPlist maps;
- private MPlist states;
- private MPlist macros;
- private MPlist externals;
+ internal MExpression.FunctionTable local_table
+ = new MExpression.FunctionTable (global_table);
+ internal MDatabase mdb;
+ internal MText description;
+ internal MText title;
+ internal Command[] commands;
+ internal Variable[] variables;
+ internal MPlist bindings;
+ internal Dictionary<MSymbol, Map> maps
+ = new Dictionary<MSymbol, Map> ();
+ internal State init_state;
+ internal Dictionary<MSymbol, State> states
+ = new Dictionary<MSymbol, State> ();
+ internal MPlist externals;
- public MInputMethod (MSymbol language, MSymbol name, MSymbol extra)
+ static MInputMethod ()
{
- MDatabase.Tag tag = new MDatabase.Tag (language, name, extra);
+ MExpression.Defun (global_table, "insert",
+ new MExpression.Evaluator (insert),
+ 2, 2, typeof (MInputContext), typeof (MExpression));
+ MExpression.Defun (global_table, "candidates",
+ new MExpression.Evaluator (insert_candidates),
+ 2, 2, typeof (MInputContext), typeof (object));
+ MExpression.Defun (global_table, "delete",
+ new MExpression.Evaluator (delete),
+ 2, 2, typeof (MInputContext), typeof (object));
+ MExpression.Defun (global_table, "select",
+ new MExpression.Evaluator (select),
+ 2, 2, typeof (MInputContext), typeof (object));
+ MExpression.Defun (global_table, "show",
+ new MExpression.Evaluator (show),
+ 1, 1, typeof (MInputContext));
+ MExpression.Defun (global_table, "hide",
+ new MExpression.Evaluator (hide),
+ 1, 1, typeof (MInputContext));
+ MExpression.Defun (global_table, "move",
+ new MExpression.Evaluator (move),
+ 2, 2, typeof (MInputContext), typeof (object));
+ MExpression.Defun (global_table, "mark",
+ new MExpression.Evaluator (mark),
+ 2, 2, typeof (MInputContext), typeof (MSymbol));
+ MExpression.Defun (global_table, "pushback",
+ new MExpression.Evaluator (pushback),
+ 2, 2, typeof (MInputContext), typeof (object));
+ MExpression.Defun (global_table, "pop",
+ new MExpression.Evaluator (pop),
+ 1, 1, typeof (MInputContext));
+ MExpression.Defun (global_table, "undo",
+ new MExpression.Evaluator (undo),
+ 2, 2, typeof (MInputContext), typeof (object));
+ MExpression.Defun (global_table, "commit",
+ new MExpression.Evaluator (commit),
+ 1, 1, typeof (MInputContext));
+ MExpression.Defun (global_table, "unhandle",
+ new MExpression.Evaluator (unhandle),
+ 1, 1, typeof (MInputContext));
+ MExpression.Defun (global_table, "shift",
+ new MExpression.Evaluator (shift),
+ 2, 2, typeof (MInputContext), typeof (MSymbol));
+ MExpression.Defun (global_table, "call",
+ new MExpression.Evaluator (call),
+ 3, -1, typeof (MInputContext), typeof (MSymbol),
+ typeof (MSymbol), typeof (object));
+ }
+ private MInputMethod (MDatabase.Tag tag)
+ {
mdb = MDatabase.Find (tag);
if (mdb == null)
throw new Exception (String.Format ("Input method {0} not available",
tag));
+ language = tag[1];
+ name = tag[2];
+ subname = tag[3];
MPlist plist = (MPlist) mdb.Load ();
+ if (plist == null)
+ return;
+ for (; ! plist.IsEmpty; plist = plist.next)
+ if (plist.IsPlist)
+ {
+ MPlist pl = plist.Plist;
+ if (pl.IsSymbol)
+ {
+ MSymbol sym = pl.Symbol;
+ pl = pl.next;
+ if (sym == Mdescription)
+ {
+ description = parse_description (pl);
+ if (description == null)
+ description = new MText ("No description");
+ }
+ else if (sym == Mtitle)
+ {
+ if (pl.IsMText)
+ title = pl.Text;
+ }
+ else if (sym == Mvariable)
+ parse_variables (pl);
+ else if (sym == Mcommand)
+ parse_commands (pl);
+ else if (sym == Minclude)
+ parse_include (pl);
+ else if (sym == Mmacro)
+ parse_macros (pl);
+ else if (sym == Mmap)
+ parse_maps (pl);
+ else if (sym == Mstate)
+ parse_states (pl);
+ }
+ }
+ }
+
+ private static MText parse_description (MPlist plist)
+ {
+ return (plist.IsMText ? plist.Text
+ : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
+ : null);
+ }
+
+ private void parse_variables (MPlist plist)
+ {
+ variables = new Variable[plist.Count];
+
+ for (int i = 0; ! plist.IsEmpty; plist = plist.next)
+ if (plist.IsPlist && plist.Plist.IsSymbol)
+ {
+ Variable var = new Variable ();
+ MPlist p = plist.Plist;
+
+ var.name = p.Symbol;
+ p = p.Next;
+ var.description = parse_description (p);
+ if (var.description == null)
+ var.description = new MText ("No description");
+ else
+ p = p.next;
+ var.type = (p.IsMText ? typeof (MText)
+ : p.IsInteger ? typeof (int)
+ : p.IsSymbol ? typeof (MSymbol)
+ : typeof (object));
+ var.value = p.val;
+ var.candidates = p.next;
+ variables[i++] = var;
+ }
+ }
+
+ private void parse_commands (MPlist plist)
+ {
+ commands = new Command[plist.Count];
+
+ for (int i = 0; ! plist.IsEmpty; plist = plist.next)
+ if (plist.IsPlist && plist.Plist.IsSymbol)
+ {
+ Command cmd = new Command ();
+ MPlist p = plist.Plist;
+
+ cmd.name = p.Symbol;
+ p = p.Next;
+ cmd.description = parse_description (p);
+ if (cmd.description == null)
+ cmd.description = new MText ("No description");
+ else
+ p = p.next;
+ KeySeq[] keys = new KeySeq[p.Count];
+ for (int j = 0; ! p.IsEmpty; p = p.next)
+ {
+ if (p.IsMText)
+ keys[j++] = new KeySeq (p.Text);
+ else if (p.IsPlist)
+ keys[j++] = new KeySeq (p.Plist);
+ }
+ commands[i++] = cmd;
+ }
+ }
+
+ private void parse_include (MPlist plist)
+ {
+ if (! plist.IsPlist)
+ return;
+ MPlist p = plist.Plist.Cons (MSymbol.symbol, Minput_method);
+ MDatabase.Tag tag = new MDatabase.Tag (ref p);
+ MInputMethod im;
+ try {
+ im_table.TryGetValue (tag, out im);
+ } catch {
+ return;
+ }
+ plist = plist.next;
+ if (! plist.IsSymbol)
+ return;
+ MSymbol target_type = plist.Symbol;
+ plist = plist.next;
+ MSymbol target_name = MSymbol.nil;
+ if (plist.IsSymbol)
+ target_name = plist.Symbol;
+ if (target_type == Mmacro)
+ {
+ if (target_name == MSymbol.nil)
+ im.local_table.Copy (local_table);
+ else
+ im.local_table.Copy (target_name, local_table);
+ }
+ else if (target_type == Mmap)
+ {
+ if (target_name == MSymbol.nil)
+ {
+ foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
+ maps[kv.Key] = kv.Value;
+ }
+ else
+ {
+ Map map;
+ if (im.maps.TryGetValue (target_name, out map))
+ maps[target_name] = map;
+ }
+ }
+ else if (target_type == Mstate)
+ {
+ if (target_name == MSymbol.nil)
+ {
+ foreach (KeyValuePair<MSymbol, State> kv in im.states)
+ states[kv.Key] = kv.Value;
+ }
+ else
+ {
+ State state;
+ if (im.states.TryGetValue (target_name, out state))
+ states[target_name] = state;
+ }
+ }
+ }
+
+ private void parse_macros (MPlist plist)
+ {
+ for (; ! plist.IsEmpty; plist = plist.next)
+ if (plist.IsPlist)
+ {
+ MPlist pl = plist.Plist;
+
+ if (! pl.IsSymbol)
+ continue;
+ MSymbol name = pl.Symbol;
+ MExpression expr = new MExpression (pl.next, local_table);
+ MExpression.Defmacro (local_table, name, expr);
+ }
+ }
+
+ private void parse_maps (MPlist plist)
+ {
+ for (; ! plist.IsEmpty; plist = plist.next)
+ if (plist.IsPlist)
+ {
+ MPlist pl = plist.Plist;
+
+ if (! pl.IsSymbol)
+ continue;
+ Map map = new Map ();
+ map.name = pl.Symbol;
+ maps[map.name] = map;
+ for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+ {
+ if (! pl.IsPlist)
+ continue;
+ KeySeq keys;
+ if (pl.IsMText)
+ keys = new KeySeq (pl.Text);
+ else if (pl.IsPlist)
+ keys = new KeySeq (pl.Plist);
+ else
+ continue;
+ if (keys.keys.Length == 0
+ && keys.keys[0] == null)
+ continue;
+ pl = pl.next;
+ if (pl.IsEmpty)
+ continue;
+ MExpression expr = new MExpression (pl, local_table);
+ map.Add (keys, 0, expr);
+ }
+ }
+ }
+
+ private void parse_states (MPlist plist)
+ {
+ for (; ! plist.IsEmpty; plist = plist.next)
+ if (plist.IsPlist)
+ {
+ MPlist pl = plist.Plist;
+ MText title = null;
+
+ if (pl.IsMText)
+ {
+ title = pl.Text;
+ pl = pl.next;
+ }
+ if (! pl.IsSymbol)
+ continue;
+
+ State state = new State ();
+ state.name = pl.Symbol;
+ state.title = title;
+ states[state.name] = state;
+ if (init_state == null)
+ init_state = state;
+ for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+ {
+ if (! pl.IsPlist)
+ continue;
+ MPlist p = pl.Plist;
+ if (! p.IsSymbol)
+ continue;
+ state.branches[p.Symbol]
+ = new MExpression (p.next, local_table);
+ }
+ }
+ }
+
+ public MInputMethod Get (MSymbol language, MSymbol name, MSymbol extra)
+ {
+ MDatabase.Tag tag
+ = new MDatabase.Tag (Minput_method, language, name, extra);
+ MInputMethod im;
+ if (im_table.TryGetValue (tag, out im))
+ return im;
+ try {
+ im = new MInputMethod (tag);
+ } catch {
+ im = null;
+ }
+ return im;
+ }
+
+ private static void adjust_markers (MInputContext ic,
+ int from, int to, object inserted)
+ {
+ int ins = (inserted == null ? 0
+ : inserted is int ? 1
+ : ((MText) inserted).Length);
+ int diff = ins - (to - from);
+
+ for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
+ {
+ int pos = plist.Integer;
+ if (pos > from)
+ {
+ if (pos >= to)
+ plist.val = pos + diff;
+ else
+ plist.val = from;
+ }
+ }
+ if (ic.cursor_pos >= to)
+ ic.cursor_pos += diff;
+ else if (ic.cursor_pos > from)
+ ic.cursor_pos = from;
+ }
+
+ private static void preedit_replace (MInputContext ic,
+ int from, int to, int c)
+ {
+ ic.preedit.Del (from, to);
+ ic.preedit.Ins (from, c);
+ adjust_markers (ic, from, to, c);
+ }
+
+ private static void preedit_replace (MInputContext ic,
+ int from, int to, MText mt)
+ {
+ ic.preedit[from, to] = mt;
+ adjust_markers (ic, from, to, mt);
+ }
+
+ private static object insert (object[] args, MPlist bindings)
+ {
+ MInputContext ic = (MInputContext) args[0];
+ object arg = ((MExpression) args[1]).Eval (bindings);
+
+ if (arg is int)
+ preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
+ else
+ preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
+ ic.preedit_changed = true;
+ ic.cursor_pos_changed = true;
+ return 1;
+ }
+
+ internal class Candidates
+ {
+ private class Block
+ {
+ public Block Prev, Next;
+ public int Index;
+ public object Data;
+
+ public Block (Block prev, int index, MPlist plist)
+ {
+ Prev = prev;
+ if (prev != null)
+ prev.Next = this;
+ Index = index;
+ Data = plist.IsMText ? plist.Text : plist.Plist;
+ }
+
+ public int Count
+ {
+ get { return (data is MText
+ ? ((MText) data).Length
+ : ((MPlist) data).Count); }
+ }
+
+ public Block First
+ {
+ get {
+ Block b;
+ for (b = this; b.Prev != null; b = n.Prev);
+ return b;
+ }
+ }
+
+ public Block Last
+ {
+ get {
+ Block b;
+ for (b = this; b.Next != null; b = n.Next);
+ return b;
+ }
+ }
+
+ public object this[int i]
+ {
+ get { return (data is MText
+ ? ((MText) data)[i]
+ : ((MPlist) data)[i]); }
+ }
+ }
+
+ private Block block;
+ private int current_index = -1;
+ private int total_count = 0;
+ private object[] group = null;
+
+ public Candidates (MPlist list, int column)
+ {
+ if (column > 0)
+ group = new object[column];
+ Block b = null;
+ int index;
+ for (index = 0; ! list.IsEmpty; list = list.next)
+ {
+ b = new Block (b, index, list);
+ if (index == 0)
+ block = b;
+ index += b.Count;
+ }
+ total_count = index;
+ }
+
+ public int InGroupIndex
+ {
+ get {
+ return (group == null
+ ? current_index - block.Index
+ : current_index % group.Length);
+ }
+ }
+
+ public static void Detach (MInputContext ic)
+ {
+ ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+ ic.candidates = null;
+ ic.preedit_changed = true;
+ ic.cursor_pos_changed = true;
+ ic.candidate_changed = true;
+ }
+
+ // Fill the array "group" by candidates stating from INDEX.
+ // INDEX must be a multiple of "column". Set NTIMES to the
+ // number of valid candidates in "group". Update "block" if
+ // necessary. Return "group".
+
+ private object fill_group (int index, out int nitems)
+ {
+ int column = group.Length;
+
+ if (index > block.Index + column)
+ block = block.Last;
+ while (index < block.Index)
+ block = block.Prev;
+
+ Block b = block;
+ int count = b.Count;
+ int inblock = index - b.Index;
+ for (nitems = 0; ntimes < column; ntimes++)
+ {
+ group[nitems] = b[inblock++];
+ if (inblock >= count)
+ {
+ b = b.Next;
+ if (b == null)
+ break;
+ count = b.Count;
+ inblock = 0;
+ }
+ }
+ return group;
+ }
+
+ // Update "block" to what contains the first candidate of the
+ // previous candidate-group, update "current_index", and update
+ // "group" if necessary. Return the previous candidate-group.
+ // Set NITEMS to the number of valid candidates contained in
+ // that group.
+
+ public object PrevGroup (out int nitems, out int ingroup)
+ {
+ object val;
+
+ ingroup = InGroupIndex;
+ if (group == null)
+ {
+ block = (block.Prev != null) ? block.Prev : block.Last;
+ current_index = block.Index;
+ nitems = block.Count;
+ val = block.Data;
+ }
+ else
+ {
+ nitems = group.Length;
+ current_index -= ingroup + nitems;
+ if (current_index < 0)
+ current_index = (total_count / nitems) * nitems;
+ val = fill_group (current_index, out nitems);
+ }
+ if (ingroup >= nitems)
+ ingroup = nitems - 1;
+ current_index += ingroup;
+ return val;
+ }
+
+ public object NextGroup (out int nitems)
+ {
+ int ingroup = InGroupIndex;
+ object val;
+
+ nitems = 0;
+ if (group == null)
+ {
+ block = (block.Next != null) ? block.Next : block.First;
+ current_index = block.Index;
+ nitems = block.Count;
+ val = block.Data;
+ }
+ else
+ {
+ nitems = group.Length;
+ current_index += column - ingroup;
+ if (current_index >= total_count)
+ current_index = 0;
+ val = fill_group (current_index, out nitems);
+ }
+ if (ingroup >= nitems)
+ ingroup = nitems - 1;
+ current_index += ingroup;
+ return val;
+ }
+
+ public object Next (out int nitems)
+ {
+ int ingroup = InGroupIndex + 1;
+ object val;
+ int index = current_index + 1;
+
+ nitems = group == null ? block.Count : group.Length;
+
+ if (ingroup >= nitems)
+ {
+ val = NextGroup (out nitems);
+ current_index = index < total_count ? index : 0;
+ return val;
+ }
+ if (group == null)
+ {
+ nitems = block.Count;
+ val = block.Data;
+ }
+ else
+ {
+ nitems = group.Length;
+ val = group;
+ }
+ current_index++;
+ return val;
+ }
+
+ public object Prev (out int nitems)
+ {
+ int ingroup = InGroupIndex - 1;
+ object val;
+ int index = current_index - 1;
+
+ if (ingroup < 0)
+ {
+ val = PrevGroup (out nitems);
+ current_index = index >= 0 ? index : total_count - 1;
+ return val;
+ }
+ if (group == null)
+ {
+ nitems = block.Count;
+ val = block.Data;
+ }
+ else
+ {
+ nitems = group.Length;
+ val = group;
+ }
+ current_index--;
+ return val;
+ }
+
+ public object First (out int nitems)
+ {
+ if (group == null)
+ {
+ nitems = block.Count;
+ current_index = block.Index;
+ return block.Data;
+ }
+ nitems = group.Length;
+ current_index = (current_index / nitems) * nitems;
+ return group;
+ }
+
+ public object Last (out int nitems)
+ {
+ if (group == null)
+ {
+ nitems = block.Count;
+ current_index = block.Index + block.Count - 1;;
+ return block.Data;
+ }
+ nitems = group.Length;
+ current_index = (current_index / nitems) * nitems + nitems - 1;
+ return group;
+ }
+
+ public object Select (int index, out int ingroup, out int len)
+ {
+ if (index < current_index)
+
+
+ MPlist prev;
+ return find_group (index, out ingroup_index, out text_len, out prev);
+ }
+
+ private MPlist find_group (int index, out int ingroup_index,
+ out int text_len, out MPlist previous)
+ {
+ MPlist p;
+ int i = 0;
+
+ for (p = list, previous = null; ! p.IsEmpty; previous = p, p = p.next)
+ {
+ int len = p.IsMText ? p.Text.Length : p.Plist.Count;
+ if (index < i + len)
+ break;
+ i += len;
+ }
+ ingroup_index = index - i;
+ if (p.IsMText)
+ text_len = 1;
+ else
+ text_len = p.Plist[ingroup_index].Text.Length;
+ return p;
+ }
+
+ private void Update (MInputContext ic, object group, int ingroup)
+ {
+ int from, to, len;
+
+ if (current_index == index)
+ return;
+ if (ic.candidates == null)
+ {
+ from = ic.cursor_pos;
+ to = ic.cursor_pos;
+ ic.candidates = this;
+ }
+ else
+ {
+ from = ic.candidate_from;
+ to = ic.candidate_to;
+ }
+ group =
+ p = find_group (index, out ingroup, out len, out prev);
+ to = from + len;
+ if (p.IsMText)
+ preedit_replace (ic, from, to, p.Text[ingroup]);
+ else
+ preedit_replace (ic, from, to, p.Plist[ingroup].Text);
+ ic.preedit.PushProp (from, to, Mcandidates, this);
+ ic.cursor_pos = to;
+ ic.candidate_from = from;
+ ic.candidate_to = to;
+ ic.preedit_changed = true;
+ ic.cursor_pos_changed = true;
+ ic.candidate_changed = true;
+ }
+
+ private void Update (MInputContext ic, int index)
+ {
+ int from, to, len;
+ object group;
+
+ if (current_index == index)
+ return;
+ if (ic.candidates == null)
+ {
+ from = ic.cursor_pos;
+ to = ic.cursor_pos;
+ ic.candidates = this;
+ }
+ else
+ {
+ from = ic.candidate_from;
+ to = ic.candidate_to;
+ }
+ group =
+ p = find_group (index, out ingroup, out len, out prev);
+ to = from + len;
+ if (p.IsMText)
+ preedit_replace (ic, from, to, p.Text[ingroup]);
+ else
+ preedit_replace (ic, from, to, p.Plist[ingroup].Text);
+ ic.preedit.PushProp (from, to, Mcandidates, this);
+ ic.cursor_pos = to;
+ ic.candidate_from = from;
+ ic.candidate_to = to;
+ ic.preedit_changed = true;
+ ic.cursor_pos_changed = true;
+ ic.candidate_changed = true;
+ }
+
+ public void Prev (MInputContext ic) {}
+ public void Next (MInputContext ic) {}
+ public void First (MInputContext ic) {}
+ public void Last (MInputContext ic) {}
+ public void PrevGroup (MInputContext ic) {}
+ public void NextGroup (MInputContext ic) {}
+ public void Select (MInputContext ic, int index) {}
+
+ }
+
+ private static object insert_candidates (object[] args, MPlist bindings)
+ {
+ MInputContext ic = (MInputContext) args[0];
+ MPlist list = (MPlist) args[1];
+ int column = 0;
+
+ MPlist slot = (MPlist) bindings.Find (Mcandidates_group_size);
+ if (slot != null)
+ column = slot.Integer;
+ Candidates candidtes = new Candidates (list);
+ candidates.Update (ic, 0);
+ return 1;
+ }
+
+ private static object select (object[] args, MPlist bindings)
+ {
+ MInputContext ic = (MInputContext) args[0];
+ object arg = args[1];
+
+ if (ic.candidates == null)
+ return 0;
+ if (arg is MSymbol)
+ {
+ MSymbol sym = (MSymbol) arg;
+
+ if (sym == Mat_less_than)
+ ic.candidates.Update (ic, 0);
+ else if (sym == Mat_greater_than)
+ ic.candidates.Update (ic, -1);
+ else if (sym == Mat_minus)
+ ic.candidates.Prev (ic);
+ else if (sym == Mat_plus)
+ ic.candidates.Next (ic);
+ else if (sym == Mat_open_square_bracket)
+ ic.candidates.PrevGroup (ic);
+ else if (sym == Mat_close_square_bracket)
+ ic.candidates.NextGroup (ic);
+ }
+ else if (arg is int)
+ ic.candidates.SelectInGroup (ic, (int) arg);
+ return 0;
+ }
+
+ private static object delete (object[] args, MPlist bindings) { return 1; }
+ private static object show (object[] args, MPlist bindings) { return 1; }
+ private static object hide (object[] args, MPlist bindings) { return 1; }
+ private static object move (object[] args, MPlist bindings) { return 1; }
+ private static object mark (object[] args, MPlist bindings) { return 1; }
+ private static object pushback (object[] args, MPlist bindings) { return 1; }
+ private static object pop (object[] args, MPlist bindings) { return 1; }
+ private static object undo (object[] args, MPlist bindings) { return 1; }
+ private static object commit (object[] args, MPlist bindings) { return 1; }
+ private static object unhandle (object[] args, MPlist bindings) { return 1; }
+ private static object shift (object[] args, MPlist bindings) { return 1; }
+ private static object call (object[] args, MPlist bindings) { return 1; }
+ }
+
+ public class MInputContext
+ {
+ public MInputMethod im;
+ public MText produced;
+ public bool active;
+ public MText status;
+ public bool status_changed;
+ public MText preedit;
+ public bool preedit_changed;
+ public int cursor_pos;
+ public bool cursor_pos_changed;
+ internal MInputMethod.Candidates candidates;
+ public MPlist candidate_group;
+ public int candidate_index;
+ public int candidate_from, candidate_to;
+ public bool candidate_show;
+ public bool candidate_changed;
+ public MPlist callback_args;
+
+ private MInputMethod.State state;
+ private MInputMethod.State prev_state;
+ private Stack<MSymbol> keys;
+ private int state_key_head;
+ private int key_head;
+ private int commit_key_head;
+ private MText preedit_saved;
+ private int state_pos;
+ internal MPlist markers = new MPlist ();
+ private MPlist vars;
+ private MPlist vars_saved;
+ private MText preceding_text;
+ private MText following_text;
+ private bool key_unhandled;
+
+ internal MPlist GetCandidates (out int ingroup_index, out int text_len)
+ {
+ ingroup_index = text_len = 0;
+ if (cursor_pos == 0)
+ return null;
+ MInputMethod.Candidates candidates
+ = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
+ MInputMethod.Mcandidates);
+ if (candidates == null)
+ return null;
+ return candidates.FindGroup (candidate_index,
+ out ingroup_index, out text_len);
}
}
}