2 using System.Collections;
3 using System.Collections.Generic;
11 public class MInputMethod
13 internal static MExpression.Domain domain = new MExpression.Domain (null);
15 private static MSymbol Minput_method = MSymbol.Of ("input-method");
16 private static MSymbol Mdescription = MSymbol.Of ("description");
17 private static MSymbol Mvariable = MSymbol.Of ("variable");
18 private static MSymbol Mcommand = MSymbol.Of ("command");
19 private static MSymbol Mtitle = MSymbol.Of ("title");
20 private static MSymbol Minclude = MSymbol.Of ("include");
21 private static MSymbol Mmacro = MSymbol.Of ("macro");
22 private static MSymbol Mmap = MSymbol.Of ("map");
23 private static MSymbol Mstate = MSymbol.Of ("state");
24 internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
25 internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
26 private static MSymbol Mat_less_than = MSymbol.Of ("@<");
27 private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
28 private static MSymbol Mat_minus = MSymbol.Of ("@-");
29 private static MSymbol Mat_plus = MSymbol.Of ("@+");
30 private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
31 private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
32 private static MSymbol Minsert = MSymbol.Of ("insert");
34 internal class Variable
37 public MText description;
40 public MPlist candidates;
43 internal class Command
46 public MText description;
47 public MSymbol[][] keys;
52 public MSymbol[] keys;
54 private static MSymbol char_to_symbol (int c)
56 return MSymbol.Of (String.Format ("#{0:X}", c));
59 public KeySeq (MPlist plist)
61 keys = new MSymbol[plist.Count];
63 foreach (MPlist p in plist)
68 keys[i++] = char_to_symbol (p.Integer);
74 public KeySeq (MText mt)
76 keys = new MSymbol[mt.Length];
77 for (int i = 0; i < mt.Length; i++)
78 keys[i] = char_to_symbol (mt[i]);
81 public MSymbol this[int i] { get { return keys[i]; } }
83 public int Length { get { return keys.Length; } }
89 public Dictionary<MSymbol, Map> submaps;
90 public MExpression actions;
92 public void Add (KeySeq keys, int index, MExpression actions)
97 submaps = new Dictionary<MSymbol, Map> ();
99 submaps.TryGetValue (keys[index], out sub);
102 MSymbol sym = keys[index];
103 submaps[sym] = sub = new Map ();
105 if (index + 1 < keys.Length)
106 sub.Add (keys, index + 1, actions);
108 this.actions = actions;
111 public MExpression Lookup (KeySeq keys, int index)
115 if (index + 1 == keys.Length)
117 if (submaps.TryGetValue (keys[index], out sub))
118 return sub.Lookup (keys, index + 1);
127 public Dictionary<MSymbol, MExpression> branches
128 = new Dictionary<MSymbol, MExpression> ();
131 private static Dictionary<MDatabase.Tag, MInputMethod> im_table
132 = new Dictionary<MDatabase.Tag, MInputMethod> ();
134 public readonly MSymbol language;
135 public readonly MSymbol name;
136 public readonly MSymbol subname;
138 internal MDatabase mdb;
139 internal MText description;
140 internal MText title;
141 internal Command[] commands;
142 internal Variable[] variables;
143 internal MPlist bindings;
144 internal Dictionary<MSymbol, Map> maps
145 = new Dictionary<MSymbol, Map> ();
146 internal State init_state;
147 internal Dictionary<MSymbol, State> states
148 = new Dictionary<MSymbol, State> ();
149 internal MPlist externals;
151 static MInputMethod ()
153 domain.Defun ("insert", new MExpression.Evaluator (insert), 1, 1);
154 domain.Defun ("candidates", new MExpression.Evaluator (insert_candidates),
156 domain.Defun ("delete", new MExpression.Evaluator (delete), 1, 1);
157 domain.Defun ("select", new MExpression.Evaluator (select), 1, 1);
158 domain.Defun ("show", new MExpression.Evaluator (show), 0, 0);
159 domain.Defun ("hide", new MExpression.Evaluator (hide), 0, 0);
160 domain.Defun ("move", new MExpression.Evaluator (move), 1, 1);
161 domain.Defun ("mark", new MExpression.Evaluator (mark), 1, 1);
162 domain.Defun ("pushback", new MExpression.Evaluator (pushback), 1, 1);
163 domain.Defun ("pop", new MExpression.Evaluator (pop), 0, 0);
164 domain.Defun ("undo", new MExpression.Evaluator (undo), 0, 1);
165 domain.Defun ("commit", new MExpression.Evaluator (commit), 0, 0);
166 domain.Defun ("unhandle", new MExpression.Evaluator (unhandle), 0, 0);
167 domain.Defun ("shift", new MExpression.Evaluator (shift), 1, 1);
168 domain.Defun ("call", new MExpression.Evaluator (call), 2, -1);
171 private MInputMethod (MDatabase.Tag tag)
173 mdb = MDatabase.Find (tag);
175 throw new Exception (String.Format ("Input method {0} not available",
180 MPlist plist = (MPlist) mdb.Load ();
183 for (; ! plist.IsEmpty; plist = plist.next)
186 MPlist pl = plist.Plist;
189 MSymbol sym = pl.Symbol;
191 if (sym == Mdescription)
193 description = parse_description (pl);
194 if (description == null)
195 description = new MText ("No description");
197 else if (sym == Mtitle)
202 else if (sym == Mvariable)
203 parse_variables (pl);
204 else if (sym == Mcommand)
206 else if (sym == Minclude)
208 else if (sym == Mmacro)
210 else if (sym == Mmap)
212 else if (sym == Mstate)
218 private static MText parse_description (MPlist plist)
220 return (plist.IsMText ? plist.Text
221 : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
225 private void parse_variables (MPlist plist)
227 variables = new Variable[plist.Count];
229 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
230 if (plist.IsPlist && plist.Plist.IsSymbol)
232 Variable var = new Variable ();
233 MPlist p = plist.Plist;
237 var.description = parse_description (p);
238 if (var.description == null)
239 var.description = new MText ("No description");
242 var.type = (p.IsMText ? typeof (MText)
243 : p.IsInteger ? typeof (int)
244 : p.IsSymbol ? typeof (MSymbol)
247 var.candidates = p.next;
248 variables[i++] = var;
252 private void parse_commands (MPlist plist)
254 commands = new Command[plist.Count];
256 for (int i = 0; ! plist.IsEmpty; plist = plist.next)
257 if (plist.IsPlist && plist.Plist.IsSymbol)
259 Command cmd = new Command ();
260 MPlist p = plist.Plist;
264 cmd.description = parse_description (p);
265 if (cmd.description == null)
266 cmd.description = new MText ("No description");
269 KeySeq[] keys = new KeySeq[p.Count];
270 for (int j = 0; ! p.IsEmpty; p = p.next)
273 keys[j++] = new KeySeq (p.Text);
275 keys[j++] = new KeySeq (p.Plist);
281 private void parse_include (MPlist plist)
285 MPlist p = plist.Plist;
286 MSymbol language, name, extra;
290 name = extra = MSymbol.nil;
301 MInputMethod im = MInputMethod.Get (language, name, extra);
305 if (! plist.IsSymbol)
307 MSymbol target_type = plist.Symbol;
309 MSymbol target_name = MSymbol.nil;
311 target_name = plist.Symbol;
312 if (target_type == Mmacro)
314 if (target_name == MSymbol.nil)
315 im.domain.Copy (local_table);
317 im.local_table.Copy (target_name, local_table);
319 else if (target_type == Mmap)
321 if (target_name == MSymbol.nil)
323 foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
324 maps[kv.Key] = kv.Value;
329 if (im.maps.TryGetValue (target_name, out map))
330 maps[target_name] = map;
333 else if (target_type == Mstate)
335 if (target_name == MSymbol.nil)
337 foreach (KeyValuePair<MSymbol, State> kv in im.states)
338 states[kv.Key] = kv.Value;
343 if (im.states.TryGetValue (target_name, out state))
344 states[target_name] = state;
349 private void parse_macros (MPlist plist)
351 for (; ! plist.IsEmpty; plist = plist.next)
354 MPlist pl = plist.Plist;
358 MSymbol name = pl.Symbol;
359 MExpression expr = new MExpression (pl.next, local_table);
360 MExpression.Defmacro (local_table, name, expr);
364 private void parse_maps (MPlist plist)
366 for (; ! plist.IsEmpty; plist = plist.next)
369 MPlist pl = plist.Plist;
373 Map map = new Map ();
374 map.name = pl.Symbol;
375 maps[map.name] = map;
376 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
383 keys = new KeySeq (p.Text);
385 keys = new KeySeq (p.Plist);
388 if (keys.keys.Length == 0
389 && keys.keys[0] == null)
394 MExpression expr = new MExpression (p, local_table);
395 map.Add (keys, 0, expr);
400 private void parse_states (MPlist plist)
402 for (; ! plist.IsEmpty; plist = plist.next)
405 MPlist pl = plist.Plist;
416 State state = new State ();
417 state.name = pl.Symbol;
419 states[state.name] = state;
420 if (init_state == null)
422 for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
429 MSymbol map_name = p.Symbol;
431 state.branches[map_name] = new MExpression (p, local_table);
436 public static MInputMethod Get (MSymbol language, MSymbol name,
440 = new MDatabase.Tag (Minput_method, language, name, extra);
442 if (im_table.TryGetValue (tag, out im))
445 im = new MInputMethod (tag);
446 } catch (Exception e) {
447 Console.WriteLine (e);
453 private static void adjust_markers (MInputContext ic,
454 int from, int to, object inserted)
456 int ins = (inserted == null ? 0
457 : inserted is int ? 1
458 : ((MText) inserted).Length);
459 int diff = ins - (to - from);
461 for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
463 int pos = plist.Integer;
467 plist.val = pos + diff;
472 if (ic.cursor_pos >= to)
473 ic.cursor_pos += diff;
474 else if (ic.cursor_pos > from)
475 ic.cursor_pos = from;
478 private static void preedit_replace (MInputContext ic,
479 int from, int to, int c)
481 ic.preedit.Del (from, to);
482 ic.preedit.Ins (from, c);
483 adjust_markers (ic, from, to, c);
486 private static void preedit_replace (MInputContext ic,
487 int from, int to, MText mt)
489 ic.preedit[from, to] = mt;
490 adjust_markers (ic, from, to, mt);
493 private static object insert (MExpression[] args,
494 MExpression.Domain domain)
496 MInputContext ic = (MInputContext) domain.context;
497 object arg = args[0].val;
500 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
502 preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
503 ic.preedit_changed = true;
504 ic.cursor_pos_changed = true;
508 internal class Candidates
515 public Block (int index, MPlist plist)
526 get { return (Data is MText
527 ? ((MText) Data).Length
528 : ((MPlist) Data).Count); }
531 public object this[int i]
534 if (Data is MText) return ((MText) Data)[i];
535 return ((MPlist) Data)[i];
540 private Block[] blocks;
542 private int index = 0;
543 public object[] group;
545 private bool IsFixed { get { return group != null; } }
548 Block last = blocks[blocks.Length - 1];
549 return last.Index + last.Count; }
553 get { return (IsFixed ? index % group.Length
554 : index - blocks[row].Index); }
557 public object Group {
558 get { return (IsFixed ? group : blocks[row].Data); }
561 public int GroupLength
566 int nitems = group.Length;
567 int start = index - (index % nitems);
569 return (start + nitems <= total ? nitems : total - start);
571 return blocks[row].Count;
575 public object Current {
577 return (IsFixed ? group[index % group.Length]
578 : blocks[row][index - blocks[row].Index]);
582 public Candidates (MPlist list, int column)
584 int nblocks = list.Count;
586 blocks = new Block[nblocks];
587 for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
588 start += (blocks[i] = new Block (index, list)).Count;
590 group = new object[column];
593 public static void Detach (MInputContext ic)
595 ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
596 ic.candidates = null;
597 ic.preedit_changed = true;
598 ic.cursor_pos_changed = true;
599 ic.candidate_changed = true;
602 // Fill the array "group" by candidates stating from INDEX.
603 // INDEX must be a multiple of "column". Set NTIMES to the
604 // number of valid candidates in "group". Update "block" if
605 // necessary. Return "group".
607 private int fill_group (int start)
609 int nitems = group.Length;
614 while (start < b.Index)
617 while (start >= b.Index + b.Count)
623 for (int i = 0; i < nitems; i++, start++)
628 if (r == blocks.Length)
639 // Update "row" to what contains the first candidate of
640 // the previous candidate-group, update "current_index", and
641 // update "group" if necessary. Return the previous
642 // candidate-group. Set NITEMS to the number of valid
643 // candidates contained in that group.
645 public int PrevGroup ()
652 nitems = group.Length;
653 if ((index -= col + nitems) < 0)
654 index = (Total / nitems) * nitems;
655 nitems = fill_group (index);
659 row = row > 0 ? row-- : blocks.Length - 1;
660 nitems = blocks[row].Count;
661 index = blocks[row].Index;
663 index += col < nitems ? col : nitems - 1;
667 public int NextGroup ()
674 nitems = group.Length;
675 if ((index += nitems - col) >= Total)
677 nitems = fill_group (index);
681 row = row < blocks.Length - 1 ? row + 1 : 0;
682 nitems = blocks[row].Count;
683 index = blocks[row].Count;
685 index += col < nitems ? col : nitems - 1;
695 int nitems = PrevGroup ();
696 index += col < nitems - 1 ? col : nitems - 1;
705 int nitems = GroupLength;
707 if (col == nitems - 1)
709 nitems = NextGroup ();
723 index += GroupLength - (Column + 1);
726 public void Select (int col)
728 int maxcol = GroupLength - 1;
731 index = index - Column + col;
734 public void Update (MInputContext ic)
738 if (ic.candidates == null)
740 from = ic.cursor_pos;
742 ic.candidates = this;
746 from = ic.candidate_from;
747 to = ic.candidate_to;
750 object candidate = ic.candidates.Current;
752 if (candidate is MText)
753 preedit_replace (ic, from, to, (MText) candidate);
755 preedit_replace (ic, from, to, (int) candidate);
756 ic.preedit.PushProp (from, to, Mcandidates, this);
758 ic.candidate_from = from;
759 ic.candidate_to = to;
760 ic.preedit_changed = true;
761 ic.cursor_pos_changed = true;
762 ic.candidate_changed = true;
766 private static object insert_candidates (MExpression[] args,
767 MExpression.Domain domain)
769 MInputContext ic = (MInputContext) domain.context;
770 MPlist list = (MPlist) args[0];
773 if (domain.IsBound (Mcandidates_group_size))
775 object val = domain.GetValue (Mcandidates_group_size);
779 Candidates candidates = new Candidates (list, column);
780 candidates.Update (ic);
784 private static object select (MExpression[] args,
785 MExpression.Domain domain)
787 MInputContext ic = (MInputContext) domain.context;
788 object arg = args[0].val;
790 if (ic.candidates == null)
794 MSymbol sym = (MSymbol) arg;
796 if (sym == Mat_less_than)
797 ic.candidates.First ();
798 else if (sym == Mat_greater_than)
799 ic.candidates.Last ();
800 else if (sym == Mat_minus)
801 ic.candidates.Prev ();
802 else if (sym == Mat_plus)
803 ic.candidates.Next ();
804 else if (sym == Mat_open_square_bracket)
805 ic.candidates.PrevGroup ();
806 else if (sym == Mat_close_square_bracket)
807 ic.candidates.NextGroup ();
812 ic.candidates.Select ((int) arg);
813 ic.candidates.Update (ic);
817 private static object delete (MExpression[] args, MExpression.Domain domain)
822 private static object show (MExpression[] args, MExpression.Domain domain)
824 private static object hide (MExpression[] args, MExpression.Domain domain)
826 private static object move (MExpression[] args, MExpression.Domain domain)
828 private static object mark (MExpression[] args, MExpression.Domain domain)
830 private static object pushback (MExpression[] args, MExpression.Domain domain)
832 private static object pop (MExpression[] args, MExpression.Domain domain)
834 private static object undo (MExpression[] args, MExpression.Domain domain)
836 private static object commit (MExpression[] args, MExpression.Domain domain)
838 private static object unhandle (MExpression[] args, MExpression.Domain domain)
840 private static object shift (MExpression[] args, MExpression.Domain domain)
842 private static object call (MExpression[] args, MExpression.Domain domain)
846 public class MInputContext
848 public MInputMethod im;
849 public MText produced;
852 public bool status_changed;
853 public MText preedit;
854 public bool preedit_changed;
855 public int cursor_pos;
856 public bool cursor_pos_changed;
857 internal MInputMethod.Candidates candidates;
858 public MPlist candidate_group;
859 public int candidate_index;
860 public int candidate_from, candidate_to;
861 public bool candidate_show;
862 public bool candidate_changed;
863 public MPlist callback_args;
865 private MInputMethod.State state;
866 private MInputMethod.State prev_state;
867 private Stack<MSymbol> keys;
868 private int state_key_head;
869 private int key_head;
870 private int commit_key_head;
871 private MText preedit_saved;
872 private int state_pos;
873 internal MPlist markers = new MPlist ();
875 private MPlist vars_saved;
876 private MText preceding_text;
877 private MText following_text;
878 private bool key_unhandled;
880 internal MExpression.Domain domain;
882 public MInputContext (MInputMethod im)
885 domain = new MExpression (this);
888 internal object GetCandidates (out int column)
893 MInputMethod.Candidates candidates
894 = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
895 MInputMethod.Mcandidates);
896 if (candidates == null)
898 column = candidates.Column;
899 return candidates.Current;