*** 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     internal MExpression.Domain local_domain;
15
16     private static MSymbol Minput_method = MSymbol.Of ("input-method");
17     private static MSymbol Mdescription = MSymbol.Of ("description");
18     private static MSymbol Mvariable = MSymbol.Of ("variable");
19     private static MSymbol Mcommand = MSymbol.Of ("command");
20     private static MSymbol Mtitle = MSymbol.Of ("title");
21     private static MSymbol Minclude = MSymbol.Of ("include");
22     private static MSymbol Mmacro = MSymbol.Of ("macro");
23     private static MSymbol Mmap = MSymbol.Of ("map");
24     private static MSymbol Mstate = MSymbol.Of ("state");
25     internal static MSymbol Mcandidates = MSymbol.Of ("candidates");
26     internal static MSymbol Mcandidates_group_size = MSymbol.Of ("candidates-group-size");
27     private static MSymbol Mat_less_than = MSymbol.Of ("@<");
28     private static MSymbol Mat_greater_than = MSymbol.Of ("@>");
29     private static MSymbol Mat_minus = MSymbol.Of ("@-");
30     private static MSymbol Mat_plus = MSymbol.Of ("@+");
31     private static MSymbol Mat_open_square_bracket = MSymbol.Of ("@[");
32     private static MSymbol Mat_close_square_bracket = MSymbol.Of ("@]");
33     private static MSymbol Minsert = MSymbol.Of ("insert");
34     private static MSymbol Mdelete = MSymbol.Of ("delete");
35     private static MSymbol Mmove = MSymbol.Of ("move");
36     private static MSymbol Mmark = MSymbol.Of ("mark");
37     private static MSymbol Mmarker = MSymbol.Of ("marker");
38     private static MSymbol Madd = MSymbol.Of ("add");
39     private static MSymbol Msub = MSymbol.Of ("sub");
40     private static MSymbol Mmul = MSymbol.Of ("mul");
41     private static MSymbol Mdiv = MSymbol.Of ("div");
42     private static MSymbol Mif = MSymbol.Of ("if");
43     private static MSymbol Mcond = MSymbol.Of ("cond");
44     private static MSymbol Mchar_at = MSymbol.Of ("char-at");
45
46     internal class Variable
47     {
48       public MSymbol name;
49       public MText description;
50       public Type type;
51       public object value;
52       public MPlist candidates;
53     }
54
55     internal class Command
56     {
57       public MSymbol name;
58       public MText description;
59       public MSymbol[][] keys;
60     }
61
62     internal class KeySeq
63     {
64       public MSymbol[] keys;
65
66       private static MSymbol char_to_symbol (int c)
67       {
68         return MSymbol.Of (String.Format ("#{0:X}", c));
69       }
70
71       public KeySeq (MPlist plist)
72       {
73         keys = new MSymbol[plist.Count];
74         int i = 0;
75         foreach (MPlist p in plist)
76           {
77             if (p.IsSymbol)
78               keys[i++] = p.Symbol;
79             else if (p.IsInteger)
80               keys[i++] = char_to_symbol (p.Integer);
81             else
82               keys[i] = null;
83           }
84       }
85
86       public KeySeq (MText mt)
87       {
88         keys = new MSymbol[mt.Length];
89         for (int i = 0; i < mt.Length; i++)
90           keys[i] = char_to_symbol (mt[i]);
91       }
92
93       public MSymbol this[int i] { get { return keys[i]; } }
94
95       public int Length { get { return keys.Length; } }
96     }
97
98     internal class Map
99     {
100       public MSymbol name;
101       public Dictionary<MSymbol, Map> submaps;
102       public MExpression actions;
103
104       public void Add (KeySeq keys, int index, MExpression actions)
105       {
106         Map sub = null;
107
108         if (submaps == null)
109           submaps = new Dictionary<MSymbol, Map> ();
110         else
111           submaps.TryGetValue (keys[index], out sub);
112         if (sub == null)
113           {
114             MSymbol sym = keys[index];
115             submaps[sym] = sub = new Map ();
116           }
117         if (index + 1 < keys.Length)
118           sub.Add (keys, index + 1, actions);
119         else
120           this.actions = actions;
121       }
122
123       public MExpression Lookup (KeySeq keys, int index)
124       {
125         Map sub;
126
127         if (index + 1 == keys.Length)
128           return actions;
129         if (submaps.TryGetValue (keys[index], out sub))
130           return sub.Lookup (keys, index + 1);
131         return null;
132       }
133     }
134
135     internal class State
136     {
137       public MSymbol name;
138       public MText title;
139       public Dictionary<MSymbol, MExpression> branches
140         = new Dictionary<MSymbol, MExpression> ();
141     }
142
143     private static Dictionary<MDatabase.Tag, MInputMethod> im_table
144       = new Dictionary<MDatabase.Tag, MInputMethod> ();
145
146     public readonly MSymbol language;
147     public readonly MSymbol name;
148     public readonly MSymbol subname;
149
150     internal MDatabase mdb;
151     internal MText description;
152     internal MText title;
153     internal Command[] commands;
154     internal Variable[] variables;
155     internal MPlist bindings;
156     internal Dictionary<MSymbol, Map> maps
157       = new Dictionary<MSymbol, Map> ();
158     internal State init_state;
159     internal Dictionary<MSymbol, State> states
160       = new Dictionary<MSymbol, State> ();
161     internal MPlist externals;
162
163     static MInputMethod ()
164     {
165       domain.Defun ("insert", insert, 1, 1);
166       domain.Defun ("candidates", insert_candidates, 1, 1);
167       domain.Defun ("delete", delete, 1, 1);
168       domain.Defun ("select", select, 1, 1);
169       domain.Defun ("show", show, 0, 0);
170       domain.Defun ("hide", hide, 0, 0);
171       domain.Defun ("move", move, 1, 1);
172       domain.Defun ("mark", mark, 1, 1);
173       domain.Defun ("pushback", pushback, 1, 1);
174       domain.Defun ("pop", pop, 0, 0);
175       domain.Defun ("undo", undo, 0, 1);
176       domain.Defun ("commit", commit, 0, 0);
177       domain.Defun ("unhandle", unhandle, 0, 0);
178       domain.Defun ("shift", shift, 1, 1);
179       domain.Defun ("call", call, 2, -1);
180       domain.Defun ("marker", marker, 1, 1, true);
181       domain.Defun ("char-at", char_at, 1, 1, true);
182     }
183
184     private MInputMethod (MDatabase.Tag tag)
185     {
186       mdb = MDatabase.Find (tag);
187       if (mdb == null)
188         throw new Exception (String.Format ("Input method {0} not available",
189                                             tag));
190       language = tag[1];
191       name = tag[2];
192       subname = tag[3];
193       local_domain = new MExpression.Domain (domain, null);
194
195       MPlist plist = (MPlist) mdb.Load ();
196       if (plist == null)
197         return;
198       for (; ! plist.IsEmpty; plist = plist.next)
199         if (plist.IsPlist)
200           {
201             MPlist pl = plist.Plist;
202             if (pl.IsSymbol)
203               {
204                 MSymbol sym = pl.Symbol;
205                 pl = pl.next;
206                 if (sym == Mdescription)
207                   {
208                     description = parse_description (pl);
209                     if (description == null)
210                       description = new MText ("No description");
211                   }
212                 else if (sym == Mtitle)
213                   {
214                     if (pl.IsMText)
215                       title = pl.Text;
216                   }
217                 else if (sym == Mvariable)
218                   parse_variables (pl);
219                 else if (sym == Mcommand)
220                   parse_commands (pl);
221                 else if (sym == Minclude)
222                   parse_include (pl);
223                 else if (sym == Mmacro)
224                   parse_macros (pl);
225                 else if (sym == Mmap)
226                   parse_maps (pl);
227                 else if (sym == Mstate)
228                   parse_states (pl);
229               }
230           }
231     }
232
233     private static void transform (MPlist plist)
234     {
235       for (; ! plist.IsEmpty; plist = plist.next)
236         {
237           if (plist.IsMText)
238             {
239               MPlist p = new MPlist ();
240               p.Add (MSymbol.symbol, Minsert);
241               p.Add (MSymbol.mtext, plist.Text);
242               plist.Set (MSymbol.plist, p);
243             }
244           else if (plist.IsInteger)
245             {
246               MPlist p = new MPlist ();
247               p.Add (MSymbol.symbol, Minsert);
248               p.Add (MSymbol.integer, plist.Integer);
249               plist.Set (MSymbol.plist, p);
250             }
251           else if (plist.IsPlist)
252             {
253               MPlist pl = plist.Plist;
254
255               if (pl.IsSymbol)
256                 {
257                   if (pl.Symbol == Madd)
258                     pl.Set (MSymbol.symbol, MSymbol.Of ("+="));
259                   else if (pl.Symbol == Msub)
260                     pl.Set (MSymbol.symbol, MSymbol.Of ("-="));
261                   else if (pl.Symbol == Mmul)
262                     pl.Set (MSymbol.symbol, MSymbol.Of ("*="));
263                   else if (pl.Symbol == Mdiv)
264                     pl.Set (MSymbol.symbol, MSymbol.Of ("/="));
265                   else if (pl.Symbol == Minsert)
266                     {
267                       // (insert (CANDIDATES ...))
268                       //   => (candidates CANDIDATES ...)
269                       if (pl.next.IsPlist)
270                         {
271                           pl.Set (MSymbol.symbol, Mcandidates);
272                           pl = pl.next;
273                           MPlist p = pl.Plist;
274                           pl.Set (p.key, p.val);
275                           for (p = p.next; ! p.IsEmpty; p = p.next);
276                           pl.Add (p.key, p.val);
277                         }
278                     }
279                   else if (pl.Symbol == Mif)
280                     {
281                       pl = pl.next;
282                       if (! pl.IsEmpty)
283                         transform (pl.next);
284                     }
285                   else if (pl.Symbol == Mcond)
286                     {
287                       for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
288                         if (pl.IsPlist)
289                           {
290                             MPlist p = pl.Plist;
291
292                             if (p.IsPlist)
293                               transform (p);
294                             else
295                               transform (p.next);
296                           }
297                     }
298                   else if (pl.Symbol == Mdelete
299                            || pl.Symbol == Mmove
300                            || pl.Symbol == Mmark)
301                     {
302                       pl = pl.next;
303                       if (pl.IsSymbol)
304                         {
305                           MSymbol sym = pl.Symbol;
306                           MPlist p = new MPlist ();
307                           p.Add (MSymbol.symbol, Mmarker);
308                           p.Add (MSymbol.symbol, sym);
309                           pl.Set (MSymbol.plist, p);
310                         }
311                     }
312                   else if (pl.Symbol == Mstate)
313                     {
314                       pl = pl.next;
315                       if (pl.IsSymbol)
316                         {
317                           MSymbol sym = pl.Symbol;
318                           MPlist p = new MPlist ();
319                           p.Add (MSymbol.symbol, Mstate);
320                           p.Add (MSymbol.symbol, sym);
321                           pl.Set (MSymbol.plist, p);
322                         }
323                     }
324                 }
325               else if (pl.IsMText)
326                 {
327                   // (CANDIDATES ...) => (candidates CANDIDATES ...)
328                   pl.Push (MSymbol.symbol, Mcandidates);
329                 }
330             }
331           else if (plist.IsSymbol)
332             {
333               MSymbol sym = plist.Symbol;
334
335               if (sym.Name[0] == '@')
336                 {
337                   MPlist p = new MPlist ();
338                   p.Add (MSymbol.symbol, Mchar_at);
339                   p.Add (MSymbol.symbol, sym);
340                   plist.Set (MSymbol.plist, p);
341                 }
342             }
343         }
344     }
345
346     private static MText parse_description (MPlist plist)
347     {
348       return (plist.IsMText ? plist.Text
349               : plist.IsPlist && plist.Plist.IsMText ? plist.Plist.Text
350               : null);
351     }
352
353     private void parse_variables (MPlist plist)
354     {
355       variables = new Variable[plist.Count];
356
357       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
358         if (plist.IsPlist && plist.Plist.IsSymbol)
359           {
360             Variable var = new Variable ();
361             MPlist p = plist.Plist;
362
363             var.name = p.Symbol;
364             p = p.Next;
365             var.description = parse_description (p);
366             if (var.description == null)
367               var.description = new MText ("No description");
368             else
369               p = p.next;
370             var.type = (p.IsMText ? typeof (MText)
371                         : p.IsInteger ? typeof (int)
372                         : p.IsSymbol ? typeof (MSymbol)
373                         : typeof (object));
374             var.value = p.val;
375             var.candidates = p.next;
376             variables[i++] = var;
377           }
378     }
379
380     private void parse_commands (MPlist plist)
381     {
382       commands = new Command[plist.Count];
383
384       for (int i = 0; ! plist.IsEmpty; plist = plist.next)
385         if (plist.IsPlist && plist.Plist.IsSymbol)
386           {
387             Command cmd = new Command ();
388             MPlist p = plist.Plist;
389
390             cmd.name = p.Symbol;
391             p = p.Next;
392             cmd.description = parse_description (p);
393             if (cmd.description == null)
394               cmd.description = new MText ("No description");
395             else
396               p = p.next;
397             KeySeq[] keys = new KeySeq[p.Count];
398             for (int j = 0; ! p.IsEmpty; p = p.next)
399               {
400                 if (p.IsMText)
401                   keys[j++] = new KeySeq (p.Text);
402                 else if (p.IsPlist)
403                   keys[j++] = new KeySeq (p.Plist);
404               }
405             commands[i++] = cmd;
406           }
407     }
408
409     private void parse_include (MPlist plist)
410     {
411       if (! plist.IsPlist)
412         return;
413       MPlist p = plist.Plist;
414       MSymbol language, name, extra;
415       language = p.Symbol;
416       p = p.next;
417       if (! p.IsSymbol)
418         name = extra = MSymbol.nil;
419       else
420         {
421           name = p.Symbol;
422           p = p.next;
423           if (! p.IsSymbol)
424             extra = MSymbol.nil;
425           else
426             extra = p.Symbol;
427         }
428
429       MInputMethod im = MInputMethod.Get (language, name, extra);
430       if (im == null)
431         return;
432       plist = plist.next;
433       if (! plist.IsSymbol)
434         return;
435       MSymbol target_type = plist.Symbol;
436       plist = plist.next;
437       MSymbol target_name = MSymbol.nil;
438       if (plist.IsSymbol)
439         target_name = plist.Symbol;
440       if (target_type == Mmacro)
441         {
442           if (target_name == MSymbol.nil)
443             im.local_domain.CopyFunc (local_domain);
444           else
445             im.local_domain.CopyFunc (target_name, local_domain);
446         }
447       else if (target_type == Mmap)
448         {
449           if (target_name == MSymbol.nil)
450             {
451               foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
452                 maps[kv.Key] = kv.Value;
453             }
454           else
455             {
456               Map map;
457               if (im.maps.TryGetValue (target_name, out map))
458                 maps[target_name] = map;
459             }
460         }
461       else if (target_type == Mstate)
462         {
463           if (target_name == MSymbol.nil)
464             {
465               foreach (KeyValuePair<MSymbol, State> kv in im.states)
466                 states[kv.Key] = kv.Value;
467             }
468           else
469             {
470               State state;
471               if (im.states.TryGetValue (target_name, out state))
472                 states[target_name] = state;
473             }
474         }
475     }
476
477     private void parse_macros (MPlist plist)
478     {
479       for (; ! plist.IsEmpty; plist = plist.next)
480         if (plist.IsPlist)
481           {
482             MPlist pl = plist.Plist;
483
484             if (! pl.IsSymbol)
485               continue;
486             transform (pl.next);
487             local_domain.Defun (pl.Symbol, new MPlist (), pl.next);
488           }
489     }
490
491     private void parse_maps (MPlist plist)
492     {
493       for (; ! plist.IsEmpty; plist = plist.next)
494         if (plist.IsPlist)
495           {
496             MPlist pl = plist.Plist;
497           
498             if (! pl.IsSymbol)
499               continue;
500             Map map = new Map ();
501             map.name = pl.Symbol;
502             maps[map.name] = map;
503             for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
504               {
505                 if (! pl.IsPlist)
506                   continue;
507                 MPlist p = pl.Plist;
508                 KeySeq keys;
509                 if (p.IsMText)
510                   keys = new KeySeq (p.Text);
511                 else if (p.IsPlist)
512                   keys = new KeySeq (p.Plist);
513                 else
514                   continue;
515                 if (keys.keys.Length == 0
516                     && keys.keys[0] == null)
517                   continue;
518                 p = p.next;
519                 if (p.IsEmpty)
520                   continue;
521                 transform (p);
522                 MExpression expr = new MExpression (p, local_domain);
523                 map.Add (keys, 0, expr);
524               }
525           }
526     }
527
528     private void parse_states (MPlist plist)
529     {
530       for (; ! plist.IsEmpty; plist = plist.next)
531         if (plist.IsPlist)
532           {
533             MPlist pl = plist.Plist;
534             MText title = null;
535           
536             if (pl.IsMText)
537               {
538                 title = pl.Text;
539                 pl = pl.next;
540               }
541             if (! pl.IsSymbol)
542               continue;
543
544             State state = new State ();
545             state.name = pl.Symbol;
546             state.title = title;
547             states[state.name] = state;
548             if (init_state == null)
549               init_state = state;
550             for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
551               {
552                 if (! pl.IsPlist)
553                   continue;
554                 MPlist p = pl.Plist;
555                 if (! p.IsSymbol)
556                   continue;
557                 MSymbol map_name = p.Symbol;
558                 p = p.next;
559                 transform (p);
560                 state.branches[map_name] = new MExpression (p, local_domain);
561               }
562           }
563     }
564
565     public static MInputMethod Get (MSymbol language, MSymbol name,
566                                     MSymbol extra)
567     {
568       MDatabase.Tag tag
569         = new MDatabase.Tag (Minput_method, language, name, extra);
570       MInputMethod im;
571       if (im_table.TryGetValue (tag, out im))
572         return im;
573       try {
574         im = new MInputMethod (tag);
575       } catch (Exception e) {
576         Console.WriteLine (e);
577         im = null;
578       }
579       return im;
580     }
581
582     private static void adjust_markers (MInputContext ic,
583                                         int from, int to, object inserted)
584     {
585       int ins = (inserted == null ? 0
586                  : inserted is int ? 1
587                  : ((MText) inserted).Length);
588       int diff = ins - (to - from);
589
590       for (MPlist plist = ic.markers; ! plist.IsEmpty; plist = plist.next)
591         {
592           int pos = plist.Integer;
593           if (pos > from)
594             {
595               if (pos >= to)
596                 plist.val = pos + diff;
597               else
598                 plist.val = from;
599             }
600         }
601       if (ic.cursor_pos >= to)
602         ic.cursor_pos += diff;
603       else if (ic.cursor_pos > from)
604         ic.cursor_pos = from;
605     }
606
607     private static void preedit_replace (MInputContext ic,
608                                          int from, int to, int c)
609     {
610       ic.preedit.Del (from, to);
611       ic.preedit.Ins (from, c);
612       adjust_markers (ic, from, to, c);
613     }
614
615     private static void preedit_replace (MInputContext ic,
616                                          int from, int to, MText mt)
617     {
618       ic.preedit[from, to] = mt;
619       adjust_markers (ic, from, to, mt);
620     }
621
622     private static object insert (MExpression[] args,
623                                   MExpression.Domain domain)
624     {
625       MInputContext ic = (MInputContext) domain.context;
626       object arg = args[0].Val;
627
628       if (arg is int)
629         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (int) arg);
630       else
631         preedit_replace (ic, ic.cursor_pos, ic.cursor_pos, (MText) arg);
632       ic.preedit_changed = true;
633       ic.cursor_pos_changed = true;
634       return 1;
635     }
636
637     internal class Candidates
638     {
639       private class Block
640       {
641         public int Index;
642         public object Data;
643
644         public Block (int index, MPlist plist)
645         {
646           Index = index;
647           if (plist.IsMText)
648             Data = plist.Text;
649           else
650             Data = plist.Plist;
651         }
652
653         public int Count
654         {
655           get { return (Data is MText
656                         ? ((MText) Data).Length
657                         : ((MPlist) Data).Count); }
658         }
659
660         public object this[int i]
661         {
662           get {
663             if (Data is MText) return ((MText) Data)[i];
664             return  ((MPlist) Data)[i];
665           }
666         }
667       }
668
669       private Block[] blocks;
670       private int row = 0;
671       private int index = 0;
672       public object[] group;
673
674       private bool IsFixed { get { return group != null; } }
675       private int Total {
676         get {
677           Block last = blocks[blocks.Length - 1];
678           return last.Index + last.Count; }
679       }
680
681       public int Column {
682         get { return (IsFixed ? index % group.Length
683                       : index - blocks[row].Index); }
684       }
685
686       public object Group {
687         get { return (IsFixed ? group : blocks[row].Data); }
688       }
689
690       public int GroupLength
691       {
692         get {
693           if (IsFixed)
694             {
695               int nitems = group.Length;
696               int start = index - (index % nitems);
697               int total = Total;
698               return (start + nitems <= total ? nitems : total - start);
699             }
700           return blocks[row].Count;
701         }
702       }
703
704       public object Current {
705         get {
706           return (IsFixed ? group[index % group.Length]
707                   : blocks[row][index - blocks[row].Index]);
708         }
709       }
710
711       public Candidates (MPlist list, int column)
712       {
713         int nblocks = list.Count;
714
715         blocks = new Block[nblocks];
716         for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
717           start += (blocks[i] = new Block (index, list)).Count;
718         if (column > 0)
719           group = new object[column];
720       }
721
722       public static void Detach (MInputContext ic)
723       {
724         ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
725         ic.candidates = null;
726         ic.preedit_changed = true;
727         ic.cursor_pos_changed = true;
728         ic.candidate_changed = true;
729       }
730
731       // Fill the array "group" by candidates stating from INDEX.
732       // INDEX must be a multiple of "column".  Set NTIMES to the
733       // number of valid candidates in "group".  Update "block" if
734       // necessary.  Return "group".
735
736       private int fill_group (int start)
737       {
738         int nitems = group.Length;
739         int r = row;
740         Block b = blocks[r];
741
742         if (start < b.Index)
743           while (start < b.Index)
744             b = blocks[--r];
745         else
746           while (start >= b.Index + b.Count)
747             b = blocks[++r];
748         row = r;
749
750         int count = b.Count;
751         start -= b.Index;
752         for (int i = 0; i < nitems; i++, start++)
753           {
754             if (start >= count)
755               {
756                 r++;
757                 if (r == blocks.Length)
758                   return i;
759                 b = blocks[r];
760                 count = b.Count;
761                 start = 0;
762               }
763             group[i] = b[start];
764           }
765         return nitems;
766       }
767
768       // Update "row" to what contains the first candidate of
769       // the previous candidate-group, update "current_index", and
770       // update "group" if necessary.  Return the previous
771       // candidate-group.  Set NITEMS to the number of valid
772       // candidates contained in that group.
773
774       public int PrevGroup ()
775       {
776         int nitems;
777         int col = Column;
778
779         if (IsFixed)
780           {
781             nitems = group.Length;
782             if ((index -= col + nitems) < 0)
783               index = (Total / nitems) * nitems;
784             nitems = fill_group (index);
785           }
786         else
787           {
788             row = row > 0 ? row-- : blocks.Length - 1;
789             nitems = blocks[row].Count;
790             index = blocks[row].Index;
791           }
792         index += col < nitems ? col : nitems - 1;
793         return nitems;
794       }
795
796       public int NextGroup ()
797       {
798         int nitems;
799         int col = Column;
800
801         if (IsFixed)
802           {
803             nitems = group.Length;
804             if ((index += nitems - col) >= Total)
805               index = 0;
806             nitems = fill_group (index);
807           }
808         else
809           {
810             row = row < blocks.Length - 1 ? row + 1 : 0;
811             nitems = blocks[row].Count;
812             index = blocks[row].Count;
813           }
814         index += col < nitems ? col : nitems - 1;
815         return nitems;
816       }
817
818       public void Prev ()
819       {
820         int col = Column;
821
822         if (col == 0)
823           {
824             int nitems = PrevGroup ();
825             index += col < nitems - 1 ? col : nitems - 1;
826           }
827         else
828           index--;
829       }
830
831       public void Next ()
832       {
833         int col = Column;
834         int nitems = GroupLength;
835
836         if (col == nitems - 1)
837           {
838             nitems = NextGroup ();
839             index -= Column;
840           }
841         else
842           index++;
843       }
844
845       public void First ()
846       {
847         index -= Column;
848       }
849
850       public void Last ()
851       {
852         index += GroupLength - (Column + 1);
853       }
854
855       public void Select (int col)
856       {
857         int maxcol = GroupLength - 1;
858         if (col > maxcol)
859           col = maxcol;
860         index = index - Column + col;
861       }
862
863       public void Update (MInputContext ic)
864       {
865         int from, to;
866
867         if (ic.candidates == null)
868           {
869             from = ic.cursor_pos;
870             to = ic.cursor_pos;
871             ic.candidates = this;
872           }
873         else
874           {
875             from = ic.candidate_from;
876             to = ic.candidate_to;
877           }
878
879         object candidate = ic.candidates.Current;
880
881         if (candidate is MText)
882           preedit_replace (ic, from, to, (MText) candidate);
883         else
884           preedit_replace (ic, from, to, (int) candidate);
885         ic.preedit.PushProp (from, to, Mcandidates, this);
886         ic.cursor_pos = to;
887         ic.candidate_from = from;
888         ic.candidate_to = to;
889         ic.preedit_changed = true;
890         ic.cursor_pos_changed = true;
891         ic.candidate_changed = true;
892       }
893     }
894
895     private static object insert_candidates (MExpression[] args,
896                                              MExpression.Domain domain)
897     {
898       MInputContext ic = (MInputContext) domain.context;
899       MPlist list = (MPlist) args[0].Val;
900       int column = 0;
901
902       if (domain.IsBound (Mcandidates_group_size))
903         {
904           object val = domain.GetValue (Mcandidates_group_size);
905           if (val is int)
906             column = (int) val;
907         }
908       Candidates candidates = new Candidates (list, column);
909       candidates.Update (ic);
910       return 1;
911     }
912
913     private static object state (MExpression[] args,
914                                  MExpression.Domain domain)
915     {
916       MInputContext ic = (MInputContext) domain.context;
917       MSymbol sym = (MSymbol) args[0].Args[0].Val;
918
919       if (sym == MSymbol.t)
920         return MSymbol.t;
921       return ic.im.states[sym];
922     }
923
924     private static object marker (MExpression[] args,
925                                   MExpression.Domain domain)
926     {
927       MInputContext ic = (MInputContext) domain.context;
928       MSymbol sym = (MSymbol) args[0].Args[0].Val;
929       int pos = ic.cursor_pos;
930
931       if (sym.Name.Length == 2 && sym.Name[0] == '@')
932         {
933           switch (sym.Name[0])
934             {
935             case '<': pos = 0; break;
936             case '>': pos = ic.preedit.Length; break;
937             case '-': pos = ic.cursor_pos - 1; break;
938             case '+': pos = ic.cursor_pos + 1; break;
939             case '[':
940               if (pos > 0)
941                 {
942                   int to;
943                   ic.preedit.FindProp (Mcandidates, pos - 1, out pos, out to);
944                 }
945               else
946                 pos = 0;
947               break;
948             case ']':
949               if (ic.cursor_pos < ic.preedit.Length - 1)
950                 {
951                   int from;
952                   ic.preedit.FindProp (Mcandidates, pos, out from, out pos);
953                 }
954               else
955                 pos = ic.preedit.Length;
956               break;
957             default:
958               if (sym.Name[0] >= '0' && sym.Name[0] <= '9')
959                 pos = sym.Name[0];
960               break;
961             }
962         }
963       else if (sym.Name.Length >= 3 && sym.Name[0] == '@')
964         {
965           
966
967         }
968       else
969         {
970           object val = ic.markers.Get (sym);
971
972           if (val is int)
973             pos = (int) val;
974         }
975       return pos;
976     }
977
978     private static object char_at (MExpression[] args,
979                                    MExpression.Domain domain)
980     {
981       MInputContext ic = (MInputContext) domain.context;
982       MSymbol sym = (MSymbol) args[0].Args[0].Val;
983
984       if (
985
986
987       return 0;
988     }
989
990     private static object delete (MExpression[] args,
991                                   MExpression.Domain domain)
992     {
993       MInputContext ic = (MInputContext) domain.context;
994       int pos = (int) args[0].Val;
995
996       if (pos < ic.cursor_pos)
997         preedit_replace (ic, pos, ic.cursor_pos, null);
998       else
999         preedit_replace (ic, ic.cursor_pos, pos, null);
1000       ic.preedit_changed = true;
1001       ic.cursor_pos_changed = true;
1002       return true;
1003     }
1004
1005     private static object select (MExpression[] args,
1006                                   MExpression.Domain domain)
1007     {
1008       MInputContext ic = (MInputContext) domain.context;
1009       object arg = args[0].Val;
1010
1011       if (ic.candidates == null)
1012         return 0;
1013       if (arg is MSymbol)
1014         {
1015           MSymbol sym = (MSymbol) arg;
1016
1017           if (sym == Mat_less_than)
1018             ic.candidates.First ();
1019           else if (sym == Mat_greater_than)
1020             ic.candidates.Last ();
1021           else if (sym == Mat_minus)
1022             ic.candidates.Prev ();
1023           else if (sym == Mat_plus)
1024             ic.candidates.Next ();
1025           else if (sym == Mat_open_square_bracket)
1026             ic.candidates.PrevGroup ();
1027           else if (sym == Mat_close_square_bracket)
1028             ic.candidates.NextGroup ();
1029           else
1030             return 0;
1031         }
1032       else if (arg is int)
1033         ic.candidates.Select ((int) arg);
1034       ic.candidates.Update (ic);
1035       return 0;
1036     }
1037
1038     private static object show (MExpression[] args, MExpression.Domain domain)
1039     { return 1; }
1040     private static object hide (MExpression[] args, MExpression.Domain domain)
1041     { return 1; }
1042     private static object move (MExpression[] args, MExpression.Domain domain)
1043     { return 1; }
1044     private static object mark (MExpression[] args, MExpression.Domain domain)
1045     { return 1; }
1046     private static object pushback (MExpression[] args, MExpression.Domain domain)
1047     { return 1; }
1048     private static object pop (MExpression[] args, MExpression.Domain domain)
1049     { return 1; }
1050     private static object undo (MExpression[] args, MExpression.Domain domain)
1051     { return 1; }
1052     private static object commit (MExpression[] args, MExpression.Domain domain)
1053     { return 1; }
1054     private static object unhandle (MExpression[] args, MExpression.Domain domain)
1055     { return 1; }
1056     private static object shift (MExpression[] args, MExpression.Domain domain)
1057     { return 1; }
1058     private static object call (MExpression[] args, MExpression.Domain domain)
1059     { return 1; }
1060   }
1061
1062   public class MInputContext
1063   {
1064     public MInputMethod im;
1065     public MText produced;
1066     public bool active;
1067     public MText status;
1068     public bool status_changed;
1069     public MText preedit;
1070     public bool preedit_changed;
1071     public int cursor_pos;
1072     public bool cursor_pos_changed;
1073     internal MInputMethod.Candidates candidates;
1074     public MPlist candidate_group;
1075     public int candidate_index;
1076     public int candidate_from, candidate_to;
1077     public bool candidate_show;
1078     public bool candidate_changed;
1079     public MPlist callback_args;
1080
1081     private MInputMethod.State state;
1082     private MInputMethod.State prev_state;
1083     private Stack<MSymbol> keys;
1084     private int state_key_head;
1085     private int key_head;
1086     private int commit_key_head;
1087     private MText preedit_saved;
1088     private int state_pos;
1089     internal MPlist markers = new MPlist ();
1090     private MPlist vars;
1091     private MPlist vars_saved;
1092     private MText preceding_text;
1093     private MText following_text;
1094     private bool key_unhandled;
1095
1096     internal MExpression.Domain domain;
1097
1098     public MInputContext (MInputMethod im)
1099     {
1100       this.im = im;
1101       domain = new MExpression.Domain (im.local_domain, this);
1102     }
1103
1104     internal object GetCandidates (out int column)
1105     {
1106       column = 0;
1107       if (cursor_pos == 0)
1108         return null;
1109       MInputMethod.Candidates candidates
1110         = (MInputMethod.Candidates) preedit.GetProp (cursor_pos - 1,
1111                                                      MInputMethod.Mcandidates);
1112       if (candidates == null)
1113         return null;
1114       column = candidates.Column;
1115       return candidates.Current;
1116     }
1117   }
1118 }