*** empty log message ***
[m17n/m17n-lib-cs.git] / MInputMethod.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using M17N;
6 using M17N.Core;
7 using M17N.Input;
8
9 namespace M17N.Input
10 {
11   public class MInputMethod
12   {
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 ("@]");
30
31
32     internal class Variable
33     {
34       public MSymbol name;
35       public MText description;
36       public Type type;
37       public object value;
38       public MPlist candidates;
39     }
40
41     internal class Command
42     {
43       public MSymbol name;
44       public MText description;
45       public MSymbol[][] keys;
46     }
47
48     internal class KeySeq
49     {
50       public MSymbol[] keys;
51
52       private static MSymbol char_to_symbol (int c)
53       {
54         return MSymbol.Of (String.Format ("#%X", c));
55       }
56
57       public KeySeq (MPlist plist)
58       {
59         keys = new MSymbol[plist.Count];
60         int i = 0;
61         foreach (MPlist p in plist)
62           {
63             if (p.IsSymbol)
64               keys[i++] = p.Symbol;
65             else if (p.IsInteger)
66               keys[i++] = char_to_symbol (p.Integer);
67             else
68               keys[i] = null;
69           }
70       }
71
72       public KeySeq (MText mt)
73       {
74         keys = new MSymbol[mt.Length];
75         for (int i = 0; i < mt.Length; i++)
76           keys[i] = char_to_symbol (mt[i]);
77       }
78
79       public MSymbol this[int i] { get { return keys[i]; } }
80
81       public int Length { get { return keys.Length; } }
82     }
83
84     internal class Map
85     {
86       public MSymbol name;
87       public Dictionary<MSymbol, Map> submaps;
88       public MExpression actions;
89
90       public void Add (KeySeq keys, int index, MExpression actions)
91       {
92         Map sub = null;
93
94         if (submaps == null)
95           submaps = new Dictionary<MSymbol, Map> ();
96         else
97           submaps.TryGetValue (keys[index], out sub);
98         if (sub == null)
99           submaps[keys[index]] = sub = new Map ();
100         if (index + 1 < keys.Length)
101           sub.Add (keys, index + 1, actions);
102         else
103           this.actions = actions;
104       }
105
106       public MExpression Lookup (KeySeq keys, int index)
107       {
108         Map sub;
109
110         if (index + 1 == keys.Length)
111           return actions;
112         if (submaps.TryGetValue (keys[index], out sub))
113           return sub.Lookup (keys, index + 1);
114         return null;
115       }
116     }
117
118     internal class State
119     {
120       public MSymbol name;
121       public MText title;
122       public Dictionary<MSymbol, MExpression> branches
123         = new Dictionary<MSymbol, MExpression> ();
124     }
125
126     private static Dictionary<MDatabase.Tag, MInputMethod> im_table;
127
128     private static MExpression.FunctionTable global_table
129       = new MExpression.FunctionTable ();
130
131     public readonly MSymbol language;
132     public readonly MSymbol name;
133     public readonly MSymbol subname;
134
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;
149
150     static MInputMethod ()
151     {
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));
198     }
199
200     private MInputMethod (MDatabase.Tag tag)
201     {
202       mdb = MDatabase.Find (tag);
203       if (mdb == null)
204         throw new Exception (String.Format ("Input method {0} not available",
205                                             tag));
206       language = tag[1];
207       name = tag[2];
208       subname = tag[3];
209       MPlist plist = (MPlist) mdb.Load ();
210       if (plist == null)
211         return;
212       for (; ! plist.IsEmpty; plist = plist.next)
213         if (plist.IsPlist)
214           {
215             MPlist pl = plist.Plist;
216             if (pl.IsSymbol)
217               {
218                 MSymbol sym = pl.Symbol;
219                 pl = pl.next;
220                 if (sym == Mdescription)
221                   {
222                     description = parse_description (pl);
223                     if (description == null)
224                       description = new MText ("No description");
225                   }
226                 else if (sym == Mtitle)
227                   {
228                     if (pl.IsMText)
229                       title = pl.Text;
230                   }
231                 else if (sym == Mvariable)
232                   parse_variables (pl);
233                 else if (sym == Mcommand)
234                   parse_commands (pl);
235                 else if (sym == Minclude)
236                   parse_include (pl);
237                 else if (sym == Mmacro)
238                   parse_macros (pl);
239                 else if (sym == Mmap)
240                   parse_maps (pl);
241                 else if (sym == Mstate)
242                   parse_states (pl);
243               }
244           }
245     }
246
247     private static MText parse_description (MPlist plist)
248     {
249       return (plist.IsMText ? plist.Text
250               : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
251               : null);
252     }
253
254     private void parse_variables (MPlist plist)
255     {
256       variables = new Variable[plist.Count];
257
258       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
259         if (plist.IsPlist && plist.Plist.IsSymbol)
260           {
261             Variable var = new Variable ();
262             MPlist p = plist.Plist;
263
264             var.name = p.Symbol;
265             p = p.Next;
266             var.description = parse_description (p);
267             if (var.description == null)
268               var.description = new MText ("No description");
269             else
270               p = p.next;
271             var.type = (p.IsMText ? typeof (MText)
272                         : p.IsInteger ? typeof (int)
273                         : p.IsSymbol ? typeof (MSymbol)
274                         : typeof (object));
275             var.value = p.val;
276             var.candidates = p.next;
277             variables[i++] = var;
278           }
279     }
280
281     private void parse_commands (MPlist plist)
282     {
283       commands = new Command[plist.Count];
284
285       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
286         if (plist.IsPlist && plist.Plist.IsSymbol)
287           {
288             Command cmd = new Command ();
289             MPlist p = plist.Plist;
290
291             cmd.name = p.Symbol;
292             p = p.Next;
293             cmd.description = parse_description (p);
294             if (cmd.description == null)
295               cmd.description = new MText ("No description");
296             else
297               p = p.next;
298             KeySeq[] keys = new KeySeq[p.Count];
299             for (int j = 0; ! p.IsEmpty; p = p.next)
300               {
301                 if (p.IsMText)
302                   keys[j++] = new KeySeq (p.Text);
303                 else if (p.IsPlist)
304                   keys[j++] = new KeySeq (p.Plist);
305               }
306             commands[i++] = cmd;
307           }
308     }
309
310     private void parse_include (MPlist plist)
311     {
312       if (! plist.IsPlist)
313         return;
314       MPlist p = plist.Plist.Cons (MSymbol.symbol, Minput_method);
315       MDatabase.Tag tag = new MDatabase.Tag (ref p);
316       MInputMethod im;
317       try {
318         im_table.TryGetValue (tag, out im);
319       } catch {
320         return;
321       }
322       plist = plist.next;
323       if (! plist.IsSymbol)
324         return;
325       MSymbol target_type = plist.Symbol;
326       plist = plist.next;
327       MSymbol target_name = MSymbol.nil;
328       if (plist.IsSymbol)
329         target_name = plist.Symbol;
330       if (target_type == Mmacro)
331         {
332           if (target_name == MSymbol.nil)
333             im.local_table.Copy (local_table);
334           else
335             im.local_table.Copy (target_name, local_table);
336         }
337       else if (target_type == Mmap)
338         {
339           if (target_name == MSymbol.nil)
340             {
341               foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
342                 maps[kv.Key] = kv.Value;
343             }
344           else
345             {
346               Map map;
347               if (im.maps.TryGetValue (target_name, out map))
348                 maps[target_name] = map;
349             }
350         }
351       else if (target_type == Mstate)
352         {
353           if (target_name == MSymbol.nil)
354             {
355               foreach (KeyValuePair<MSymbol, State> kv in im.states)
356                 states[kv.Key] = kv.Value;
357             }
358           else
359             {
360               State state;
361               if (im.states.TryGetValue (target_name, out state))
362                 states[target_name] = state;
363             }
364         }
365     }
366
367     private void parse_macros (MPlist plist)
368     {
369       for (; ! plist.IsEmpty; plist = plist.next)
370         if (plist.IsPlist)
371           {
372             MPlist pl = plist.Plist;
373
374             if (! pl.IsSymbol)
375               continue;
376             MSymbol name = pl.Symbol;
377             MExpression expr = new MExpression (pl.next, local_table);
378             MExpression.Defmacro (local_table, name, expr);
379           }
380     }
381
382     private void parse_maps (MPlist plist)
383     {
384       for (; ! plist.IsEmpty; plist = plist.next)
385         if (plist.IsPlist)
386           {
387             MPlist pl = plist.Plist;
388           
389             if (! pl.IsSymbol)
390               continue;
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)
395               {
396                 if (! pl.IsPlist)
397                   continue;
398                 KeySeq keys;
399                 if (pl.IsMText)
400                   keys = new KeySeq (pl.Text);
401                 else if (pl.IsPlist)
402                   keys = new KeySeq (pl.Plist);
403                 else
404                   continue;
405                 if (keys.keys.Length == 0
406                     && keys.keys[0] == null)
407                   continue;
408                 pl = pl.next;
409                 if (pl.IsEmpty)
410                   continue;
411                 MExpression expr = new MExpression (pl, local_table);
412                 map.Add (keys, 0, expr);
413               }
414           }
415     }
416
417     private void parse_states (MPlist plist)
418     {
419       for (; ! plist.IsEmpty; plist = plist.next)
420         if (plist.IsPlist)
421           {
422             MPlist pl = plist.Plist;
423             MText title = null;
424           
425             if (pl.IsMText)
426               {
427                 title = pl.Text;
428                 pl = pl.next;
429               }
430             if (! pl.IsSymbol)
431               continue;
432
433             State state = new State ();
434             state.name = pl.Symbol;
435             state.title = title;
436             states[state.name] = state;
437             if (init_state == null)
438               init_state = state;
439             for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
440               {
441                 if (! pl.IsPlist)
442                   continue;
443                 MPlist p = pl.Plist;
444                 if (! p.IsSymbol)
445                   continue;
446                 state.branches[p.Symbol]
447                   = new MExpression (p.next, local_table);
448               }
449           }
450     }
451
452     public MInputMethod Get (MSymbol language, MSymbol name, MSymbol extra)
453     {
454       MDatabase.Tag tag
455         = new MDatabase.Tag (Minput_method, language, name, extra);
456       MInputMethod im;
457       if (im_table.TryGetValue (tag, out im))
458         return im;
459       try {
460         im = new MInputMethod (tag);
461       } catch {
462         im = null;
463       }
464       return im;
465     }
466
467     private static void adjust_markers (MInputContext ic,
468                                         int from, int to, object inserted)
469     {
470       int ins = (inserted == null ? 0
471                  : inserted is int ? 1
472                  : ((MText) inserted).Length);
473       int diff = ins - (to - from);
474
475       for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
476         {
477           int pos = plist.Integer;
478           if (pos > from)
479             {
480               if (pos >= to)
481                 plist.val = pos + diff;
482               else
483                 plist.val = from;
484             }
485         }
486       if (ic.cursor_pos >= to)
487         ic.cursor_pos += diff;
488       else if (ic.cursor_pos > from)
489         ic.cursor_pos = from;
490     }
491
492     private static void preedit_replace (MInputContext ic,
493                                          int from, int to, int c)
494     {
495       ic.preedit.Del (from, to);
496       ic.preedit.Ins (from, c);
497       adjust_markers (ic, from, to, c);
498     }
499
500     private static void preedit_replace (MInputContext ic,
501                                          int from, int to, MText mt)
502     {
503       ic.preedit[from, to] = mt;
504       adjust_markers (ic, from, to, mt);
505     }
506
507     private static object insert (object[] args, MPlist bindings)
508     {
509       MInputContext ic = (MInputContext) args[0];
510       object arg = ((MExpression) args[1]).Eval (bindings);
511
512       if (arg is int)
513         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
514       else
515         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
516       ic.preedit_changed = true;
517       ic.cursor_pos_changed = true;
518       return 1;
519     }
520
521     internal class Candidates
522     {
523       private class Block
524       {
525         public Block Prev, Next;
526         public int Index;
527         public object Data;
528
529         public Block (Block prev, int index, MPlist plist)
530         {
531           Prev = prev;
532           if (prev != null)
533             prev.Next = this;
534           Index = index;
535           Data = plist.IsMText ? plist.Text : plist.Plist;
536         }
537
538         public int Count
539         {
540           get { return (data is MText
541                         ? ((MText) data).Length
542                         : ((MPlist) data).Count); }
543         }
544
545         public Block First
546         {
547           get {
548             Block b;
549             for (b = this; b.Prev != null; b = n.Prev);
550             return b;
551           }
552         }
553
554         public Block Last
555         {
556           get {
557             Block b;
558             for (b = this; b.Next != null; b = n.Next);
559             return b;
560           }
561         }
562
563         public object this[int i]
564         {
565           get { return (data is MText
566                         ? ((MText) data)[i]
567                         : ((MPlist) data)[i]); }
568         }
569       }
570
571       private Block block;
572       private int current_index = -1;
573       private int total_count = 0;
574       private object[] group = null;
575
576       public Candidates (MPlist list, int column)
577       {
578         if (column > 0)
579           group = new object[column];
580         Block b = null;
581         int index;
582         for (index = 0; ! list.IsEmpty; list = list.next)
583           {
584             b = new Block (b, index, list);
585             if (index == 0)
586               block = b;
587             index += b.Count;
588           }
589         total_count = index;
590       }
591
592       public int InGroupIndex
593       {
594         get {
595           return (group == null
596                   ? current_index - block.Index
597                   : current_index % group.Length);
598         }
599       }
600
601       public static void Detach (MInputContext ic)
602       {
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;
608       }
609
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".
614
615       private object fill_group (int index, out int nitems)
616       {
617         int column = group.Length;
618
619         if (index > block.Index + column)
620           block = block.Last;
621         while (index < block.Index)
622           block = block.Prev;
623
624         Block b = block;
625         int count = b.Count;
626         int inblock = index - b.Index;
627         for (nitems = 0; ntimes < column; ntimes++)
628           {
629             group[nitems] = b[inblock++];
630             if (inblock >= count)
631               {
632                 b = b.Next;
633                 if (b == null)
634                   break;
635                 count = b.Count;
636                 inblock = 0;
637               }
638           }
639         return group;
640       }
641
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
646       // that group.
647
648       public object PrevGroup (out int nitems, out int ingroup)
649       {
650         object val;
651
652         ingroup = InGroupIndex;
653         if (group == null)
654           {
655             block = (block.Prev != null) ? block.Prev : block.Last;
656             current_index = block.Index;
657             nitems = block.Count;
658             val = block.Data;
659           }
660         else
661           {
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);
667           }
668         if (ingroup >= nitems)
669           ingroup = nitems - 1;
670         current_index += ingroup;
671         return val;
672       }
673
674       public object NextGroup (out int nitems)
675       {
676         int ingroup = InGroupIndex;
677         object val;
678
679         nitems = 0;
680         if (group == null)
681           {
682             block = (block.Next != null) ? block.Next : block.First;
683             current_index = block.Index;
684             nitems = block.Count;
685             val = block.Data;
686           }
687         else
688           {
689             nitems = group.Length;
690             current_index += column - ingroup;
691             if (current_index >= total_count)
692               current_index = 0;
693             val = fill_group (current_index, out nitems);
694           }
695         if (ingroup >= nitems)
696           ingroup = nitems - 1;
697         current_index += ingroup;
698         return val;
699       }
700
701       public object Next (out int nitems)
702       {
703         int ingroup = InGroupIndex + 1;
704         object val;
705         int index = current_index + 1;
706
707         nitems = group == null ? block.Count : group.Length;
708
709         if (ingroup >= nitems)
710           {
711             val = NextGroup (out nitems);
712             current_index = index < total_count ? index : 0;
713             return val;
714           }
715         if (group == null)
716           {
717             nitems = block.Count;
718             val = block.Data;
719           }
720         else
721           {
722             nitems = group.Length;
723             val = group;
724           }
725         current_index++;
726         return val;
727       }
728
729       public object Prev (out int nitems)
730       {
731         int ingroup = InGroupIndex - 1;
732         object val;
733         int index = current_index - 1;
734
735         if (ingroup < 0)
736           {
737             val = PrevGroup (out nitems);
738             current_index = index >= 0 ? index : total_count - 1;
739             return val;
740           }
741         if (group == null)
742           {
743             nitems = block.Count;
744             val = block.Data;
745           }
746         else
747           {
748             nitems = group.Length;
749             val = group;
750           }
751         current_index--;
752         return val;
753       }
754
755       public object First (out int nitems)
756       {
757         if (group == null)
758           {
759             nitems = block.Count;
760             current_index = block.Index;
761             return block.Data;
762           }
763         nitems = group.Length;
764         current_index = (current_index / nitems) * nitems;
765         return group;
766       }
767
768       public object Last (out int nitems)
769       {
770         if (group == null)
771           {
772             nitems = block.Count;
773             current_index = block.Index + block.Count - 1;;
774             return block.Data;
775           }
776         nitems = group.Length;
777         current_index = (current_index / nitems) * nitems + nitems - 1;
778         return group;
779       }
780
781       public object Select (int index, out int ingroup, out int len)
782       {
783         if (index < current_index)
784
785
786         MPlist prev;
787         return find_group (index, out ingroup_index, out text_len, out prev);
788       }
789
790       private MPlist find_group (int index, out int ingroup_index,
791                                  out int text_len, out MPlist previous)
792       {
793         MPlist p;
794         int i = 0;
795
796         for (p = list, previous = null; ! p.IsEmpty; previous = p, p = p.next)
797           {
798             int len = p.IsMText ? p.Text.Length : p.Plist.Count;
799             if (index < i + len)
800               break;
801             i += len;
802           }
803         ingroup_index = index - i;
804         if (p.IsMText)
805           text_len = 1;
806         else
807           text_len = p.Plist[ingroup_index].Text.Length;
808         return p;
809       }
810
811       private void Update (MInputContext ic, object group, int ingroup)
812       {
813         int from, to, len;
814
815         if (current_index == index)
816           return;
817         if (ic.candidates == null)
818           {
819             from = ic.cursor_pos;
820             to = ic.cursor_pos;
821             ic.candidates = this;
822           }
823         else
824           {
825             from = ic.candidate_from;
826             to = ic.candidate_to;
827           }
828         group = 
829         p = find_group (index, out ingroup, out len, out prev);
830         to = from + len;
831         if (p.IsMText)
832           preedit_replace (ic, from, to, p.Text[ingroup]);
833         else
834           preedit_replace (ic, from, to, p.Plist[ingroup].Text);
835         ic.preedit.PushProp (from, to, Mcandidates, this);
836         ic.cursor_pos = to;
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;
842       }
843
844       private void Update (MInputContext ic, int index)
845       {
846         int from, to, len;
847         object group;
848
849         if (current_index == index)
850           return;
851         if (ic.candidates == null)
852           {
853             from = ic.cursor_pos;
854             to = ic.cursor_pos;
855             ic.candidates = this;
856           }
857         else
858           {
859             from = ic.candidate_from;
860             to = ic.candidate_to;
861           }
862         group = 
863         p = find_group (index, out ingroup, out len, out prev);
864         to = from + len;
865         if (p.IsMText)
866           preedit_replace (ic, from, to, p.Text[ingroup]);
867         else
868           preedit_replace (ic, from, to, p.Plist[ingroup].Text);
869         ic.preedit.PushProp (from, to, Mcandidates, this);
870         ic.cursor_pos = to;
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;
876       }
877
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) {}
885
886     }
887
888     private static object insert_candidates (object[] args, MPlist bindings)
889     {
890       MInputContext ic = (MInputContext) args[0];
891       MPlist list = (MPlist) args[1];
892       int column = 0;
893
894       MPlist slot = (MPlist) bindings.Find (Mcandidates_group_size);
895       if (slot != null)
896         column = slot.Integer;
897       Candidates candidtes = new Candidates (list);
898       candidates.Update (ic, 0);
899       return 1;
900     }
901
902     private static object select (object[] args, MPlist bindings)
903     {
904       MInputContext ic = (MInputContext) args[0];
905       object arg = args[1];
906
907       if (ic.candidates == null)
908         return 0;
909       if (arg is MSymbol)
910         {
911           MSymbol sym = (MSymbol) arg;
912
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);
925         }
926       else if (arg is int)
927         ic.candidates.SelectInGroup (ic, (int) arg);
928       return 0;
929     }
930
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; }
943   }
944
945   public class MInputContext
946   {
947     public MInputMethod im;
948     public MText produced;
949     public bool active;
950     public MText status;
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;
963
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 ();
973     private MPlist vars;
974     private MPlist vars_saved;
975     private MText preceding_text;
976     private MText following_text;
977     private bool key_unhandled;
978
979     internal MPlist GetCandidates (out int ingroup_index, out int text_len)
980     {
981       ingroup_index = text_len = 0;
982       if (cursor_pos == 0)
983         return null;
984       MInputMethod.Candidates candidates
985         = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
986                                                      MInputMethod.Mcandidates);
987       if (candidates == null)
988         return null;
989       return candidates.FindGroup (candidate_index,
990                                    out ingroup_index, out text_len);
991     }
992   }
993 }