2 using System.Collections;
3 using System.Collections.Generic;
11 public class MInputMethod
13 private static MSymbol Minput_method = MSymbol.Of ("input-method");
14 private static MSymbol Mdescription = MSymbol.Of ("description");
15 private static MSymbol Mvariable = MSymbol.Of ("variable");
16 private static MSymbol Mcommand = MSymbol.Of ("command");
17 private static MSymbol Mtitle = MSymbol.Of ("title");
18 private static MSymbol Minclude = MSymbol.Of ("include");
19 private static MSymbol Mmacro = MSymbol.Of ("macro");
20 private static MSymbol Mmap = MSymbol.Of ("map");
21 private static MSymbol Mstate = MSymbol.Of ("state");
22 internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
23 internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
24 private static MSymbol Mat_less_than = MSymbol.Of ("@<");
25 private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
26 private static MSymbol Mat_minus = MSymbol.Of ("@-");
27 private static MSymbol Mat_plus = MSymbol.Of ("@+");
28 private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
29 private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
32 internal class Variable
35 public MText description;
38 public MPlist candidates;
41 internal class Command
44 public MText description;
45 public MSymbol[][] keys;
50 public MSymbol[] keys;
52 private static MSymbol char_to_symbol (int c)
54 return MSymbol.Of (String.Format ("#%X", c));
57 public KeySeq (MPlist plist)
59 keys = new MSymbol[plist.Count];
61 foreach (MPlist p in plist)
66 keys[i++] = char_to_symbol (p.Integer);
72 public KeySeq (MText mt)
74 keys = new MSymbol[mt.Length];
75 for (int i = 0; i < mt.Length; i++)
76 keys[i] = char_to_symbol (mt[i]);
79 public MSymbol this[int i] { get { return keys[i]; } }
81 public int Length { get { return keys.Length; } }
87 public Dictionary<MSymbol, Map> submaps;
88 public MExpression actions;
90 public void Add (KeySeq keys, int index, MExpression actions)
95 submaps = new Dictionary<MSymbol, Map> ();
97 submaps.TryGetValue (keys[index], out sub);
99 submaps[keys[index]] = sub = new Map ();
100 if (index + 1 < keys.Length)
101 sub.Add (keys, index + 1, actions);
103 this.actions = actions;
106 public MExpression Lookup (KeySeq keys, int index)
110 if (index + 1 == keys.Length)
112 if (submaps.TryGetValue (keys[index], out sub))
113 return sub.Lookup (keys, index + 1);
122 public Dictionary<MSymbol, MExpression> branches
123 = new Dictionary<MSymbol, MExpression> ();
126 private static Dictionary<MDatabase.Tag, MInputMethod> im_table;
128 private static MExpression.FunctionTable global_table
129 = new MExpression.FunctionTable ();
131 public readonly MSymbol language;
132 public readonly MSymbol name;
133 public readonly MSymbol subname;
135 internal MExpression.FunctionTable local_table
136 = new MExpression.FunctionTable (global_table);
137 internal MDatabase mdb;
138 internal MText description;
139 internal MText title;
140 internal Command[] commands;
141 internal Variable[] variables;
142 internal MPlist bindings;
143 internal Dictionary<MSymbol, Map> maps
144 = new Dictionary<MSymbol, Map> ();
145 internal State init_state;
146 internal Dictionary<MSymbol, State> states
147 = new Dictionary<MSymbol, State> ();
148 internal MPlist externals;
150 static MInputMethod ()
152 MExpression.Defun (global_table, "insert",
153 new MExpression.Evaluator (insert),
154 2, 2, typeof (MInputContext), typeof (MExpression));
155 MExpression.Defun (global_table, "candidates",
156 new MExpression.Evaluator (insert_candidates),
157 2, 2, typeof (MInputContext), typeof (object));
158 MExpression.Defun (global_table, "delete",
159 new MExpression.Evaluator (delete),
160 2, 2, typeof (MInputContext), typeof (object));
161 MExpression.Defun (global_table, "select",
162 new MExpression.Evaluator (select),
163 2, 2, typeof (MInputContext), typeof (object));
164 MExpression.Defun (global_table, "show",
165 new MExpression.Evaluator (show),
166 1, 1, typeof (MInputContext));
167 MExpression.Defun (global_table, "hide",
168 new MExpression.Evaluator (hide),
169 1, 1, typeof (MInputContext));
170 MExpression.Defun (global_table, "move",
171 new MExpression.Evaluator (move),
172 2, 2, typeof (MInputContext), typeof (object));
173 MExpression.Defun (global_table, "mark",
174 new MExpression.Evaluator (mark),
175 2, 2, typeof (MInputContext), typeof (MSymbol));
176 MExpression.Defun (global_table, "pushback",
177 new MExpression.Evaluator (pushback),
178 2, 2, typeof (MInputContext), typeof (object));
179 MExpression.Defun (global_table, "pop",
180 new MExpression.Evaluator (pop),
181 1, 1, typeof (MInputContext));
182 MExpression.Defun (global_table, "undo",
183 new MExpression.Evaluator (undo),
184 2, 2, typeof (MInputContext), typeof (object));
185 MExpression.Defun (global_table, "commit",
186 new MExpression.Evaluator (commit),
187 1, 1, typeof (MInputContext));
188 MExpression.Defun (global_table, "unhandle",
189 new MExpression.Evaluator (unhandle),
190 1, 1, typeof (MInputContext));
191 MExpression.Defun (global_table, "shift",
192 new MExpression.Evaluator (shift),
193 2, 2, typeof (MInputContext), typeof (MSymbol));
194 MExpression.Defun (global_table, "call",
195 new MExpression.Evaluator (call),
196 3, -1, typeof (MInputContext), typeof (MSymbol),
197 typeof (MSymbol), typeof (object));
200 private MInputMethod (MDatabase.Tag tag)
202 mdb = MDatabase.Find (tag);
204 throw new Exception (String.Format ("Input method {0} not available",
209 MPlist plist = (MPlist) mdb.Load ();
212 for (; ! plist.IsEmpty; plist = plist.next)
215 MPlist pl = plist.Plist;
218 MSymbol sym = pl.Symbol;
220 if (sym == Mdescription)
222 description = parse_description (pl);
223 if (description == null)
224 description = new MText ("No description");
226 else if (sym == Mtitle)
231 else if (sym == Mvariable)
232 parse_variables (pl);
233 else if (sym == Mcommand)
235 else if (sym == Minclude)
237 else if (sym == Mmacro)
239 else if (sym == Mmap)
241 else if (sym == Mstate)
247 private static MText parse_description (MPlist plist)
249 return (plist.IsMText ? plist.Text
250 : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
254 private void parse_variables (MPlist plist)
256 variables = new Variable[plist.Count];
258 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
259 if (plist.IsPlist && plist.Plist.IsSymbol)
261 Variable var = new Variable ();
262 MPlist p = plist.Plist;
266 var.description = parse_description (p);
267 if (var.description == null)
268 var.description = new MText ("No description");
271 var.type = (p.IsMText ? typeof (MText)
272 : p.IsInteger ? typeof (int)
273 : p.IsSymbol ? typeof (MSymbol)
276 var.candidates = p.next;
277 variables[i++] = var;
281 private void parse_commands (MPlist plist)
283 commands = new Command[plist.Count];
285 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
286 if (plist.IsPlist && plist.Plist.IsSymbol)
288 Command cmd = new Command ();
289 MPlist p = plist.Plist;
293 cmd.description = parse_description (p);
294 if (cmd.description == null)
295 cmd.description = new MText ("No description");
298 KeySeq[] keys = new KeySeq[p.Count];
299 for (int j = 0; ! p.IsEmpty; p = p.next)
302 keys[j++] = new KeySeq (p.Text);
304 keys[j++] = new KeySeq (p.Plist);
310 private void parse_include (MPlist plist)
314 MPlist p = plist.Plist.Cons (MSymbol.symbol, Minput_method);
315 MDatabase.Tag tag = new MDatabase.Tag (ref p);
318 im_table.TryGetValue (tag, out im);
323 if (! plist.IsSymbol)
325 MSymbol target_type = plist.Symbol;
327 MSymbol target_name = MSymbol.nil;
329 target_name = plist.Symbol;
330 if (target_type == Mmacro)
332 if (target_name == MSymbol.nil)
333 im.local_table.Copy (local_table);
335 im.local_table.Copy (target_name, local_table);
337 else if (target_type == Mmap)
339 if (target_name == MSymbol.nil)
341 foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
342 maps[kv.Key] = kv.Value;
347 if (im.maps.TryGetValue (target_name, out map))
348 maps[target_name] = map;
351 else if (target_type == Mstate)
353 if (target_name == MSymbol.nil)
355 foreach (KeyValuePair<MSymbol, State> kv in im.states)
356 states[kv.Key] = kv.Value;
361 if (im.states.TryGetValue (target_name, out state))
362 states[target_name] = state;
367 private void parse_macros (MPlist plist)
369 for (; ! plist.IsEmpty; plist = plist.next)
372 MPlist pl = plist.Plist;
376 MSymbol name = pl.Symbol;
377 MExpression expr = new MExpression (pl.next, local_table);
378 MExpression.Defmacro (local_table, name, expr);
382 private void parse_maps (MPlist plist)
384 for (; ! plist.IsEmpty; plist = plist.next)
387 MPlist pl = plist.Plist;
391 Map map = new Map ();
392 map.name = pl.Symbol;
393 maps[map.name] = map;
394 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
400 keys = new KeySeq (pl.Text);
402 keys = new KeySeq (pl.Plist);
405 if (keys.keys.Length == 0
406 && keys.keys[0] == null)
411 MExpression expr = new MExpression (pl, local_table);
412 map.Add (keys, 0, expr);
417 private void parse_states (MPlist plist)
419 for (; ! plist.IsEmpty; plist = plist.next)
422 MPlist pl = plist.Plist;
433 State state = new State ();
434 state.name = pl.Symbol;
436 states[state.name] = state;
437 if (init_state == null)
439 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
446 state.branches[p.Symbol]
447 = new MExpression (p.next, local_table);
452 public MInputMethod Get (MSymbol language, MSymbol name, MSymbol extra)
455 = new MDatabase.Tag (Minput_method, language, name, extra);
457 if (im_table.TryGetValue (tag, out im))
460 im = new MInputMethod (tag);
467 private static void adjust_markers (MInputContext ic,
468 int from, int to, object inserted)
470 int ins = (inserted == null ? 0
471 : inserted is int ? 1
472 : ((MText) inserted).Length);
473 int diff = ins - (to - from);
475 for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
477 int pos = plist.Integer;
481 plist.val = pos + diff;
486 if (ic.cursor_pos >= to)
487 ic.cursor_pos += diff;
488 else if (ic.cursor_pos > from)
489 ic.cursor_pos = from;
492 private static void preedit_replace (MInputContext ic,
493 int from, int to, int c)
495 ic.preedit.Del (from, to);
496 ic.preedit.Ins (from, c);
497 adjust_markers (ic, from, to, c);
500 private static void preedit_replace (MInputContext ic,
501 int from, int to, MText mt)
503 ic.preedit[from, to] = mt;
504 adjust_markers (ic, from, to, mt);
507 private static object insert (object[] args, MPlist bindings)
509 MInputContext ic = (MInputContext) args[0];
510 object arg = ((MExpression) args[1]).Eval (bindings);
513 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
515 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
516 ic.preedit_changed = true;
517 ic.cursor_pos_changed = true;
521 internal class Candidates
525 public Block Prev, Next;
529 public Block (Block prev, int index, MPlist plist)
535 Data = plist.IsMText ? plist.Text : plist.Plist;
540 get { return (data is MText
541 ? ((MText) data).Length
542 : ((MPlist) data).Count); }
549 for (b = this; b.Prev != null; b = n.Prev);
558 for (b = this; b.Next != null; b = n.Next);
563 public object this[int i]
565 get { return (data is MText
567 : ((MPlist) data)[i]); }
572 private int current_index = -1;
573 private int total_count = 0;
574 private object[] group = null;
576 public Candidates (MPlist list, int column)
579 group = new object[column];
582 for (index = 0; ! list.IsEmpty; list = list.next)
584 b = new Block (b, index, list);
592 public int InGroupIndex
595 return (group == null
596 ? current_index - block.Index
597 : current_index % group.Length);
601 public static void Detach (MInputContext ic)
603 ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
604 ic.candidates = null;
605 ic.preedit_changed = true;
606 ic.cursor_pos_changed = true;
607 ic.candidate_changed = true;
610 // Fill the array "group" by candidates stating from INDEX.
611 // INDEX must be a multiple of "column". Set NTIMES to the
612 // number of valid candidates in "group". Update "block" if
613 // necessary. Return "group".
615 private object fill_group (int index, out int nitems)
617 int column = group.Length;
619 if (index > block.Index + column)
621 while (index < block.Index)
626 int inblock = index - b.Index;
627 for (nitems = 0; ntimes < column; ntimes++)
629 group[nitems] = b[inblock++];
630 if (inblock >= count)
642 // Update "block" to what contains the first candidate of the
643 // previous candidate-group, update "current_index", and update
644 // "group" if necessary. Return the previous candidate-group.
645 // Set NITEMS to the number of valid candidates contained in
648 public object PrevGroup (out int nitems, out int ingroup)
652 ingroup = InGroupIndex;
655 block = (block.Prev != null) ? block.Prev : block.Last;
656 current_index = block.Index;
657 nitems = block.Count;
662 nitems = group.Length;
663 current_index -= ingroup + nitems;
664 if (current_index < 0)
665 current_index = (total_count / nitems) * nitems;
666 val = fill_group (current_index, out nitems);
668 if (ingroup >= nitems)
669 ingroup = nitems - 1;
670 current_index += ingroup;
674 public object NextGroup (out int nitems)
676 int ingroup = InGroupIndex;
682 block = (block.Next != null) ? block.Next : block.First;
683 current_index = block.Index;
684 nitems = block.Count;
689 nitems = group.Length;
690 current_index += column - ingroup;
691 if (current_index >= total_count)
693 val = fill_group (current_index, out nitems);
695 if (ingroup >= nitems)
696 ingroup = nitems - 1;
697 current_index += ingroup;
701 public object Next (out int nitems)
703 int ingroup = InGroupIndex + 1;
705 int index = current_index + 1;
707 nitems = group == null ? block.Count : group.Length;
709 if (ingroup >= nitems)
711 val = NextGroup (out nitems);
712 current_index = index < total_count ? index : 0;
717 nitems = block.Count;
722 nitems = group.Length;
729 public object Prev (out int nitems)
731 int ingroup = InGroupIndex - 1;
733 int index = current_index - 1;
737 val = PrevGroup (out nitems);
738 current_index = index >= 0 ? index : total_count - 1;
743 nitems = block.Count;
748 nitems = group.Length;
755 public object First (out int nitems)
759 nitems = block.Count;
760 current_index = block.Index;
763 nitems = group.Length;
764 current_index = (current_index / nitems) * nitems;
768 public object Last (out int nitems)
772 nitems = block.Count;
773 current_index = block.Index + block.Count - 1;;
776 nitems = group.Length;
777 current_index = (current_index / nitems) * nitems + nitems - 1;
781 public object Select (int index, out int ingroup, out int len)
783 if (index < current_index)
787 return find_group (index, out ingroup_index, out text_len, out prev);
790 private MPlist find_group (int index, out int ingroup_index,
791 out int text_len, out MPlist previous)
796 for (p = list, previous = null; ! p.IsEmpty; previous = p, p = p.next)
798 int len = p.IsMText ? p.Text.Length : p.Plist.Count;
803 ingroup_index = index - i;
807 text_len = p.Plist[ingroup_index].Text.Length;
811 private void Update (MInputContext ic, object group, int ingroup)
815 if (current_index == index)
817 if (ic.candidates == null)
819 from = ic.cursor_pos;
821 ic.candidates = this;
825 from = ic.candidate_from;
826 to = ic.candidate_to;
829 p = find_group (index, out ingroup, out len, out prev);
832 preedit_replace (ic, from, to, p.Text[ingroup]);
834 preedit_replace (ic, from, to, p.Plist[ingroup].Text);
835 ic.preedit.PushProp (from, to, Mcandidates, this);
837 ic.candidate_from = from;
838 ic.candidate_to = to;
839 ic.preedit_changed = true;
840 ic.cursor_pos_changed = true;
841 ic.candidate_changed = true;
844 private void Update (MInputContext ic, int index)
849 if (current_index == index)
851 if (ic.candidates == null)
853 from = ic.cursor_pos;
855 ic.candidates = this;
859 from = ic.candidate_from;
860 to = ic.candidate_to;
863 p = find_group (index, out ingroup, out len, out prev);
866 preedit_replace (ic, from, to, p.Text[ingroup]);
868 preedit_replace (ic, from, to, p.Plist[ingroup].Text);
869 ic.preedit.PushProp (from, to, Mcandidates, this);
871 ic.candidate_from = from;
872 ic.candidate_to = to;
873 ic.preedit_changed = true;
874 ic.cursor_pos_changed = true;
875 ic.candidate_changed = true;
878 public void Prev (MInputContext ic) {}
879 public void Next (MInputContext ic) {}
880 public void First (MInputContext ic) {}
881 public void Last (MInputContext ic) {}
882 public void PrevGroup (MInputContext ic) {}
883 public void NextGroup (MInputContext ic) {}
884 public void Select (MInputContext ic, int index) {}
888 private static object insert_candidates (object[] args, MPlist bindings)
890 MInputContext ic = (MInputContext) args[0];
891 MPlist list = (MPlist) args[1];
894 MPlist slot = (MPlist) bindings.Find (Mcandidates_group_size);
896 column = slot.Integer;
897 Candidates candidtes = new Candidates (list);
898 candidates.Update (ic, 0);
902 private static object select (object[] args, MPlist bindings)
904 MInputContext ic = (MInputContext) args[0];
905 object arg = args[1];
907 if (ic.candidates == null)
911 MSymbol sym = (MSymbol) arg;
913 if (sym == Mat_less_than)
914 ic.candidates.Update (ic, 0);
915 else if (sym == Mat_greater_than)
916 ic.candidates.Update (ic, -1);
917 else if (sym == Mat_minus)
918 ic.candidates.Prev (ic);
919 else if (sym == Mat_plus)
920 ic.candidates.Next (ic);
921 else if (sym == Mat_open_square_bracket)
922 ic.candidates.PrevGroup (ic);
923 else if (sym == Mat_close_square_bracket)
924 ic.candidates.NextGroup (ic);
927 ic.candidates.SelectInGroup (ic, (int) arg);
931 private static object delete (object[] args, MPlist bindings) { return 1; }
932 private static object show (object[] args, MPlist bindings) { return 1; }
933 private static object hide (object[] args, MPlist bindings) { return 1; }
934 private static object move (object[] args, MPlist bindings) { return 1; }
935 private static object mark (object[] args, MPlist bindings) { return 1; }
936 private static object pushback (object[] args, MPlist bindings) { return 1; }
937 private static object pop (object[] args, MPlist bindings) { return 1; }
938 private static object undo (object[] args, MPlist bindings) { return 1; }
939 private static object commit (object[] args, MPlist bindings) { return 1; }
940 private static object unhandle (object[] args, MPlist bindings) { return 1; }
941 private static object shift (object[] args, MPlist bindings) { return 1; }
942 private static object call (object[] args, MPlist bindings) { return 1; }
945 public class MInputContext
947 public MInputMethod im;
948 public MText produced;
951 public bool status_changed;
952 public MText preedit;
953 public bool preedit_changed;
954 public int cursor_pos;
955 public bool cursor_pos_changed;
956 internal MInputMethod.Candidates candidates;
957 public MPlist candidate_group;
958 public int candidate_index;
959 public int candidate_from, candidate_to;
960 public bool candidate_show;
961 public bool candidate_changed;
962 public MPlist callback_args;
964 private MInputMethod.State state;
965 private MInputMethod.State prev_state;
966 private Stack<MSymbol> keys;
967 private int state_key_head;
968 private int key_head;
969 private int commit_key_head;
970 private MText preedit_saved;
971 private int state_pos;
972 internal MPlist markers = new MPlist ();
974 private MPlist vars_saved;
975 private MText preceding_text;
976 private MText following_text;
977 private bool key_unhandled;
979 internal MPlist GetCandidates (out int ingroup_index, out int text_len)
981 ingroup_index = text_len = 0;
984 MInputMethod.Candidates candidates
985 = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
986 MInputMethod.Mcandidates);
987 if (candidates == null)
989 return candidates.FindGroup (candidate_index,
990 out ingroup_index, out text_len);