*** 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     internal static MExpression.Domain domain = new MExpression.Domain (null);
14
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");
33
34     internal class Variable
35     {
36       public MSymbol name;
37       public MText description;
38       public Type type;
39       public object value;
40       public MPlist candidates;
41     }
42
43     internal class Command
44     {
45       public MSymbol name;
46       public MText description;
47       public MSymbol[][] keys;
48     }
49
50     internal class KeySeq
51     {
52       public MSymbol[] keys;
53
54       private static MSymbol char_to_symbol (int c)
55       {
56         return MSymbol.Of (String.Format ("#{0:X}", c));
57       }
58
59       public KeySeq (MPlist plist)
60       {
61         keys = new MSymbol[plist.Count];
62         int i = 0;
63         foreach (MPlist p in plist)
64           {
65             if (p.IsSymbol)
66               keys[i++] = p.Symbol;
67             else if (p.IsInteger)
68               keys[i++] = char_to_symbol (p.Integer);
69             else
70               keys[i] = null;
71           }
72       }
73
74       public KeySeq (MText mt)
75       {
76         keys = new MSymbol[mt.Length];
77         for (int i = 0; i < mt.Length; i++)
78           keys[i] = char_to_symbol (mt[i]);
79       }
80
81       public MSymbol this[int i] { get { return keys[i]; } }
82
83       public int Length { get { return keys.Length; } }
84     }
85
86     internal class Map
87     {
88       public MSymbol name;
89       public Dictionary<MSymbol, Map> submaps;
90       public MExpression actions;
91
92       public void Add (KeySeq keys, int index, MExpression actions)
93       {
94         Map sub = null;
95
96         if (submaps == null)
97           submaps = new Dictionary<MSymbol, Map> ();
98         else
99           submaps.TryGetValue (keys[index], out sub);
100         if (sub == null)
101           {
102             MSymbol sym = keys[index];
103             submaps[sym] = sub = new Map ();
104           }
105         if (index + 1 < keys.Length)
106           sub.Add (keys, index + 1, actions);
107         else
108           this.actions = actions;
109       }
110
111       public MExpression Lookup (KeySeq keys, int index)
112       {
113         Map sub;
114
115         if (index + 1 == keys.Length)
116           return actions;
117         if (submaps.TryGetValue (keys[index], out sub))
118           return sub.Lookup (keys, index + 1);
119         return null;
120       }
121     }
122
123     internal class State
124     {
125       public MSymbol name;
126       public MText title;
127       public Dictionary<MSymbol, MExpression> branches
128         = new Dictionary<MSymbol, MExpression> ();
129     }
130
131     private static Dictionary<MDatabase.Tag, MInputMethod> im_table
132       = new Dictionary<MDatabase.Tag, MInputMethod> ();
133
134     public readonly MSymbol language;
135     public readonly MSymbol name;
136     public readonly MSymbol subname;
137
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;
150
151     static MInputMethod ()
152     {
153       domain.Defun ("insert", new MExpression.Evaluator (insert), 1, 1);
154       domain.Defun ("candidates", new MExpression.Evaluator (insert_candidates),
155                     1, 1);
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);
169     }
170
171     private MInputMethod (MDatabase.Tag tag)
172     {
173       mdb = MDatabase.Find (tag);
174       if (mdb == null)
175         throw new Exception (String.Format ("Input method {0} not available",
176                                             tag));
177       language = tag[1];
178       name = tag[2];
179       subname = tag[3];
180       MPlist plist = (MPlist) mdb.Load ();
181       if (plist == null)
182         return;
183       for (; ! plist.IsEmpty; plist = plist.next)
184         if (plist.IsPlist)
185           {
186             MPlist pl = plist.Plist;
187             if (pl.IsSymbol)
188               {
189                 MSymbol sym = pl.Symbol;
190                 pl = pl.next;
191                 if (sym == Mdescription)
192                   {
193                     description = parse_description (pl);
194                     if (description == null)
195                       description = new MText ("No description");
196                   }
197                 else if (sym == Mtitle)
198                   {
199                     if (pl.IsMText)
200                       title = pl.Text;
201                   }
202                 else if (sym == Mvariable)
203                   parse_variables (pl);
204                 else if (sym == Mcommand)
205                   parse_commands (pl);
206                 else if (sym == Minclude)
207                   parse_include (pl);
208                 else if (sym == Mmacro)
209                   parse_macros (pl);
210                 else if (sym == Mmap)
211                   parse_maps (pl);
212                 else if (sym == Mstate)
213                   parse_states (pl);
214               }
215           }
216     }
217
218     private static MText parse_description (MPlist plist)
219     {
220       return (plist.IsMText ? plist.Text
221               : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
222               : null);
223     }
224
225     private void parse_variables (MPlist plist)
226     {
227       variables = new Variable[plist.Count];
228
229       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
230         if (plist.IsPlist && plist.Plist.IsSymbol)
231           {
232             Variable var = new Variable ();
233             MPlist p = plist.Plist;
234
235             var.name = p.Symbol;
236             p = p.Next;
237             var.description = parse_description (p);
238             if (var.description == null)
239               var.description = new MText ("No description");
240             else
241               p = p.next;
242             var.type = (p.IsMText ? typeof (MText)
243                         : p.IsInteger ? typeof (int)
244                         : p.IsSymbol ? typeof (MSymbol)
245                         : typeof (object));
246             var.value = p.val;
247             var.candidates = p.next;
248             variables[i++] = var;
249           }
250     }
251
252     private void parse_commands (MPlist plist)
253     {
254       commands = new Command[plist.Count];
255
256       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
257         if (plist.IsPlist && plist.Plist.IsSymbol)
258           {
259             Command cmd = new Command ();
260             MPlist p = plist.Plist;
261
262             cmd.name = p.Symbol;
263             p = p.Next;
264             cmd.description = parse_description (p);
265             if (cmd.description == null)
266               cmd.description = new MText ("No description");
267             else
268               p = p.next;
269             KeySeq[] keys = new KeySeq[p.Count];
270             for (int j = 0; ! p.IsEmpty; p = p.next)
271               {
272                 if (p.IsMText)
273                   keys[j++] = new KeySeq (p.Text);
274                 else if (p.IsPlist)
275                   keys[j++] = new KeySeq (p.Plist);
276               }
277             commands[i++] = cmd;
278           }
279     }
280
281     private void parse_include (MPlist plist)
282     {
283       if (! plist.IsPlist)
284         return;
285       MPlist p = plist.Plist;
286       MSymbol language, name, extra;
287       language = p.Symbol;
288       p = p.next;
289       if (! p.IsSymbol)
290         name = extra = MSymbol.nil;
291       else
292         {
293           name = p.Symbol;
294           p = p.next;
295           if (! p.IsSymbol)
296             extra = MSymbol.nil;
297           else
298             extra = p.Symbol;
299         }
300
301       MInputMethod im = MInputMethod.Get (language, name, extra);
302       if (im == null)
303         return;
304       plist = plist.next;
305       if (! plist.IsSymbol)
306         return;
307       MSymbol target_type = plist.Symbol;
308       plist = plist.next;
309       MSymbol target_name = MSymbol.nil;
310       if (plist.IsSymbol)
311         target_name = plist.Symbol;
312       if (target_type == Mmacro)
313         {
314           if (target_name == MSymbol.nil)
315             im.domain.Copy (local_table);
316           else
317             im.local_table.Copy (target_name, local_table);
318         }
319       else if (target_type == Mmap)
320         {
321           if (target_name == MSymbol.nil)
322             {
323               foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
324                 maps[kv.Key] = kv.Value;
325             }
326           else
327             {
328               Map map;
329               if (im.maps.TryGetValue (target_name, out map))
330                 maps[target_name] = map;
331             }
332         }
333       else if (target_type == Mstate)
334         {
335           if (target_name == MSymbol.nil)
336             {
337               foreach (KeyValuePair<MSymbol, State> kv in im.states)
338                 states[kv.Key] = kv.Value;
339             }
340           else
341             {
342               State state;
343               if (im.states.TryGetValue (target_name, out state))
344                 states[target_name] = state;
345             }
346         }
347     }
348
349     private void parse_macros (MPlist plist)
350     {
351       for (; ! plist.IsEmpty; plist = plist.next)
352         if (plist.IsPlist)
353           {
354             MPlist pl = plist.Plist;
355
356             if (! pl.IsSymbol)
357               continue;
358             MSymbol name = pl.Symbol;
359             MExpression expr = new MExpression (pl.next, local_table);
360             MExpression.Defmacro (local_table, name, expr);
361           }
362     }
363
364     private void parse_maps (MPlist plist)
365     {
366       for (; ! plist.IsEmpty; plist = plist.next)
367         if (plist.IsPlist)
368           {
369             MPlist pl = plist.Plist;
370           
371             if (! pl.IsSymbol)
372               continue;
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)
377               {
378                 if (! pl.IsPlist)
379                   continue;
380                 MPlist p = pl.Plist;
381                 KeySeq keys;
382                 if (p.IsMText)
383                   keys = new KeySeq (p.Text);
384                 else if (p.IsPlist)
385                   keys = new KeySeq (p.Plist);
386                 else
387                   continue;
388                 if (keys.keys.Length == 0
389                     && keys.keys[0] == null)
390                   continue;
391                 p = p.next;
392                 if (p.IsEmpty)
393                   continue;
394                 MExpression expr = new MExpression (p, local_table);
395                 map.Add (keys, 0, expr);
396               }
397           }
398     }
399
400     private void parse_states (MPlist plist)
401     {
402       for (; ! plist.IsEmpty; plist = plist.next)
403         if (plist.IsPlist)
404           {
405             MPlist pl = plist.Plist;
406             MText title = null;
407           
408             if (pl.IsMText)
409               {
410                 title = pl.Text;
411                 pl = pl.next;
412               }
413             if (! pl.IsSymbol)
414               continue;
415
416             State state = new State ();
417             state.name = pl.Symbol;
418             state.title = title;
419             states[state.name] = state;
420             if (init_state == null)
421               init_state = state;
422             for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
423               {
424                 if (! pl.IsPlist)
425                   continue;
426                 MPlist p = pl.Plist;
427                 if (! p.IsSymbol)
428                   continue;
429                 MSymbol map_name = p.Symbol;
430                 p = p.next;
431                 state.branches[map_name] = new MExpression (p, local_table);
432               }
433           }
434     }
435
436     public static MInputMethod Get (MSymbol language, MSymbol name,
437                                     MSymbol extra)
438     {
439       MDatabase.Tag tag
440         = new MDatabase.Tag (Minput_method, language, name, extra);
441       MInputMethod im;
442       if (im_table.TryGetValue (tag, out im))
443         return im;
444       try {
445         im = new MInputMethod (tag);
446       } catch (Exception e) {
447         Console.WriteLine (e);
448         im = null;
449       }
450       return im;
451     }
452
453     private static void adjust_markers (MInputContext ic,
454                                         int from, int to, object inserted)
455     {
456       int ins = (inserted == null ? 0
457                  : inserted is int ? 1
458                  : ((MText) inserted).Length);
459       int diff = ins - (to - from);
460
461       for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
462         {
463           int pos = plist.Integer;
464           if (pos > from)
465             {
466               if (pos >= to)
467                 plist.val = pos + diff;
468               else
469                 plist.val = from;
470             }
471         }
472       if (ic.cursor_pos >= to)
473         ic.cursor_pos += diff;
474       else if (ic.cursor_pos > from)
475         ic.cursor_pos = from;
476     }
477
478     private static void preedit_replace (MInputContext ic,
479                                          int from, int to, int c)
480     {
481       ic.preedit.Del (from, to);
482       ic.preedit.Ins (from, c);
483       adjust_markers (ic, from, to, c);
484     }
485
486     private static void preedit_replace (MInputContext ic,
487                                          int from, int to, MText mt)
488     {
489       ic.preedit[from, to] = mt;
490       adjust_markers (ic, from, to, mt);
491     }
492
493     private static object insert (MExpression[] args,
494                                   MExpression.Domain domain)
495     {
496       MInputContext ic = (MInputContext) domain.context;
497       object arg = args[0].val;
498
499       if (arg is int)
500         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
501       else
502         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
503       ic.preedit_changed = true;
504       ic.cursor_pos_changed = true;
505       return 1;
506     }
507
508     internal class Candidates
509     {
510       private class Block
511       {
512         public int Index;
513         public object Data;
514
515         public Block (int index, MPlist plist)
516         {
517           Index = index;
518           if (plist.IsMText)
519             Data = plist.Text;
520           else
521             Data = plist.Plist;
522         }
523
524         public int Count
525         {
526           get { return (Data is MText
527                         ? ((MText) Data).Length
528                         : ((MPlist) Data).Count); }
529         }
530
531         public object this[int i]
532         {
533           get {
534             if (Data is MText) return ((MText) Data)[i];
535             return  ((MPlist) Data)[i];
536           }
537         }
538       }
539
540       private Block[] blocks;
541       private int row = 0;
542       private int index = 0;
543       public object[] group;
544
545       private bool IsFixed { get { return group != null; } }
546       private int Total {
547         get {
548           Block last = blocks[blocks.Length - 1];
549           return last.Index + last.Count; }
550       }
551
552       public int Column {
553         get { return (IsFixed ? index % group.Length
554                       : index - blocks[row].Index); }
555       }
556
557       public object Group {
558         get { return (IsFixed ? group : blocks[row].Data); }
559       }
560
561       public int GroupLength
562       {
563         get {
564           if (IsFixed)
565             {
566               int nitems = group.Length;
567               int start = index - (index % nitems);
568               int total = Total;
569               return (start + nitems <= total ? nitems : total - start);
570             }
571           return blocks[row].Count;
572         }
573       }
574
575       public object Current {
576         get {
577           return (IsFixed ? group[index % group.Length]
578                   : blocks[row][index - blocks[row].Index]);
579         }
580       }
581
582       public Candidates (MPlist list, int column)
583       {
584         int nblocks = list.Count;
585
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;
589         if (column > 0)
590           group = new object[column];
591       }
592
593       public static void Detach (MInputContext ic)
594       {
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;
600       }
601
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".
606
607       private int fill_group (int start)
608       {
609         int nitems = group.Length;
610         int r = row;
611         Block b = blocks[r];
612
613         if (start < b.Index)
614           while (start < b.Index)
615             b = blocks[--r];
616         else
617           while (start >= b.Index + b.Count)
618             b = blocks[++r];
619         row = r;
620
621         int count = b.Count;
622         start -= b.Index;
623         for (int i = 0; i < nitems; i++, start++)
624           {
625             if (start >= count)
626               {
627                 r++;
628                 if (r == blocks.Length)
629                   return i;
630                 b = blocks[r];
631                 count = b.Count;
632                 start = 0;
633               }
634             group[i] = b[start];
635           }
636         return nitems;
637       }
638
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.
644
645       public int PrevGroup ()
646       {
647         int nitems;
648         int col = Column;
649
650         if (IsFixed)
651           {
652             nitems = group.Length;
653             if ((index -= col + nitems) < 0)
654               index = (Total / nitems) * nitems;
655             nitems = fill_group (index);
656           }
657         else
658           {
659             row = row > 0 ? row-- : blocks.Length - 1;
660             nitems = blocks[row].Count;
661             index = blocks[row].Index;
662           }
663         index += col < nitems ? col : nitems - 1;
664         return nitems;
665       }
666
667       public int NextGroup ()
668       {
669         int nitems;
670         int col = Column;
671
672         if (IsFixed)
673           {
674             nitems = group.Length;
675             if ((index += nitems - col) >= Total)
676               index = 0;
677             nitems = fill_group (index);
678           }
679         else
680           {
681             row = row < blocks.Length - 1 ? row + 1 : 0;
682             nitems = blocks[row].Count;
683             index = blocks[row].Count;
684           }
685         index += col < nitems ? col : nitems - 1;
686         return nitems;
687       }
688
689       public void Prev ()
690       {
691         int col = Column;
692
693         if (col == 0)
694           {
695             int nitems = PrevGroup ();
696             index += col < nitems - 1 ? col : nitems - 1;
697           }
698         else
699           index--;
700       }
701
702       public void Next ()
703       {
704         int col = Column;
705         int nitems = GroupLength;
706
707         if (col == nitems - 1)
708           {
709             nitems = NextGroup ();
710             index -= Column;
711           }
712         else
713           index++;
714       }
715
716       public void First ()
717       {
718         index -= Column;
719       }
720
721       public void Last ()
722       {
723         index += GroupLength - (Column + 1);
724       }
725
726       public void Select (int col)
727       {
728         int maxcol = GroupLength - 1;
729         if (col > maxcol)
730           col = maxcol;
731         index = index - Column + col;
732       }
733
734       public void Update (MInputContext ic)
735       {
736         int from, to;
737
738         if (ic.candidates == null)
739           {
740             from = ic.cursor_pos;
741             to = ic.cursor_pos;
742             ic.candidates = this;
743           }
744         else
745           {
746             from = ic.candidate_from;
747             to = ic.candidate_to;
748           }
749
750         object candidate = ic.candidates.Current;
751
752         if (candidate is MText)
753           preedit_replace (ic, from, to, (MText) candidate);
754         else
755           preedit_replace (ic, from, to, (int) candidate);
756         ic.preedit.PushProp (from, to, Mcandidates, this);
757         ic.cursor_pos = to;
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;
763       }
764     }
765
766     private static object insert_candidates (MExpression[] args,
767                                              MExpression.Domain domain)
768     {
769       MInputContext ic = (MInputContext) domain.context;
770       MPlist list = (MPlist) args[0];
771       int column = 0;
772
773       if (domain.IsBound (Mcandidates_group_size))
774         {
775           object val = domain.GetValue (Mcandidates_group_size);
776           if (val is int)
777             column = (int) val;
778         }
779       Candidates candidates = new Candidates (list, column);
780       candidates.Update (ic);
781       return 1;
782     }
783
784     private static object select (MExpression[] args,
785                                   MExpression.Domain domain)
786     {
787       MInputContext ic = (MInputContext) domain.context;
788       object arg = args[0].val;
789
790       if (ic.candidates == null)
791         return 0;
792       if (arg is MSymbol)
793         {
794           MSymbol sym = (MSymbol) arg;
795
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 ();
808           else
809             return 0;
810         }
811       else if (arg is int)
812         ic.candidates.Select ((int) arg);
813       ic.candidates.Update (ic);
814       return 0;
815     }
816
817     private static object delete (MExpression[] args, MExpression.Domain domain)
818     {
819       return 1;
820     }
821
822     private static object show (MExpression[] args, MExpression.Domain domain)
823     { return 1; }
824     private static object hide (MExpression[] args, MExpression.Domain domain)
825     { return 1; }
826     private static object move (MExpression[] args, MExpression.Domain domain)
827     { return 1; }
828     private static object mark (MExpression[] args, MExpression.Domain domain)
829     { return 1; }
830     private static object pushback (MExpression[] args, MExpression.Domain domain)
831     { return 1; }
832     private static object pop (MExpression[] args, MExpression.Domain domain)
833     { return 1; }
834     private static object undo (MExpression[] args, MExpression.Domain domain)
835     { return 1; }
836     private static object commit (MExpression[] args, MExpression.Domain domain)
837     { return 1; }
838     private static object unhandle (MExpression[] args, MExpression.Domain domain)
839     { return 1; }
840     private static object shift (MExpression[] args, MExpression.Domain domain)
841     { return 1; }
842     private static object call (MExpression[] args, MExpression.Domain domain)
843     { return 1; }
844   }
845
846   public class MInputContext
847   {
848     public MInputMethod im;
849     public MText produced;
850     public bool active;
851     public MText status;
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;
864
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 ();
874     private MPlist vars;
875     private MPlist vars_saved;
876     private MText preceding_text;
877     private MText following_text;
878     private bool key_unhandled;
879
880     internal MExpression.Domain domain;
881
882     public MInputContext (MInputMethod im)
883     {
884       this.im = im;
885       domain = new MExpression (this);
886     }
887
888     internal object GetCandidates (out int column)
889     {
890       column = 0;
891       if (cursor_pos == 0)
892         return null;
893       MInputMethod.Candidates candidates
894         = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
895                                                      MInputMethod.Mcandidates);
896       if (candidates == null)
897         return null;
898       column = candidates.Column;
899       return candidates.Current;
900     }
901   }
902 }