*** 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     private static MSymbol Minsert = MSymbol.Of ("insert");
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 ("#{0: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           {
100             MSymbol sym = keys[index];
101             submaps[sym] = sub = new Map ();
102           }
103         if (index + 1 < keys.Length)
104           sub.Add (keys, index + 1, actions);
105         else
106           this.actions = actions;
107       }
108
109       public MExpression Lookup (KeySeq keys, int index)
110       {
111         Map sub;
112
113         if (index + 1 == keys.Length)
114           return actions;
115         if (submaps.TryGetValue (keys[index], out sub))
116           return sub.Lookup (keys, index + 1);
117         return null;
118       }
119     }
120
121     internal class State
122     {
123       public MSymbol name;
124       public MText title;
125       public Dictionary<MSymbol, MExpression> branches
126         = new Dictionary<MSymbol, MExpression> ();
127     }
128
129     private static Dictionary<MDatabase.Tag, MInputMethod> im_table
130       = new Dictionary<MDatabase.Tag, MInputMethod> ();
131
132     private static MExpression.FunctionTable global_table
133       = new MExpression.FunctionTable ();
134
135     public readonly MSymbol language;
136     public readonly MSymbol name;
137     public readonly MSymbol subname;
138
139     internal MExpression.FunctionTable local_table
140       = new MExpression.FunctionTable (global_table);
141     internal MDatabase mdb;
142     internal MText description;
143     internal MText title;
144     internal Command[] commands;
145     internal Variable[] variables;
146     internal MPlist bindings;
147     internal Dictionary<MSymbol, Map> maps
148       = new Dictionary<MSymbol, Map> ();
149     internal State init_state;
150     internal Dictionary<MSymbol, State> states
151       = new Dictionary<MSymbol, State> ();
152     internal MPlist externals;
153
154     static MInputMethod ()
155     {
156       MExpression.Defun (global_table, "insert",
157                          new MExpression.Evaluator (insert),
158                          1, 1, typeof (MExpression));
159       MExpression.Defun (global_table, "candidates",
160                          new MExpression.Evaluator (insert_candidates),
161                          1, 1, typeof (object));
162       MExpression.Defun (global_table, "delete",
163                          new MExpression.Evaluator (delete),
164                          1, 1, typeof (object));
165       MExpression.Defun (global_table, "select",
166                          new MExpression.Evaluator (select),
167                          1, 1, typeof (object));
168       MExpression.Defun (global_table, "show",
169                          new MExpression.Evaluator (show),
170                          0, 0);
171       MExpression.Defun (global_table, "hide",
172                          new MExpression.Evaluator (hide),
173                          0, 0);
174       MExpression.Defun (global_table, "move",
175                          new MExpression.Evaluator (move),
176                          1, 1, typeof (object));
177       MExpression.Defun (global_table, "mark",
178                          new MExpression.Evaluator (mark),
179                          1, 1, typeof (MSymbol));
180       MExpression.Defun (global_table, "pushback",
181                          new MExpression.Evaluator (pushback),
182                          1, 1, typeof (object));
183       MExpression.Defun (global_table, "pop",
184                          new MExpression.Evaluator (pop),
185                          0, 0);
186       MExpression.Defun (global_table, "undo",
187                          new MExpression.Evaluator (undo),
188                          0, 1, typeof (object));
189       MExpression.Defun (global_table, "commit",
190                          new MExpression.Evaluator (commit),
191                          0, 0);
192       MExpression.Defun (global_table, "unhandle",
193                          new MExpression.Evaluator (unhandle),
194                          0, 0);
195       MExpression.Defun (global_table, "shift",
196                          new MExpression.Evaluator (shift),
197                          1, 1, typeof (MSymbol));
198       MExpression.Defun (global_table, "call",
199                          new MExpression.Evaluator (call),
200                          2, -1, typeof (MSymbol), typeof (MSymbol),
201                          typeof (object));
202     }
203
204     private MInputMethod (MDatabase.Tag tag)
205     {
206       mdb = MDatabase.Find (tag);
207       if (mdb == null)
208         throw new Exception (String.Format ("Input method {0} not available",
209                                             tag));
210       language = tag[1];
211       name = tag[2];
212       subname = tag[3];
213       MPlist plist = (MPlist) mdb.Load ();
214       if (plist == null)
215         return;
216       for (; ! plist.IsEmpty; plist = plist.next)
217         if (plist.IsPlist)
218           {
219             MPlist pl = plist.Plist;
220             if (pl.IsSymbol)
221               {
222                 MSymbol sym = pl.Symbol;
223                 pl = pl.next;
224                 if (sym == Mdescription)
225                   {
226                     description = parse_description (pl);
227                     if (description == null)
228                       description = new MText ("No description");
229                   }
230                 else if (sym == Mtitle)
231                   {
232                     if (pl.IsMText)
233                       title = pl.Text;
234                   }
235                 else if (sym == Mvariable)
236                   parse_variables (pl);
237                 else if (sym == Mcommand)
238                   parse_commands (pl);
239                 else if (sym == Minclude)
240                   parse_include (pl);
241                 else if (sym == Mmacro)
242                   parse_macros (pl);
243                 else if (sym == Mmap)
244                   parse_maps (pl);
245                 else if (sym == Mstate)
246                   parse_states (pl);
247               }
248           }
249     }
250
251     private static MText parse_description (MPlist plist)
252     {
253       return (plist.IsMText ? plist.Text
254               : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
255               : null);
256     }
257
258     private void parse_variables (MPlist plist)
259     {
260       variables = new Variable[plist.Count];
261
262       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
263         if (plist.IsPlist && plist.Plist.IsSymbol)
264           {
265             Variable var = new Variable ();
266             MPlist p = plist.Plist;
267
268             var.name = p.Symbol;
269             p = p.Next;
270             var.description = parse_description (p);
271             if (var.description == null)
272               var.description = new MText ("No description");
273             else
274               p = p.next;
275             var.type = (p.IsMText ? typeof (MText)
276                         : p.IsInteger ? typeof (int)
277                         : p.IsSymbol ? typeof (MSymbol)
278                         : typeof (object));
279             var.value = p.val;
280             var.candidates = p.next;
281             variables[i++] = var;
282           }
283     }
284
285     private void parse_commands (MPlist plist)
286     {
287       commands = new Command[plist.Count];
288
289       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
290         if (plist.IsPlist && plist.Plist.IsSymbol)
291           {
292             Command cmd = new Command ();
293             MPlist p = plist.Plist;
294
295             cmd.name = p.Symbol;
296             p = p.Next;
297             cmd.description = parse_description (p);
298             if (cmd.description == null)
299               cmd.description = new MText ("No description");
300             else
301               p = p.next;
302             KeySeq[] keys = new KeySeq[p.Count];
303             for (int j = 0; ! p.IsEmpty; p = p.next)
304               {
305                 if (p.IsMText)
306                   keys[j++] = new KeySeq (p.Text);
307                 else if (p.IsPlist)
308                   keys[j++] = new KeySeq (p.Plist);
309               }
310             commands[i++] = cmd;
311           }
312     }
313
314     private void parse_include (MPlist plist)
315     {
316       if (! plist.IsPlist)
317         return;
318       MPlist p = plist.Plist;
319       MSymbol language, name, extra;
320       language = p.Symbol;
321       p = p.next;
322       if (! p.IsSymbol)
323         name = extra = MSymbol.nil;
324       else
325         {
326           name = p.Symbol;
327           p = p.next;
328           if (! p.IsSymbol)
329             extra = MSymbol.nil;
330           else
331             extra = p.Symbol;
332         }
333
334       MInputMethod im = MInputMethod.Get (language, name, extra);
335       if (im == null)
336         return;
337       plist = plist.next;
338       if (! plist.IsSymbol)
339         return;
340       MSymbol target_type = plist.Symbol;
341       plist = plist.next;
342       MSymbol target_name = MSymbol.nil;
343       if (plist.IsSymbol)
344         target_name = plist.Symbol;
345       if (target_type == Mmacro)
346         {
347           if (target_name == MSymbol.nil)
348             im.local_table.Copy (local_table);
349           else
350             im.local_table.Copy (target_name, local_table);
351         }
352       else if (target_type == Mmap)
353         {
354           if (target_name == MSymbol.nil)
355             {
356               foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
357                 maps[kv.Key] = kv.Value;
358             }
359           else
360             {
361               Map map;
362               if (im.maps.TryGetValue (target_name, out map))
363                 maps[target_name] = map;
364             }
365         }
366       else if (target_type == Mstate)
367         {
368           if (target_name == MSymbol.nil)
369             {
370               foreach (KeyValuePair<MSymbol, State> kv in im.states)
371                 states[kv.Key] = kv.Value;
372             }
373           else
374             {
375               State state;
376               if (im.states.TryGetValue (target_name, out state))
377                 states[target_name] = state;
378             }
379         }
380     }
381
382     private void parse_macros (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             MSymbol name = pl.Symbol;
392             MExpression expr = new MExpression (pl.next, local_table);
393             MExpression.Defmacro (local_table, name, expr);
394           }
395     }
396
397     private void parse_maps (MPlist plist)
398     {
399       for (; ! plist.IsEmpty; plist = plist.next)
400         if (plist.IsPlist)
401           {
402             MPlist pl = plist.Plist;
403           
404             if (! pl.IsSymbol)
405               continue;
406             Map map = new Map ();
407             map.name = pl.Symbol;
408             maps[map.name] = map;
409             for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
410               {
411                 if (! pl.IsPlist)
412                   continue;
413                 MPlist p = pl.Plist;
414                 KeySeq keys;
415                 if (p.IsMText)
416                   keys = new KeySeq (p.Text);
417                 else if (p.IsPlist)
418                   keys = new KeySeq (p.Plist);
419                 else
420                   continue;
421                 if (keys.keys.Length == 0
422                     && keys.keys[0] == null)
423                   continue;
424                 p = p.next;
425                 if (p.IsEmpty)
426                   continue;
427                 MExpression expr = new MExpression (p, local_table);
428                 map.Add (keys, 0, expr);
429               }
430           }
431     }
432
433     private void parse_states (MPlist plist)
434     {
435       for (; ! plist.IsEmpty; plist = plist.next)
436         if (plist.IsPlist)
437           {
438             MPlist pl = plist.Plist;
439             MText title = null;
440           
441             if (pl.IsMText)
442               {
443                 title = pl.Text;
444                 pl = pl.next;
445               }
446             if (! pl.IsSymbol)
447               continue;
448
449             State state = new State ();
450             state.name = pl.Symbol;
451             state.title = title;
452             states[state.name] = state;
453             if (init_state == null)
454               init_state = state;
455             for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
456               {
457                 if (! pl.IsPlist)
458                   continue;
459                 MPlist p = pl.Plist;
460                 if (! p.IsSymbol)
461                   continue;
462                 MSymbol map_name = p.Symbol;
463                 p = p.next;
464                 state.branches[map_name] = new MExpression (p, local_table);
465               }
466           }
467     }
468
469     public static MInputMethod Get (MSymbol language, MSymbol name,
470                                     MSymbol extra)
471     {
472       MDatabase.Tag tag
473         = new MDatabase.Tag (Minput_method, language, name, extra);
474       MInputMethod im;
475       if (im_table.TryGetValue (tag, out im))
476         return im;
477       try {
478         im = new MInputMethod (tag);
479       } catch (Exception e) {
480         Console.WriteLine (e);
481         im = null;
482       }
483       return im;
484     }
485
486     private static void adjust_markers (MInputContext ic,
487                                         int from, int to, object inserted)
488     {
489       int ins = (inserted == null ? 0
490                  : inserted is int ? 1
491                  : ((MText) inserted).Length);
492       int diff = ins - (to - from);
493
494       for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
495         {
496           int pos = plist.Integer;
497           if (pos > from)
498             {
499               if (pos >= to)
500                 plist.val = pos + diff;
501               else
502                 plist.val = from;
503             }
504         }
505       if (ic.cursor_pos >= to)
506         ic.cursor_pos += diff;
507       else if (ic.cursor_pos > from)
508         ic.cursor_pos = from;
509     }
510
511     private static void preedit_replace (MInputContext ic,
512                                          int from, int to, int c)
513     {
514       ic.preedit.Del (from, to);
515       ic.preedit.Ins (from, c);
516       adjust_markers (ic, from, to, c);
517     }
518
519     private static void preedit_replace (MInputContext ic,
520                                          int from, int to, MText mt)
521     {
522       ic.preedit[from, to] = mt;
523       adjust_markers (ic, from, to, mt);
524     }
525
526     private static object insert (object[] args, MPlist bindings,
527                                   object context)
528     {
529       MInputContext ic = (MInputContext) context;
530       object arg = ((MExpression) args[0]).Eval (bindings, ic);
531
532       if (arg is int)
533         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
534       else
535         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
536       ic.preedit_changed = true;
537       ic.cursor_pos_changed = true;
538       return 1;
539     }
540
541     internal class Candidates
542     {
543       private class Block
544       {
545         public int Index;
546         public object Data;
547
548         public Block (int index, MPlist plist)
549         {
550           Index = index;
551           if (plist.IsMText)
552             Data = plist.Text;
553           else
554             Data = plist.Plist;
555         }
556
557         public int Count
558         {
559           get { return (Data is MText
560                         ? ((MText) Data).Length
561                         : ((MPlist) Data).Count); }
562         }
563
564         public object this[int i]
565         {
566           get {
567             if (Data is MText) return ((MText) Data)[i];
568             return  ((MPlist) Data)[i];
569           }
570         }
571       }
572
573       private Block[] blocks;
574       private int row = 0;
575       private int index = 0;
576       public object[] group;
577
578       private bool IsFixed { get { return group != null; } }
579       private int Total {
580         get {
581           Block last = blocks[blocks.Length - 1];
582           return last.Index + last.Count; }
583       }
584
585       public int Column {
586         get { return (IsFixed ? index % group.Length
587                       : index - blocks[row].Index); }
588       }
589
590       public object Group {
591         get { return (IsFixed ? group : blocks[row].Data); }
592       }
593
594       public int GroupLength
595       {
596         get {
597           if (IsFixed)
598             {
599               int nitems = group.Length;
600               int start = index - (index % nitems);
601               int total = Total;
602               return (start + nitems <= total ? nitems : total - start);
603             }
604           return blocks[row].Count;
605         }
606       }
607
608       public object Current {
609         get {
610           return (IsFixed ? group[index % group.Length]
611                   : blocks[row][index - blocks[row].Index]);
612         }
613       }
614
615       public Candidates (MPlist list, int column)
616       {
617         int nblocks = list.Count;
618
619         blocks = new Block[nblocks];
620         for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
621           start += (blocks[i] = new Block (index, list)).Count;
622         if (column > 0)
623           group = new object[column];
624       }
625
626       public static void Detach (MInputContext ic)
627       {
628         ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
629         ic.candidates = null;
630         ic.preedit_changed = true;
631         ic.cursor_pos_changed = true;
632         ic.candidate_changed = true;
633       }
634
635       // Fill the array "group" by candidates stating from INDEX.
636       // INDEX must be a multiple of "column".  Set NTIMES to the
637       // number of valid candidates in "group".  Update "block" if
638       // necessary.  Return "group".
639
640       private int fill_group (int start)
641       {
642         int nitems = group.Length;
643         int r = row;
644         Block b = blocks[r];
645
646         if (start < b.Index)
647           while (start < b.Index)
648             b = blocks[--r];
649         else
650           while (start >= b.Index + b.Count)
651             b = blocks[++r];
652         row = r;
653
654         int count = b.Count;
655         start -= b.Index;
656         for (int i = 0; i < nitems; i++, start++)
657           {
658             if (start >= count)
659               {
660                 r++;
661                 if (r == blocks.Length)
662                   return i;
663                 b = blocks[r];
664                 count = b.Count;
665                 start = 0;
666               }
667             group[i] = b[start];
668           }
669         return nitems;
670       }
671
672       // Update "row" to what contains the first candidate of
673       // the previous candidate-group, update "current_index", and
674       // update "group" if necessary.  Return the previous
675       // candidate-group.  Set NITEMS to the number of valid
676       // candidates contained in that group.
677
678       public int PrevGroup ()
679       {
680         int nitems;
681         int col = Column;
682
683         if (IsFixed)
684           {
685             nitems = group.Length;
686             if ((index -= col + nitems) < 0)
687               index = (Total / nitems) * nitems;
688             nitems = fill_group (index);
689           }
690         else
691           {
692             row = row > 0 ? row-- : blocks.Length - 1;
693             nitems = blocks[row].Count;
694             index = blocks[row].Index;
695           }
696         index += col < nitems ? col : nitems - 1;
697         return nitems;
698       }
699
700       public int NextGroup ()
701       {
702         int nitems;
703         int col = Column;
704
705         if (IsFixed)
706           {
707             nitems = group.Length;
708             if ((index += nitems - col) >= Total)
709               index = 0;
710             nitems = fill_group (index);
711           }
712         else
713           {
714             row = row < blocks.Length - 1 ? row + 1 : 0;
715             nitems = blocks[row].Count;
716             index = blocks[row].Count;
717           }
718         index += col < nitems ? col : nitems - 1;
719         return nitems;
720       }
721
722       public void Prev ()
723       {
724         int col = Column;
725
726         if (col == 0)
727           {
728             int nitems = PrevGroup ();
729             index += col < nitems - 1 ? col : nitems - 1;
730           }
731         else
732           index--;
733       }
734
735       public void Next ()
736       {
737         int col = Column;
738         int nitems = GroupLength;
739
740         if (col == nitems - 1)
741           {
742             nitems = NextGroup ();
743             index -= Column;
744           }
745         else
746           index++;
747       }
748
749       public void First ()
750       {
751         index -= Column;
752       }
753
754       public void Last ()
755       {
756         index += GroupLength - (Column + 1);
757       }
758
759       public void Select (int col)
760       {
761         int maxcol = GroupLength - 1;
762         if (col > maxcol)
763           col = maxcol;
764         index = index - Column + col;
765       }
766
767       public void Update (MInputContext ic)
768       {
769         int from, to;
770
771         if (ic.candidates == null)
772           {
773             from = ic.cursor_pos;
774             to = ic.cursor_pos;
775             ic.candidates = this;
776           }
777         else
778           {
779             from = ic.candidate_from;
780             to = ic.candidate_to;
781           }
782
783         object candidate = ic.candidates.Current;
784
785         if (candidate is MText)
786           preedit_replace (ic, from, to, (MText) candidate);
787         else
788           preedit_replace (ic, from, to, (int) candidate);
789         ic.preedit.PushProp (from, to, Mcandidates, this);
790         ic.cursor_pos = to;
791         ic.candidate_from = from;
792         ic.candidate_to = to;
793         ic.preedit_changed = true;
794         ic.cursor_pos_changed = true;
795         ic.candidate_changed = true;
796       }
797     }
798
799     private static object insert_candidates (object[] args, MPlist bindings,
800                                              object context)
801     {
802       MInputContext ic = (MInputContext) context;
803       MPlist list = (MPlist) args[0];
804       int column = 0;
805
806       MPlist slot = (MPlist) bindings.Find (Mcandidates_group_size);
807       if (slot != null)
808         column = slot.Integer;
809       Candidates candidates = new Candidates (list, column);
810       candidates.Update (ic);
811       return 1;
812     }
813
814     private static object select (object[] args, MPlist bindings,
815                                   object context)
816     {
817       MInputContext ic = (MInputContext) context;
818       object arg = args[0];
819
820       if (ic.candidates == null)
821         return 0;
822       if (arg is MSymbol)
823         {
824           MSymbol sym = (MSymbol) arg;
825
826           if (sym == Mat_less_than)
827             ic.candidates.First ();
828           else if (sym == Mat_greater_than)
829             ic.candidates.Last ();
830           else if (sym == Mat_minus)
831             ic.candidates.Prev ();
832           else if (sym == Mat_plus)
833             ic.candidates.Next ();
834           else if (sym == Mat_open_square_bracket)
835             ic.candidates.PrevGroup ();
836           else if (sym == Mat_close_square_bracket)
837             ic.candidates.NextGroup ();
838           else
839             return 0;
840         }
841       else if (arg is int)
842         ic.candidates.Select ((int) arg);
843       ic.candidates.Update (ic);
844       return 0;
845     }
846
847     private static object delete (object[] args, MPlist bindings,
848                                   object context) { return 1; }
849     private static object show (object[] args, MPlist bindings,
850                                 object context) { return 1; }
851     private static object hide (object[] args, MPlist bindings,
852                                 object context) { return 1; }
853     private static object move (object[] args, MPlist bindings,
854                                 object context) { return 1; }
855     private static object mark (object[] args, MPlist bindings,
856                                 object context) { return 1; }
857     private static object pushback (object[] args, MPlist bindings,
858                                     object context) { return 1; }
859     private static object pop (object[] args, MPlist bindings,
860                                object context) { return 1; }
861     private static object undo (object[] args, MPlist bindings,
862                                 object context) { return 1; }
863     private static object commit (object[] args, MPlist bindings,
864                                   object context) { return 1; }
865     private static object unhandle (object[] args, MPlist bindings,
866                                     object context) { return 1; }
867     private static object shift (object[] args, MPlist bindings,
868                                  object context) { return 1; }
869     private static object call (object[] args, MPlist bindings,
870                                 object context) { return 1; }
871   }
872
873   public class MInputContext
874   {
875     public MInputMethod im;
876     public MText produced;
877     public bool active;
878     public MText status;
879     public bool status_changed;
880     public MText preedit;
881     public bool preedit_changed;
882     public int cursor_pos;
883     public bool cursor_pos_changed;
884     internal MInputMethod.Candidates candidates;
885     public MPlist candidate_group;
886     public int candidate_index;
887     public int candidate_from, candidate_to;
888     public bool candidate_show;
889     public bool candidate_changed;
890     public MPlist callback_args;
891
892     private MInputMethod.State state;
893     private MInputMethod.State prev_state;
894     private Stack<MSymbol> keys;
895     private int state_key_head;
896     private int key_head;
897     private int commit_key_head;
898     private MText preedit_saved;
899     private int state_pos;
900     internal MPlist markers = new MPlist ();
901     private MPlist vars;
902     private MPlist vars_saved;
903     private MText preceding_text;
904     private MText following_text;
905     private bool key_unhandled;
906
907     internal object GetCandidates (out int column)
908     {
909       column = 0;
910       if (cursor_pos == 0)
911         return null;
912       MInputMethod.Candidates candidates
913         = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
914                                                      MInputMethod.Mcandidates);
915       if (candidates == null)
916         return null;
917       column = candidates.Column;
918       return candidates.Current;
919     }
920   }
921 }