*** empty log message ***
[m17n/m17n-lib-cs.git] / MInputMethod.cs
index cfd6d7f..1a79af3 100644 (file)
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Reflection;
 using System.IO;
+using System.Xml;
+
 using M17N;
 using M17N.Core;
 using M17N.Input;
 
 namespace M17N.Input
 {
+  using Xex = System.Xml.Expression.Xexpression;
+  using Mim = MInputMethod;
+
   public class MInputMethod
   {
-    private class MInputAction
+    // Delegaes
+    public delegate bool Callback (MInputContext ic, MPlist args);
+
+    // Class members
+    public static Callback PreeditStart, PreeditDone, PreeditDraw;
+    public static Callback StatusStart, StatusDone, StatusDraw;
+    public static Callback CandidateStart, CandidateDone, CandidateDraw;
+    public static Callback SetSpot;
+    public static Callback Toggle;
+    public static Callback Reset;
+    public static Callback GetSurroundingText;
+    public static Callback DeleteSurroundingText;
+
+    internal static Xex.Domain im_domain = new Xex.Domain (null);
+    private static MSymbol Minput_method = "input-method";
+    private static MSymbol Mdescription = "description";
+    private static MSymbol Mvariable = "variable";
+    private static MSymbol Mcommand = "command";
+    private static MSymbol Mmodule = "module";
+    private static MSymbol Mtitle = "title";
+    private static MSymbol Minclude = "include";
+    private static MSymbol Mmacro = "macro";
+    private static MSymbol Mmap = "map";
+    private static MSymbol Mmap_list = "map-list";
+    private static MSymbol Mstate = "state";
+    internal static MSymbol Mcandidates = "candidates";
+    private static MSymbol Minsert = "insert";
+    private static MSymbol Mdelete = "delete";
+    private static MSymbol Mmove = "move";
+    private static MSymbol Mmark = "mark";
+    private static MSymbol Mmarker = "marker";
+    private static MSymbol Mset = "set";
+    private static MSymbol Madd = "add";
+    private static MSymbol Msub = "sub";
+    private static MSymbol Mmul = "mul";
+    private static MSymbol Mdiv = "div";
+    private static MSymbol Mif = "if";
+    private static MSymbol Mcond = "cond";
+    private static MSymbol Mchar_at = "char-at";
+    private static MSymbol Msurrounding_text_p = "surrounding-text-p";
+    private static MSymbol Mpushback = "pushback"; 
+    private static MSymbol Mkeyseq = "keyseq"; 
+
+    private static Xex.Symbol Nprogn = "progn";
+    private static Xex.Term Tnil = new Xex.Term ((Xex.Symbol) "nil");
+    private static Xex.Term Tcatch_tag = new Xex.Term ((Xex.Symbol) "@mimtag");
+
+    private static Dictionary<MDatabase.Tag, MInputMethod> im_table
+      = new Dictionary<MDatabase.Tag, MInputMethod> ();
+
+    internal static MInputMethod im_global = null;
+
+    [FlagsAttribute]
+    private enum LoadStatus
+    {
+      None =   0x00,
+      Header = 0x01,
+      Body =   0x02,
+      Full =   0x03,
+      Error =  0x04,
+    };
+
+    [FlagsAttribute]
+    public enum ChangedStatus
     {
-      MExpression[] expr_list;
+      None =           0x00,
+      StateTitle =     0x01,
+      Preedit   =      0x02,
+      CursorPos =      0x04,
+      CandidateList =  0x08,
+      CandidateIndex = 0x10,
+      CandidateShow =  0x20,
     }
 
-    private class MInputMethodMap
+    private static ChangedStatus CandidateAll = (ChangedStatus.CandidateList
+                                                | ChangedStatus.CandidateIndex
+                                                | ChangedStatus.CandidateShow);
+    [FlagsAttribute]
+    public enum KeyModifier
+      {
+       None =      0x00000000,
+       Shift_L =   0x00400000,
+       Shift_R =   0x00800000,
+       Shift =     0x00C00000,
+       Control_L = 0x01000000,
+       Control_R = 0x02000000,
+       Control   = 0x03000000,
+       Alt_L =     0x04000000,
+       Alt_R =     0x08000000,
+       Alt =       0x0C000000,
+       AltGr =     0x10000000,
+       Super =     0x20000000,
+       Hyper =     0x40000000,
+       High =      0x70000000,
+       All =       0x7FC00000,
+      };
+
+    public struct Key
+    {
+      internal uint key;
+
+      private static Dictionary<string, uint> keysyms
+       = new Dictionary<string, uint> ();
+      private static Dictionary<string, KeyModifier> keymodifiers
+       = new Dictionary<string, KeyModifier> ();
+      private static uint keysym_base = 0x200000;
+      private static uint char_mask = ~((uint) KeyModifier.All);
+
+      static Key ()
+      {
+       keysyms["bs"] = keysyms["backspace"] = 0x08;
+       keysyms["tab"] = 0x09;
+       keysyms["lf"] = keysyms["linefeed"] = 0x10;
+       keysyms["cr"] = keysyms["return"] = keysyms["enter"] = 0x13;
+       keysyms["esc"] = keysyms["escape"] = 0x1B;
+       keysyms["spc"] = keysyms["space"] = 0x20;
+       keysyms["del"] = keysyms["delete"] = 0x7F;
+       keymodifiers["shift-l"] = KeyModifier.Shift_L;
+       keymodifiers["shift-r"] = KeyModifier.Shift_R;
+       keymodifiers["shift"] = KeyModifier.Shift;
+       keymodifiers["control-l"] = KeyModifier.Control_L;
+       keymodifiers["control-r"] = KeyModifier.Control_R;
+       keymodifiers["control"] = KeyModifier.Control;
+       keymodifiers["alt-l"] = KeyModifier.Alt_L;
+       keymodifiers["alt-r"] = KeyModifier.Alt_R;
+       keymodifiers["alt"] = KeyModifier.Alt;
+       keymodifiers["altgr"] = KeyModifier.AltGr;
+       keymodifiers["super"] = KeyModifier.Super;
+       keymodifiers["hyper"] = KeyModifier.Hyper;
+      }
+
+      private static uint decode_keysym (MSymbol keysym)
+      {
+       uint key;
+       string name = keysym.Name;
+
+       if (name.Length == 1)
+         return name[0];
+       name = name.ToLower ();
+       if (! keysyms.TryGetValue (name, out key))
+         keysyms[name] = key = keysym_base++;
+       return key;
+      }
+
+      private static uint combine_modifiers (uint c, KeyModifier modifiers)
+      {
+       if (c < 0x7F && c != 0x20)
+         {
+           if ((modifiers & KeyModifier.Shift) != KeyModifier.None
+               && Char.IsLower ((char) c))
+             {
+               modifiers &= ~KeyModifier.Shift;
+               c = Char.ToUpper ((char) c);
+             }
+           if ((modifiers & KeyModifier.Control) != KeyModifier.None)
+             {
+               modifiers &= ~KeyModifier.Control;
+               c &= 0x1F;
+             }
+         }     
+       return c | (uint) modifiers;
+      }
+
+      public Key (uint c) { key = c; }
+      public Key (int c) { key = (uint) c; }
+
+      public Key (uint c, KeyModifier modifiers)
+      {
+       key = combine_modifiers (c, modifiers);
+      }
+
+      public Key (MSymbol keysym, KeyModifier modifiers)
+      {
+       key = combine_modifiers (decode_keysym (keysym), modifiers);
+      }
+
+      public Key (MSymbol keysym)
+      {
+       string str = keysym.Name;
+       int len = str.Length;
+       int i;
+       KeyModifier modifiers = KeyModifier.None;
+
+       for (i = 0; i + 2 < len && str[i + 1] == '-'; i += 2)
+         {
+           if (str[i] == 'S')
+             modifiers |= KeyModifier.Shift;
+           else if (str[i] == 'C')
+             modifiers |= KeyModifier.Control;
+           else if (str[i] == 'A')
+             modifiers |= KeyModifier.Alt;
+           else if (str[i] == 'G')
+             modifiers |= KeyModifier.AltGr;
+           else if (str[i] == 's')
+             modifiers |= KeyModifier.Super;
+           else if (str[i] == 'H')
+             modifiers |= KeyModifier.Hyper;
+         }
+       if (i + 1 == len)
+         key = combine_modifiers (str[i], modifiers);
+       else
+         key = combine_modifiers (decode_keysym (keysym), modifiers);
+      }
+
+      public Key (MPlist plist)
+      {
+       KeyModifier modifiers = KeyModifier.None;
+       MPlist p;
+
+       for (p = plist; ! p.IsEmpty; p = p.next)
+         {
+           if (p.IsInteger)
+             {
+               if (! p.next.IsEmpty)
+                 throw new Exception ("Invalid Key: " + plist);
+               break;
+             }
+           else if (! p.IsSymbol)
+             throw new Exception ("Invalid Key: " + plist);
+           else
+             {
+               string name = p.Symbol.Name.ToLower ();
+               KeyModifier m;
+               
+               if (! keymodifiers.TryGetValue (name, out m))
+                 break;
+               modifiers |= m;
+             }
+         }
+       if (p.IsEmpty || ! p.next.IsEmpty)
+         throw new Exception ("Invalid Key: " + plist);
+       if (p.IsInteger)
+         key = combine_modifiers ((uint) p.Integer, modifiers);
+       else
+         key = combine_modifiers (decode_keysym (p.Symbol), modifiers);
+      }
+
+      public bool HasModifier
+      {
+       get { return ((key & (uint) KeyModifier.All) != 0); }
+      }
+
+      public bool Match (Key k)
+      {
+       if (k.key == key)
+         return true;
+       if ((k.key & char_mask) != (key & char_mask))
+         return false;
+       KeyModifier m1 = ((KeyModifier) key) & KeyModifier.All;
+       KeyModifier m2 = ((KeyModifier) k.key) & KeyModifier.All;
+       return (((m1 & KeyModifier.Shift) == (m2 & KeyModifier.Shift)
+                || ((m1 & KeyModifier.Shift) == KeyModifier.Shift
+                    && (m2 & KeyModifier.Shift) != KeyModifier.None))
+               && ((m1 & KeyModifier.Control) == (m2 & KeyModifier.Control)
+                   || ((m1 & KeyModifier.Control) == KeyModifier.Control
+                       && (m2 & KeyModifier.Control) != KeyModifier.None))
+               && ((m1 & KeyModifier.Alt) == (m2 & KeyModifier.Alt)
+                   || ((m1 & KeyModifier.Alt) == KeyModifier.Alt
+                       && (m2 & KeyModifier.Alt) != KeyModifier.None))
+               && ((m1 & KeyModifier.High) == (m2 & KeyModifier.High)));
+      }
+
+      public override string ToString ()
+      {
+       string str = Char.ToString ((char) key);
+       KeyModifier m = ((KeyModifier) key) & KeyModifier.All;
+
+       if (m != KeyModifier.None)
+         {
+           if ((m & KeyModifier.Shift) != KeyModifier.None)
+             str = "S-" + str;
+           if ((m & KeyModifier.Control) != KeyModifier.None)
+             str = "C-" + str;
+           if ((m & KeyModifier.Alt) != KeyModifier.None)
+             str = "A-" + str;
+           if ((m & KeyModifier.AltGr) != KeyModifier.None)
+             str = "G-" + str;
+           if ((m & KeyModifier.Super) != KeyModifier.None)
+             str = "s-" + str;
+           if ((m & KeyModifier.Hyper) != KeyModifier.None)
+             str = "H-" + str;
+         }
+       return str;
+      }
+    }
+
+    public class KeySeq : Xex.TermValue
+    {
+      public List<Key> keyseq = new List<Key> ();
+
+      public override Xex.TermValue Clone ()
+      {
+       KeySeq ks = new KeySeq ();
+       ks.keyseq.InsertRange (0, keyseq);
+       return ks;
+      }
+
+      public KeySeq () { }
+
+      public KeySeq (MPlist plist)
+      {
+       foreach (MPlist p in plist)
+         {
+           if (p.IsSymbol)
+             keyseq.Add (new Key (p.Symbol));
+           else if (p.IsInteger)
+             keyseq.Add (new Key ((char) p.Integer));
+           else if (p.IsPlist)
+             keyseq.Add (new Key (p.Plist));
+           else
+             throw new Exception ("Invalid Key Sequence: " + plist);
+         }
+      }
+
+      public KeySeq (MText mt) : base ()
+      {
+       for (int i = 0; i < mt.Length; i++)
+         keyseq.Add (new Key ((uint) mt[i]));
+      }
+
+      private static uint parse_integer (string str)
+      {
+       if (Char.IsDigit (str[0]))
+         {
+           if (str[0] == '0' && str.Length > 2 && str[1] == 'x')
+             {
+               uint i = 0;
+               for (int idx = 2; idx < str.Length; idx++)
+                 {
+                   uint c = str[idx];
+                   if (c >= '0' && c <= '9')
+                     i = i * 16 + (c - '0');
+                   else if (c >= 'A' && c <= 'F')
+                     i = i * 16 + 10 + (c - 'A');
+                   else if (c >= 'a' && c <= 'f')
+                     i = i * 16 + 10 + (c - 'a');
+                   else
+                     break;
+                 }
+               return i;
+             }
+           return UInt32.Parse (str);
+         }
+       else if (str[0] == '?')
+         return str[1];
+       return 0;
+      }
+
+      public KeySeq (List<Xex.Term> list)
+       {
+         int len = list.Count;
+
+         for (int i = 0; i < len; i++)
+           {
+             if (list[i].IsInt)
+               keyseq.Add (new Key (list[i].Intval));
+             else if (list[i].IsStr)
+               keyseq.Add (new Key (list[i].Strval));
+             else if (list[i].IsSymbol)
+               keyseq.Add (new Key ((string) list[i].Symval));
+             else
+               throw new Exception ("Invalid key: " + list[i]);
+           }
+       }
+
+      public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+       {
+         Xex.Term term = new Xex.Term (domain, node.FirstChild).Eval (domain);
+         return (term.IsStr ? new KeySeq ((MText) term.Strval)
+                 : new KeySeq (term.Listval));
+       }
+
+      public override string ToString ()
+      {
+       string str;
+
+       foreach (Key key in keyseq)
+         if (key.HasModifier)
+           {
+             str = "(keyseq";
+             foreach (Key k in keyseq)
+               str += " " + k.ToString ();
+             return str + ")";
+           }
+       str = "\"";
+       foreach (Key key in keyseq)             
+         str += key.ToString ();
+       return str + "\"";
+      }
+    }
+
+    public class Variable
     {
       public MSymbol name;
-      private MSymbol[] keys;
-      private MInputAction[] actions;
+      public MText description;
+      public Type type;
+      public object value;
+      public object[] candidates;
+
+      public Variable (MPlist p)
+      {
+       name = p.Symbol;
+       p = p.Next;
+       description = parse_description (p);
+       if (description == null)
+         description = new MText ("No description");
+       else
+         p = p.next;
+       type = (p.IsMText ? typeof (MText)
+               : p.IsInteger ? typeof (int)
+               : p.IsSymbol ? typeof (MSymbol)
+               : typeof (object));
+       value = p.val;
+       p = p.next;
+       candidates = new object[p.Count];
+       for (int i = 0; ! p.IsEmpty; i++, p = p.next)
+         candidates[i] = p.val;
+      }
+
+      private static Type parse_value (XmlNode node, out object value)
+      {
+       string typename = node.Attributes["type"].Value;
+       Type type;
+
+       if (typename == "integer")
+         {
+           int i;
+           if (! Int32.TryParse (node.InnerText, out i))
+             i = 0;
+           value = i;
+           type = typeof (int);
+         }
+       else if (typename == "string")
+         {
+           MText mt = node.InnerText;
+           value = mt;
+           type = typeof (MText);
+         }
+       else if (typename == "symbol")
+         {
+           MSymbol sym = node.InnerText;
+           value = sym;
+           type = typeof (MSymbol);
+         }
+       else
+         {
+           value = null;
+           type = typeof (object);
+         }
+       return type;
+      }
+
+      public Variable (XmlNode node)
+      {
+       name = node.Attributes["id"].Value;
+       for (node = node.FirstChild; node != null; node = node.NextSibling)
+         if (node.NodeType == XmlNodeType.Element)
+           {
+             if (node.Name == "description")
+               description = parse_description (node);
+             else if (node.Name == "value")
+               type = parse_value (node, out value);
+             else if (node.Name == "valiable-value-candidate")
+               {
+                 XmlNodeList n_list = node.ChildNodes;
+                 candidates = new object[n_list.Count];
+                 for (int i = 0; i < n_list.Count; i++)
+                   {
+                     object val;
+                     parse_value (n_list[i], out val);
+                     candidates[i] = val;
+                   }
+               }
+           }
+      }
+
+      public override string ToString ()
+      {
+       return ("(" + name + " \"" + (string) description
+               + "\" " + type + " " + value + " " + candidates + ")");
+      }
     }
 
-    private class MInputMethodBranch
+    public class Command
     {
       public MSymbol name;
-      private MInputAction[] actions;
+      public MText description;
+      public List<KeySeq> keys;
+
+      public Command (MPlist p)
+      {
+       name = p.Symbol;
+       p = p.Next;
+       description = parse_description (p);
+       if (description == null)
+         description = "No description";
+       keys = new List<KeySeq> ();
+       Console.WriteLine ("cmd:" + p);
+       for (p = p.next; ! p.IsEmpty; p = p.next)
+         {
+           if (p.IsMText)
+             keys.Add (new KeySeq (p.Text));
+           else if (p.IsPlist)
+             keys.Add (new KeySeq (p.Plist));
+         }
+      }
+
+      public Command (XmlNode node)
+      {
+       name = node.Attributes[0].Value;
+       keys = new List<KeySeq> ();
+       for (node = node.FirstChild; node != null; node = node.NextSibling)
+         {
+           if (node.Name == "description")
+             description = parse_description (node);
+           else if (node.Name == "keyseq")
+             keys.Add ((KeySeq) KeySeq.parser (null, node));
+         }
+      }
+
+      public override string ToString ()
+      {
+       string str = "(" + name + " \"" + (string) description;
+       foreach (KeySeq keyseq in keys)
+         str += " " + keyseq;
+       return str + ")";
+      }
     }
 
-    private class MInputMethodState
+    internal class Plugin
+    {
+      private string name;
+      private Assembly assembly;
+      private Type plugin_type;
+
+      public Plugin (string name)
+      {
+       this.name = name;
+      }
+
+      public MethodInfo GetMethod (Xex.Symbol name)
+      {
+       if (assembly == null)
+         {
+           assembly = Assembly.LoadFrom (name + ".dll");
+           plugin_type = assembly.GetType ("M17n.MInputMethod.Plugin");
+         }
+
+       MethodInfo info = plugin_type.GetMethod ((string) name);
+       if (info == null)
+         throw new Exception ("Invalid plugin method: " + name);
+       return info;
+      }
+
+      public override string ToString ()
+      {
+       return String.Format ("(module {0}", name);
+      }
+    }
+
+    internal class PluginMethod : Xex.Function
+    {
+      private Plugin plugin;
+      private MethodInfo method_info;
+      object[] parameters = new object[2];
+
+      public PluginMethod (Plugin plugin, string name)
+       : base ((Xex.Symbol) name, 0, -1)
+       {
+         this.plugin = plugin;
+       }
+
+      public override Xex.Term Call (Xex.Domain domain, Xex.Variable vari,
+                                    Xex.Term[] args)
+      {
+       args = (Xex.Term[]) args.Clone ();
+       for (int i = 0; i < args.Length; i++)
+         {
+           args[i] = args[i].Eval (domain);
+           if (domain.Thrown)
+             return args[i];
+         }
+       if (method_info == null)
+         method_info = plugin.GetMethod (name);
+       parameters[0] = domain.context;
+       parameters[1] = args;
+       return (Xex.Term) method_info.Invoke (null, parameters);
+      }
+    }
+
+    internal abstract class Marker : Xex.TermValue
+    {
+      private MSymbol name;
+
+      public Marker (MSymbol name)
+       {
+         this.name = name;
+       }
+
+      public abstract int Position (MInputContext ic);
+      public abstract void Mark (MInputContext ic);
+
+      public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+      {
+       MSymbol name = node.InnerText;
+       
+       return Get ((MInputContext) domain.context, name);
+      }
+
+      public class Named : Marker
+      {
+       int pos;
+
+       public Named (MSymbol name) : this (name, 0) { }
+
+       private Named (MSymbol name, int p) : base (name) { pos = p; }
+
+       public override int Position (MInputContext ic) { return pos; }
+
+       public override void Mark (MInputContext ic) { pos = ic.cursor_pos; } 
+
+       public override Xex.TermValue Clone ()
+       {
+         return new Named (name, pos);
+       }
+      }
+     
+      public class Predefined : Marker
+      {
+       public Predefined (MSymbol name) : base (name) { }
+       
+       public override int Position (MInputContext ic)
+       {
+         switch (name.Name[1]) {
+         case '<': return 0;
+         case '>': return ic.preedit.Length;
+         case '-': return ic.cursor_pos - 1;
+         case '+': return ic.cursor_pos + 1;
+         case '[':
+           if (ic.cursor_pos > 0)
+             {
+               int pos = ic.cursor_pos;
+               int to;
+               ic.preedit.FindProp (Mcandidates, pos - 1, out pos, out to);
+               return pos;
+             }
+           return 0;
+         case ']':
+           if (ic.cursor_pos < ic.preedit.Length - 1)
+             {
+               int pos = ic.cursor_pos;
+               int from;
+               ic.preedit.FindProp (Mcandidates, pos, out from, out pos);
+               return pos;
+             }
+           return ic.preedit.Length;
+         default:
+           return name.Name[1] - '0';
+         }
+       }
+      
+       public override void Mark (MInputContext ic)
+       {
+         throw new Exception ("Can't set predefined marker: " + name);
+       }
+
+       public override Xex.TermValue Clone ()
+       {
+         return new Predefined (name);
+       }
+      }
+
+      static internal Dictionary<MSymbol,Predefined> predefined_markers;
+
+      static Marker ()
+      {
+       predefined_markers = new Dictionary<MSymbol,Predefined> ();
+       MSymbol[] symlist = new MSymbol[] {"@<", "@>", "@-", "@+", "@[", "@]",
+                                          "@0", "@1", "@2", "@3", "@4",
+                                          "@5", "@6", "@7", "@8", "@9" };
+       foreach (MSymbol s in symlist)
+         predefined_markers[s] = new Predefined (s);
+      }
+
+      public static Marker Get (MInputContext ic, MSymbol name)
+      {
+       Predefined pred;
+       Marker m;
+
+       if (predefined_markers.TryGetValue (name, out pred))
+         return pred;
+       if (name.Name[0] == '@')
+         throw new Exception ("Invalid marker name: " + name);
+       m = (Marker) ic.markers.Get (name);
+       if (m == null)
+         {
+           m = new Named (name);
+           ic.markers.Put (name, m);
+         }
+       return m;
+      }
+    }
+      
+    internal class Candidates
+    {
+      private class Block
+      {
+       public int Index;
+       public object Data;
+
+       public Block (int index, Xex.Term term)
+       {
+         Index = index;
+         if (term.IsStr)
+           Data = (MText) term.Strval;
+         else
+           {
+             MPlist plist = new MPlist ();
+             MPlist p = plist;
+             foreach (Xex.Term t in term.Listval)
+               p = p.Add (MSymbol.mtext, (MText) t.Strval);
+             Data = plist;
+           }
+       }
+
+       public Block (int index, MPlist plist)
+       {
+         Index = index;
+         if (plist.IsMText)
+           Data = plist.Text;
+         else if (plist.IsPlist)
+           Data = plist.Plist;
+         else
+           throw new Exception ("Invalid candidate: " + plist);
+       }
+
+       public int Count
+       {
+         get { return (Data is MText
+                       ? ((MText) Data).Length
+                       : ((MPlist) Data).Count); }
+       }
+
+       public object this[int i]
+       {
+         get {
+           if (Data is MText) return ((MText) Data)[i];
+           return  ((MPlist) Data)[i];
+         }
+       }
+      }
+
+      private Block[] blocks;
+      private int row = 0;
+      private int index = 0;
+      public object[] group;
+
+      private bool IsFixed { get { return group != null; } }
+      private int Total {
+       get {
+         Block last = blocks[blocks.Length - 1];
+         return last.Index + last.Count; }
+      }
+
+      public int Column {
+       get { return (IsFixed ? index % group.Length
+                     : index - blocks[row].Index); }
+      }
+
+      public object Group {
+       get { return (IsFixed ? group : blocks[row].Data); }
+      }
+
+      public int GroupLength
+      {
+       get {
+         if (IsFixed)
+           {
+             int nitems = group.Length;
+             int start = index - (index % nitems);
+             int total = Total;
+             return (start + nitems <= total ? nitems : total - start);
+           }
+         return blocks[row].Count;
+       }
+      }
+
+      public object Current {
+       get {
+         return (IsFixed ? group[index % group.Length]
+                 : blocks[row][index - blocks[row].Index]);
+       }
+      }
+
+      public Candidates (MPlist list, int column)
+      {
+       int nblocks = list.Count;
+
+       blocks = new Block[nblocks];
+       for (int i = 0, start = 0; i < nblocks; i++, list = list.next)
+         start += (blocks[i] = new Block (index, list)).Count;
+       if (column > 0)
+         group = new object[column];
+      }
+
+      public Candidates (List<Xex.Term> list, int column)
+      {
+       int nblocks = list.Count;
+
+       blocks = new Block[nblocks];
+       for (int i = 0, start = 0; i < nblocks; i++)
+         start += (blocks[i] = new Block (index, list[i])).Count;
+       if (column > 0)
+         group = new object[column];
+      }
+
+      public static void Detach (MInputContext ic)
+      {
+       ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+       ic.candidates = null;
+       ic.changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                      | CandidateAll);
+      }
+
+      // Fill the array "group" by candidates stating from INDEX.
+      // INDEX must be a multiple of "column".  Set NTIMES to the
+      // number of valid candidates in "group".  Update "block" if
+      // necessary.  Return "group".
+
+      private int fill_group (int start)
+      {
+       int nitems = group.Length;
+       int r = row;
+       Block b = blocks[r];
+
+       if (start < b.Index)
+         while (start < b.Index)
+            b = blocks[--r];
+       else
+         while (start >= b.Index + b.Count)
+           b = blocks[++r];
+       row = r;
+
+       int count = b.Count;
+       start -= b.Index;
+       for (int i = 0; i < nitems; i++, start++)
+         {
+           if (start >= count)
+             {
+               r++;
+               if (r == blocks.Length)
+                 return i;
+               b = blocks[r];
+               count = b.Count;
+               start = 0;
+             }
+           group[i] = b[start];
+         }
+       return nitems;
+      }
+
+      // Update "row" to what contains the first candidate of
+      // the previous candidate-group, update "current_index", and
+      // update "group" if necessary.  Return the previous
+      // candidate-group.  Set NITEMS to the number of valid
+      // candidates contained in that group.
+
+      public int PrevGroup ()
+      {
+       int nitems;
+       int col = Column;
+
+       if (IsFixed)
+         {
+           nitems = group.Length;
+           if ((index -= col + nitems) < 0)
+             index = (Total / nitems) * nitems;
+           nitems = fill_group (index);
+         }
+       else
+         {
+           row = row > 0 ? row-- : blocks.Length - 1;
+           nitems = blocks[row].Count;
+           index = blocks[row].Index;
+         }
+       index += col < nitems ? col : nitems - 1;
+       return nitems;
+      }
+
+      public int NextGroup ()
+      {
+       int nitems;
+       int col = Column;
+
+       if (IsFixed)
+         {
+           nitems = group.Length;
+           if ((index += nitems - col) >= Total)
+             index = 0;
+           nitems = fill_group (index);
+         }
+       else
+         {
+           row = row < blocks.Length - 1 ? row + 1 : 0;
+           nitems = blocks[row].Count;
+           index = blocks[row].Count;
+         }
+       index += col < nitems ? col : nitems - 1;
+       return nitems;
+      }
+
+      public void Prev ()
+      {
+       int col = Column;
+
+       if (col == 0)
+         {
+           int nitems = PrevGroup ();
+           index += col < nitems - 1 ? col : nitems - 1;
+         }
+       else
+         index--;
+      }
+
+      public void Next ()
+      {
+       int col = Column;
+       int nitems = GroupLength;
+
+       if (col == nitems - 1)
+         {
+           nitems = NextGroup ();
+           index -= Column;
+         }
+       else
+         index++;
+      }
+
+      public void First ()
+      {
+       index -= Column;
+      }
+
+      public void Last ()
+      {
+       index += GroupLength - (Column + 1);
+      }
+
+      public void Select (int col)
+      {
+       int maxcol = GroupLength - 1;
+       if (col > maxcol)
+         col = maxcol;
+       index = index - Column + col;
+      }
+    }
+
+    internal abstract class Selector : Xex.TermValue
+    {
+      private Selector () { }
+
+      public abstract void Select (Candidates candidates);
+
+      public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+      {
+       MSymbol name = node.InnerText;
+       Predefined pred;
+
+       if (predefined_selectors.TryGetValue (name, out pred))
+         return pred;
+       if (name.Name[0] == '@')
+         throw new Exception ("Invalid selector name: " + name);
+       int index;
+       if (! Int32.TryParse (node.InnerText, out index))
+         throw new Exception ("Invalid selector name: " + name);
+       return new Numbered (index);
+      }
+
+      public override Xex.TermValue Clone () { return this; }
+
+      public class Numbered : Selector
+      {
+       int index;
+
+       public Numbered (int index) { this.index = index; }
+
+       public override void Select (Candidates can) { can.Select (index); }
+      }
+
+      public class Predefined : Selector
+      {
+       private char tag;
+
+       internal Predefined (MSymbol sym) { this.tag = sym.Name[1]; }
+
+       public override void Select (Candidates can)
+       {
+         switch (tag)
+           {
+           case '<': candidates.First (); break;
+           case '>': candidates.Last (); break;
+           case '-': candidates.Prev (); break;
+           case '+': candidates.Next (); break;
+           case '[': candidates.PrevGroup (); break;
+           case ']': candidates.NextGroup (); break;
+           default: break;
+           }
+       }
+      }
+
+      static new Dictionary<MSymbol, Predefined> predefined_selectors;
+
+      static Selector ()
+       {
+         predefined_selectors = new Dictionary<MSymbol, Predefined> ();
+         MSymbol[] symlist = new MSymbol[] { "@<", "@=", "@>", "@-", "@+",
+                                             "@[", "@]" };
+         foreach (MSymbol s in symlist)
+           predefined_selectors[s] = new Predefined (s);
+       }
+    }
+
+    internal class Map
     {
-      public MText title;
       public MSymbol name;
-      public MInputMethodBranch[] branches;
+      public Dictionary<Key, Map> submaps;
+      public Xex.Term actions;
+
+      public void Add (KeySeq keys, int index, Xex.Term actions)
+      {
+       Map sub = null;
+
+       if (submaps == null)
+         submaps = new Dictionary<Key, Map> ();
+       else
+         submaps.TryGetValue (keys.keyseq[index], out sub);
+       if (sub == null)
+         {
+           Key key = keys.keyseq[index];
+           submaps[key] = sub = new Map ();
+         }
+       if (index + 1 < keys.keyseq.Count)
+         sub.Add (keys, index + 1, actions);
+       else
+         this.actions = actions;
+      }
+
+      public Xex.Term Lookup (KeySeq keys, int index)
+      {
+       Map sub;
+
+       if (index + 1 == keys.keyseq.Count)
+         return actions;
+       if (submaps.TryGetValue (keys.keyseq[index], out sub))
+         return sub.Lookup (keys, index + 1);
+       return Tnil;
+      }
+
+      private void describe (MText mt, KeySeq keyseq)
+      {
+       if (keyseq.keyseq.Count > 0)
+         {
+           mt.Cat (" (").Cat (keyseq.ToString ());
+           mt.Cat (' ').Cat (actions.ToString ());
+           mt.Cat (')');           
+         }
+       if (submaps != null)
+         foreach (KeyValuePair<Key, Map> kv in submaps)
+           {
+             keyseq.keyseq.Add (kv.Key);
+             kv.Value.describe (mt, keyseq);
+             keyseq.keyseq.RemoveAt (keyseq.keyseq.Count - 1);
+           }
+      }
+
+      public override string ToString ()
+      {
+       MText mt = "(" + name.Name;
+       KeySeq keyseq = new KeySeq ();
+
+       describe (mt, keyseq);
+       mt.Cat (')');
+       return (string) mt;
+      }
     }
 
-    public readonly MSymbol language;
-    public readonly MSymbol name;
-    public readonly MSymbol subname;
+    internal class State
+    {
+      public MSymbol name;
+      public MText title;
+      public MPlist branches = new MPlist ();
+
+      public State (MSymbol name)
+      {
+       this.name = name;
+      }
 
+      public override string ToString ()
+      {
+       MText mt = "(" + name.Name;
+
+       if (title != null)
+         mt.Cat (" \"" + title + "\"");
+       for (MPlist p = branches; ! p.IsEmpty; p = p.next)
+         mt.Cat (" (" + p.Key + " " + (Xex) p.Val + ")");
+       return (string) mt + ")";
+      }
+    }
+
+    // Instance members
+    internal Xex.Domain domain = new Xex.Domain (im_domain, null);
+
+    private LoadStatus load_status = LoadStatus.None;
+    private MDatabase.Tag tag;
     private MDatabase mdb;
+
     private MText description;
-    private MText title;
-    private MPlist commands;
-    private MPlist variables;
-    private MPlist maps;
-    private MPlist states;
-    private MPlist macros;
-    private MPlist externals;
+    internal MText title;
+    internal Command[] commands;
+    internal Xex.Symbol[] var_names;
+    internal Dictionary<MSymbol, Plugin> plugins;
+    internal Dictionary<MSymbol, Map> maps;
+    internal MPlist states;
+
+    static MInputMethod ()
+    {
+      im_domain.DefTerm ("keyseq", KeySeq.parser);
+      im_domain.DefTerm ("marker", Marker.parser);
+      im_domain.DefTerm ("selector", Selector.parser);
+
+      im_domain.DefSubr (Finsert, "insert", true, 1, 1);
+      im_domain.DefSubr (Finsert_candidates, "candidates", true, 1, -1);
+      im_domain.DefSubr (Fdelete, "delete", true, 1, 1);
+      im_domain.DefSubr (Fselect, "select", true, 1, 1);
+      im_domain.DefSubr (Fshow, "show", true, 0, 0);
+      im_domain.DefSubr (Fhide, "hide", true, 0, 0);
+      im_domain.DefSubr (Fmove, "move", true, 1, 1);
+      im_domain.DefSubr (Fmark, "mark", true, 1, 1);
+      im_domain.DefSubr (Fpushback, "pushback", true, 1, 1);
+      im_domain.DefSubr (Fpop, "pop", true, 0, 0);
+      im_domain.DefSubr (Fundo, "undo", true, 0, 1);
+      im_domain.DefSubr (Fcommit, "commit", true, 0, 0);
+      im_domain.DefSubr (Funhandle, "unhandle", true, 0, 0);
+      im_domain.DefSubr (Fshift, "shift", true, 1, 1);
+      im_domain.DefSubr (Fmarker, "marker", true, 1, 1);
+      im_domain.DefSubr (Fchar_at, "char-at", true, 1, 1);
+
+      MDatabase.Tag tag = new MDatabase.Tag (Minput_method, "*", "*", "*");
+      List<MDatabase> list = MDatabase.List (tag);
+      M17n.DebugPrint ("Found {0} input methods\n", list.Count);
+      foreach (MDatabase mdb in list)
+       im_table[mdb.tag] = new MInputMethod (mdb.tag);
+    }
 
-    public MInputMethod (MSymbol language, MSymbol name, MSymbol extra)
+    // Constructor
+    private MInputMethod (MDatabase.Tag tag)
     {
-      MDatabase.Tag tag = new MDatabase.Tag (language, name, extra);
+      this.tag = tag;
+    }
+
+    // Instance Properties
+    public MSymbol Language { get { return tag[1]; } }
+    public MSymbol Name { get { return tag[2]; } }
+    public MSymbol SubName { get { return tag[3]; } }
 
+    public bool Info (out MText description,
+                     out MText title,
+                     out Xex.Variable[] variables,
+                     out Command[] commands)
+    {
+      if ((load_status & LoadStatus.Header) != LoadStatus.Header
+         && ! load_header ())
+       {
+         description = null;
+         title = null;
+         variables = null;
+         commands = null;
+         return false;
+       }
+      description = this.description;
+      title = this.title;
+      if (var_names == null)
+       variables = null;
+      else
+       {
+         variables = new Xex.Variable[var_names.Length];
+         int i = 0;
+         foreach (Xex.Symbol name in var_names)
+           variables[i++] = domain.GetVar (name, false);
+       }
+      commands = this.commands;
+      return true;
+    }
+
+    public static MInputMethod Find (MSymbol language, MSymbol name)
+    {
+      return Find (language, name, MSymbol.nil);
+    }
+
+    public static MInputMethod Find (MSymbol language, MSymbol name,
+                                    MSymbol subname)
+    {
+      MDatabase.Tag tag = new MDatabase.Tag (Minput_method, language,
+                                            name, subname);
+      MInputMethod im;
+
+      return (im_table.TryGetValue (tag, out im) ? im : null);
+    }
+
+    public bool Open ()
+    {
+      return ((load_status == LoadStatus.Full) || load_body ());
+    }
+
+    public static MInputMethod[] List ()
+    {
+      MInputMethod[] array = new MInputMethod[im_table.Count];
+      int i = 0;
+
+      foreach (KeyValuePair<MDatabase.Tag, MInputMethod> kv in im_table)
+       array[i++] = kv.Value;
+      return array;
+    }
+
+    private bool load_header ()
+    {
+      mdb = MDatabase.Find (tag);
+      if (mdb == null)
+       return false;
+      mdb.name_table = Xex.Symbol.Table;
+      try {
+       MSymbol format = mdb.Format;
+
+       if (format == MSymbol.plist)
+         load ((MPlist) mdb.Load (Mmap), false);
+       else
+         {
+           XmlDocument doc = (XmlDocument) mdb.Load (Mmap_list);
+           load (doc.DocumentElement, false);
+         }
+      } catch (Exception e) {
+       Console.WriteLine ("{0}\n", e);
+       load_status = LoadStatus.Error;
+       return false;
+      }
+      load_status |= LoadStatus.Header;
+      return true;
+    }
+
+    private bool load_body ()
+    {
       mdb = MDatabase.Find (tag);
       if (mdb == null)
-       throw new Exception (String.Format ("Input method {0} not available",
-                                           tag));
-      MPlist plist = (MPlist) mdb.Load ();
+       return false;
+      mdb.name_table = Xex.Symbol.Table;
+      try {
+       object obj = mdb.Load ();
+       if (obj is MPlist)
+         load ((MPlist) obj, true);
+       else
+         load ((XmlDocument) obj, true);
+      } catch (Exception e) {
+       Console.WriteLine (e);
+       load_status = LoadStatus.Error;
+       return false;
+      }
+      load_status = LoadStatus.Full;
+      return true;
+    }
+
+    private void load (MPlist plist, bool full)
+    {
+      maps = new Dictionary<MSymbol, Map> ();
+      states = new MPlist ();
+
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+           if (pl.IsSymbol)
+             {
+               MSymbol sym = pl.Symbol;
+
+               pl = pl.next;
+               if (sym == Mdescription)
+                 description = parse_description (pl);
+               else if (sym == Mtitle)
+                 {
+                   if (pl.IsMText)
+                     title = pl.Text;
+                 }
+               else if (sym == Mvariable)
+                 parse_variables (pl);
+               else if (sym == Mcommand)
+                 parse_commands (pl);
+               else if (full)
+                 {
+                   if (sym == Mmodule)
+                     parse_plugins (pl);
+                   else if (sym == Minclude)
+                     parse_include (pl);
+                   else if (sym == Mmacro)
+                     parse_macros (pl);
+                   else if (sym == Mmap)
+                     parse_maps (pl);
+                   else if (sym == Mstate)
+                     parse_states (pl);
+                 }
+             }
+         }
+      if (description == null)
+       description = (MText) "No description";
+      if (title == null)
+       title = new MText (tag[2].Name);
+      if (commands == null)
+       commands = new Command[0];
+      if (! full)
+       return;
+      if (states.IsEmpty)
+       {
+         State state = new State ((MSymbol) "init");
+         plist = new MPlist ();
+         foreach (KeyValuePair<MSymbol, Map>kv in maps)
+           state.branches.Add (kv.Key, null);
+         states.Add (state.name, state);
+       }
+    }
+
+    private void load (XmlNode node, bool full)
+    {
+      bool skip_header = load_status == LoadStatus.Header;
+
+      maps = new Dictionary<MSymbol, Map> ();
+      states = new MPlist ();
+
+      if (node.NodeType == XmlNodeType.Document)
+       node = node.FirstChild;
+      while (node.NodeType != XmlNodeType.Element)
+       node = node.NextSibling;
+      for (node = node.FirstChild; node != null; node = node.NextSibling)
+       {
+         if (node.NodeType != XmlNodeType.Element)
+           continue;
+         if (! skip_header)
+           {
+             if (node.Name == "description")
+               description = parse_description (node);
+             else if (node.Name == "title")
+               title = parse_title (node);
+             else if (node.Name == "variable-list")
+               parse_variables (node);
+             else if (node.Name == "command-list")
+               parse_commands (node);
+           }
+         else if (full)
+           {
+             if (node.Name == "module-list")
+               parse_plugins (node);
+             else if (node.Name == "macro-list")
+               parse_macros (node);
+             else if (node.Name == "map-list")
+               parse_maps (node);
+             else if (node.Name == "state-list")
+               parse_states (node);
+           }
+       }
+      if (description == null)
+       description = (MText) "No description";
+      if (title == null)
+       title = new MText (tag[2].Name);
+      if (commands == null)
+       commands = new Command[0];
+      if (! full)
+       return;
+      if (states.IsEmpty)
+       {
+         State state = new State ((MSymbol) "init");
+         foreach (KeyValuePair<MSymbol, Map>kv in maps)
+           state.branches.Add (kv.Key, null);
+         states.Add (state.name, state);
+       }
+    }
+
+    private static void transform (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       {
+         if (plist.IsMText)
+           {
+             MPlist p = new MPlist ();
+             p.Add (MSymbol.symbol, Minsert);
+             p.Add (MSymbol.mtext, plist.Text);
+             plist.Set (MSymbol.plist, p);
+           }
+         else if (plist.IsInteger)
+           {
+             MPlist p = new MPlist ();
+             p.Add (MSymbol.symbol, Minsert);
+             p.Add (MSymbol.integer, plist.Integer);
+             plist.Set (MSymbol.plist, p);
+           }
+         else if (plist.IsPlist)
+           {
+             MPlist pl = plist.Plist;
+
+             if (pl.IsSymbol)
+               {
+                 if (pl.Symbol == Madd)
+                   pl.Set (MSymbol.symbol, (MSymbol) "+=");
+                 else if (pl.Symbol == Msub)
+                   pl.Set (MSymbol.symbol, (MSymbol) "-=");
+                 else if (pl.Symbol == Mmul)
+                   pl.Set (MSymbol.symbol, (MSymbol) "*=");
+                 else if (pl.Symbol == Mdiv)
+                   pl.Set (MSymbol.symbol, (MSymbol) "/=");
+                 else if (pl.Symbol == Minsert)
+                   {
+                     // (insert (CANDIDATES ...))
+                     //   => (candidates CANDIDATES ...)
+                     if (pl.next.IsPlist)
+                       {
+                         pl.Set (MSymbol.symbol, Mcandidates);
+                         pl = pl.next;
+                         MPlist p = pl.Plist;
+                         pl.Set (p.key, p.val);
+                         for (p = p.next; ! p.IsEmpty; p = p.next);
+                         pl.Add (p.key, p.val);
+                       }
+                   }
+                 else if (pl.Symbol == Mif)
+                   {
+                     pl = pl.next;
+                     if (! pl.IsEmpty)
+                       transform (pl.next);
+                   }
+                 else if (pl.Symbol == Mcond)
+                   {
+                     for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+                       if (pl.IsPlist)
+                         {
+                           MPlist p = pl.Plist;
+
+                           if (p.IsPlist)
+                             transform (p);
+                           else
+                             transform (p.next);
+                         }
+                   }
+                 else if (pl.Symbol == Mdelete
+                          || pl.Symbol == Mmove
+                          || pl.Symbol == Mmark)
+                   {
+                     pl = pl.next;
+                     if (pl.IsSymbol)
+                       {
+                         MSymbol sym = pl.Symbol;
+                         MPlist p = new MPlist ();
+                         p.Add (MSymbol.symbol, Mmarker);
+                         p.Add (MSymbol.symbol, sym);
+                         pl.Set (MSymbol.plist, p);
+                       }
+                   }
+                 else if (pl.Symbol == Mpushback)
+                   {
+                     pl = pl.next;
+                     if (pl.IsPlist)
+                       pl.Plist.Push (MSymbol.symbol, Mkeyseq);
+                   }
+               }
+             else if (pl.IsMText)
+               {
+                 // (CANDIDATES ...) => (candidates CANDIDATES ...)
+                 pl.Push (MSymbol.symbol, Mcandidates);
+               }
+           }
+         else if (plist.IsSymbol)
+           {
+             MSymbol sym = plist.Symbol;
+
+             if (sym.Name.Length >= 3
+                 && sym.Name[0] == '@'
+                 && (sym.Name[1] == '-' || sym.Name[1] == '+'))
+               {
+                 int pos = int.Parse (sym.Name.Substring (1));
+                 MPlist p = new MPlist ();
+
+                 if (pos == 0)
+                   {
+                     p.Add (MSymbol.symbol, Msurrounding_text_p);
+                   }
+                 else
+                   {
+                     if (sym.Name[1] == '+')
+                       pos--;
+                     p.Add (MSymbol.symbol, Mchar_at);
+                     p.Add (MSymbol.integer, pos);
+                   }
+                 plist.Set (MSymbol.plist, p);
+               }
+           }
+       }
+    }
+
+    private static MText parse_description (MPlist plist)
+    {
+      if (plist.IsMText)
+       return plist.Text;
+      if (plist.IsPlist)
+       {
+         plist = plist.Plist;
+         if (plist.IsSymbol && plist.Symbol == (MSymbol) "_"
+             && plist.next.IsMText)
+           return plist.next.Text;
+       }
+      return null;
+    }
+
+    private static MText parse_description (XmlNode node)
+    {
+      if (node.HasChildNodes)
+       node = node.FirstChild;
+      return node.InnerText;
+    }
+
+    private static MText parse_title (XmlNode node)
+    {
+      return node.InnerText;
+    }
+
+    private void new_variable (Xex.Symbol name, string desc, int val,
+                              MPlist pl, Xex.Variable vari)
+    {
+      int[] range;
+
+      if (pl.IsEmpty)
+       range = null;
+      else
+       {
+         range = new int[pl.Count * 2];
+         for (int i = 0; i < range.Length; i++)
+           {
+             if (pl.IsPlist)
+               {
+                 MPlist p = pl.Plist;
+
+                 if (! p.IsInteger || ! p.next.IsInteger)
+                   throw new Exception ("Invalid range: " + p);
+                 range[i * 2] = p.Integer;
+                 range[i * 2 + 1] = p.next.Integer;
+               }
+             else if (pl.IsInteger)
+               range[i * 2] = range[i * 2 + 1] = pl.Integer;
+             else
+               throw new Exception ("Invalid range: " + pl);
+           }
+       }
+      if (vari == null)
+       domain.Defvar (new Xex.Variable.Int (name, desc, val, range));
+      else
+       {
+         Xex.Term term = new Xex.Term (val);
+         vari.Value = term;
+         vari.DefaultValue = term;
+         vari.Range = range;
+       }
+    }
+
+    private void new_variable (Xex.Symbol name, string desc, MText val,
+                              MPlist pl, Xex.Variable vari)
+    {
+      string[] range;
+
+      if (pl.IsEmpty)
+       range = null;
+      else
+       {
+         range = new string[pl.Count * 2];
+         for (int i = 0; i < range.Length; i++)
+           {
+             if (pl.IsMText)
+               range[i] = (string) pl.Text;
+             else
+               throw new Exception ("Invalid range: " + pl);
+           }
+       }
+      if (vari == null)
+       domain.Defvar (new Xex.Variable.Str (name, desc, (string) val, range));
+      else
+       {
+         Xex.Term term = new Xex.Term ((string) val);
+         vari.Value = term;
+         vari.DefaultValue = term;
+         vari.Range = range;
+       }
+    }
+
+    private void new_variable (Xex.Symbol name, string desc, MSymbol val,
+                              MPlist pl, Xex.Variable vari)
+    {
+      Xex.Symbol[] range;
+      Xex.Symbol sym = val.Name;
+
+      if (pl.IsEmpty)
+       range = null;
+      else
+       {
+         range = new Xex.Symbol[pl.Count * 2];
+         for (int i = 0; i < range.Length; i++)
+           {
+             if (pl.IsSymbol)
+               range[i] = pl.Symbol.Name;
+             else
+               throw new Exception ("Invalid range: " + pl);
+           }
+       }
+      if (vari == null)
+       domain.Defvar (new Xex.Variable.Sym (name, desc, sym, range));
+      else
+       {
+         Xex.Term term = new Xex.Term (sym);
+         vari.Value = term;
+         vari.DefaultValue = term;
+         vari.Range = range;
+       }
+    }
+
+    private Xex.Variable get_global_var (Xex.Symbol name)
+    {
+      if (im_global == null || this != im_global)
+       {
+         tag = new MDatabase.Tag (Minput_method, MSymbol.t, MSymbol.nil,
+                                  "global");
+         im_global = im_table[tag];
+         if (! im_global.Open ())
+           throw new Exception ("Failed to load global"); 
+       }
+      return im_global.domain.GetVar (name, false);
+    }
+
+    private void parse_variables (MPlist plist)
+    {
+      var_names = new Xex.Symbol[plist.Count];
+
+      for (int i = 0; ! plist.IsEmpty; i++, plist = plist.next)
+       {
+         if (! plist.IsPlist || ! plist.Plist.IsSymbol)
+           throw new Exception ("Invalid variable: " + plist);
+
+         MPlist p = plist.Plist;
+         Xex.Symbol name = (Xex.Symbol) p.Symbol.Name;
+         var_names[i] = name;
+         p = p.next;
+         string desc = (string) parse_description (p);
+         Xex.Variable vari = get_global_var (name);
+         if (vari != null)
+           domain.Defvar (vari);
+         if (desc != null)
+           p = p.next;
+         if (! p.IsEmpty)
+           {
+             if (p.IsInteger)
+               new_variable (name, desc, p.Integer, p.next, vari);
+             else if (p.IsMText)
+               new_variable (name, desc, p.Text, p.next, vari);
+             else if (p.IsSymbol)
+               new_variable (name, desc, p.Symbol, p.next, vari);
+             else
+               throw new Exception ("Invalid variable type: " + p.val);
+           }
+       }
+    }
+
+    private void parse_variables (XmlNode node)
+    {
+      XmlNodeList node_list = node.ChildNodes;
+
+      var_names = new Xex.Symbol[node_list.Count];
+      for (int i = 0; i < node_list.Count; i++)
+       {
+         Xex.Symbol name = node_list[i].Attributes[0].Value;
+         Xex.Variable vari = get_global_var (name);
+         if (vari != null)
+           domain.Defvar (vari);
+         domain.Defvar (node_list[i]);
+         var_names[i] = name;
+       }
+    }
+
+    private void parse_commands (MPlist plist)
+    {
+      commands = new Command[plist.Count];
+
+      for (int i = 0; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist && plist.Plist.IsSymbol)
+         commands[i++] = new Command (plist.Plist);
+    }
+
+    private void parse_commands (XmlNode node)
+    {
+      XmlNodeList node_list = node.ChildNodes;
+
+      commands = new Command[node_list.Count];
+      for (int i = 0; i < node_list.Count; i++)
+       {
+         if (node_list[i].NodeType == XmlNodeType.Element)
+           commands[i] = new Command (node_list[i]);
+       }
+    }
+
+    private void parse_plugins (MPlist plist)
+    {
+      plugins = new Dictionary<MSymbol, Plugin> ();
+
+      for (; ! plist.IsEmpty; plist = plist.Next)
+       {
+         MPlist p = plist.Plist;
+         MSymbol sym = p.Symbol;
+         Plugin plugin = new Plugin (sym.Name);
+
+         for (p = p.next; ! p.IsEmpty; p = p.next)
+           {
+             Xex.Function func = new PluginMethod (plugin, p.Symbol.Name);
+             domain.Defun (func);
+           }
+       }
+    }
+
+    private void parse_plugins (XmlNode node)
+    {
+      plugins = new Dictionary<MSymbol, Plugin> ();
+
+      foreach (XmlNode n in node.ChildNodes)
+       {
+         Plugin plugin = new Plugin (n.Attributes[0].Value);
+         foreach (XmlNode nn in n.ChildNodes)
+           {
+             Xex.Function func = new PluginMethod (plugin,
+                                                   nn.Attributes[0].Value);
+             domain.Defun (func);
+           }
+       }
+    }
+
+    private void parse_macros (XmlNode node)
+    {
+      for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
+       if (nn.NodeType == XmlNodeType.Element)
+         domain.Defun (nn, true);
+      for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
+       if (nn.NodeType == XmlNodeType.Element)
+         domain.Defun (nn, false);
+    }
+
+    private void parse_maps (XmlNode node)
+    {
+    }
+
+    private void parse_states (XmlNode node)
+    {
+    }
+
+    private void parse_include (MPlist plist)
+    {
+      if (! plist.IsPlist)
+       return;
+      MPlist p = plist.Plist;
+      MSymbol language, name, subname;
+      language = p.Symbol;
+      p = p.next;
+      if (! p.IsSymbol)
+       name = subname = MSymbol.nil;
+      else
+       {
+         name = p.Symbol;
+         p = p.next;
+         if (! p.IsSymbol)
+           subname = MSymbol.nil;
+         else
+           subname = p.Symbol;
+       }
+
+      MInputMethod im = MInputMethod.Find (language, name, subname);
+      if (im == null)
+       return;
+      if (! im.Open ())
+       return;
+      plist = plist.next;
+      if (! plist.IsSymbol)
+       return;
+      MSymbol target_type = plist.Symbol;
+      plist = plist.next;
+      MSymbol target_name = MSymbol.nil;
+      if (plist.IsSymbol)
+       target_name = plist.Symbol;
+      if (target_type == Mmacro)
+       {
+         if (target_name == MSymbol.nil)
+           im.domain.CopyFunc (domain);
+         else
+           im.domain.CopyFunc (domain, (Xex.Symbol) target_name.Name);
+       }
+      else if (target_type == Mmap)
+       {
+         if (target_name == MSymbol.nil)
+           {
+             foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
+               maps[kv.Key] = kv.Value;
+           }
+         else
+           {
+             Map map;
+             if (im.maps.TryGetValue (target_name, out map))
+               maps[target_name] = map;
+           }
+       }
+      else if (target_type == Mstate)
+       {
+         if (target_name == MSymbol.nil)
+           {
+             for (p = im.states; ! p.IsEmpty; p = p.next)
+               states.Add (p.key, p.val);
+           }
+         else
+           {
+             object state = im.states.Get (target_name);
+             if (state != null)
+               states.Add (target_name, state);
+           }
+       }
+    }
+
+    private Xex.Term parse_cond (MPlist plist)
+    {
+      Xex.Term[] args = new Xex.Term[plist.Count];
+
+      for (int i = 0; ! plist.IsEmpty; i++, plist = plist.next)
+       {
+         if (! plist.IsPlist)
+           throw new Exception ("Invalid cond args: " + plist);
+         MPlist p = plist.Plist;
+         List<Xex.Term> arg = new List<Xex.Term> (parse_action (p));
+         args[i] = new Xex.Term (arg);
+       }
+      return new Xex.Term (domain, (Xex.Symbol) Mcond.Name, args);
+    }
+
+    private Xex.Term[] parse_action (MPlist plist)
+    {
+      Xex.Term[] terms = new Xex.Term[plist.Count];
+
+      for (int i = 0; ! plist.IsEmpty; i++, plist = plist.next)
+       {
+         if (plist.IsSymbol)
+           terms[i] = new Xex.Term ((Xex.Symbol) plist.Symbol.Name);
+         else if (plist.IsMText)
+           terms[i] = new Xex.Term ((string) plist.Text);
+         else if (plist.IsInteger)
+           terms[i] = new Xex.Term (plist.Integer);
+         else if (plist.IsPlist)
+           {
+             MPlist p = plist.Plist;
+             if (! p.IsSymbol)
+               throw new Exception ("Invalid action: " + p);
+             MSymbol name = p.Symbol;
+             p = p.next;
+             if (name == Mcond)
+               terms[i] = parse_cond (p);
+             else if (name == Mset || name == Madd || name == Msub
+                      || name == Mmul || name == Mdiv)
+               {
+                 if (! p.IsSymbol)
+                   throw new Exception ("Invalid action: " + p);
+                 Xex.Symbol varname = p.Symbol.Name;
+                 terms[i] = new Xex.Term (domain, (Xex.Symbol) name.Name,
+                                          varname, parse_action (p.next));
+               }
+             else
+               terms[i] = new Xex.Term (domain, (Xex.Symbol) name.Name,
+                                        parse_action (p));
+           }
+         else
+           throw new Exception ("Invalid action: " + plist);
+       }
+      return terms;
+    }
+
+
+    private void parse_macros (MPlist plist)
+    {
+      for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
+       if (pl.IsPlist)
+         {
+           MPlist p = pl.Plist;
+
+           if (! p.IsSymbol)
+             continue;
+           domain.Defun ((Xex.Symbol) p.Symbol.Name, false, null, null, true);
+         }
+      for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
+       if (pl.IsPlist)
+         {
+           MPlist p = pl.Plist;
+
+           if (! p.IsSymbol)
+             continue;
+           transform (p.next);
+           domain.Defun ((Xex.Symbol) p.Symbol.Name, false, null,
+                         parse_action (p.next), false);
+         }
+    }
+
+    private void parse_maps (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+         
+           if (! pl.IsSymbol)
+             continue;
+           Map map = new Map ();
+           map.name = pl.Symbol;
+           maps[map.name] = map;
+           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+             {
+               if (! pl.IsPlist)
+                 continue;
+               MPlist p = pl.Plist;
+               KeySeq keys;
+               if (p.IsMText)
+                 keys = new KeySeq (p.Text);
+               else if (p.IsPlist)
+                 keys = new KeySeq (p.Plist);
+               else
+                 continue;
+               p = p.next;
+               if (p.IsEmpty)
+                 continue;
+               map.Add (keys, 0,
+                        new Xex.Term (domain, Nprogn, parse_action (p)));
+             }
+         }
+    }
+
+    private void parse_states (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           MPlist pl = plist.Plist;
+           MText title = null;
+         
+           if (pl.IsMText)
+             {
+               title = pl.Text;
+               pl = pl.next;
+             }
+           if (! pl.IsSymbol)
+             continue;
+
+           State state = new State (pl.Symbol);
+           state.title = title;
+           if (states == null)
+             states = new MPlist ();
+           states.Add (state.name, state);
+           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
+             {
+               if (! pl.IsPlist)
+                 continue;
+               MPlist p = pl.Plist;
+               if (! p.IsSymbol)
+                 continue;
+               MSymbol map_name = p.Symbol;
+               p = p.next;
+               Xex.Term term = new Xex.Term (domain, Nprogn, parse_action (p));
+               state.branches.Add (map_name, term);
+             }
+         }
+    }
+
+    private static Xex.Term Finsert (Xex.Domain domain, Xex.Variable vari,
+                                    Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).insert (args[0]);
+      return args[0];
+    }
+
+    private static Xex.Term Finsert_candidates (Xex.Domain domain,
+                                               Xex.Variable vari,
+                                               Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).insert_candidates (args[0]);
+      return args[0];
+    }
+
+    private static Xex.Term Fmarker (Xex.Domain domain, Xex.Variable vari,
+                                    Xex.Term[] args)
+    {
+      MSymbol name = (string) args[0].Symval;
+      return new Xex.Term (Marker.Get ((MInputContext) domain.context, name));
+    }
+
+    private static Xex.Term Fchar_at (Xex.Domain domain, Xex.Variable vari,
+                                     Xex.Term[] args)
+    {
+      int c = ((MInputContext) domain.context).char_at (args[0].Intval);
+      return new Xex.Term (c);
+    }
+
+    private static Xex.Term Fdelete (Xex.Domain domain, Xex.Variable vari,
+                                  Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).delete ((int) args[0].Intval);
+      return args[0];
+    }
+
+    private static Xex.Term Fselect (Xex.Domain domain, Xex.Variable vari,
+                                    Xex.Term[] args)
+    {
+      Candidates can = ((MInputContext) domain.context).candidates;
+
+      if (can != null)
+       ((Selector) args[0].Objval).Select (can);
+      return args[0];
+    }
+
+    private static Xex.Term Fshow (Xex.Domain domain, Xex.Variable vari,
+                                Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).show ();
+      return Tnil;
+    }
+
+    private static Xex.Term Fhide (Xex.Domain domain, Xex.Variable vari,
+                                Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).hide ();
+      return Tnil;
+    }
+
+    private static Xex.Term Fmove (Xex.Domain domain, Xex.Variable vari,
+                                Xex.Term[] args)
+    {
+      if (args[0].IsInt)
+       ((MInputContext) domain.context).move (args[0].Intval);
+      else
+       {
+         Marker m = (Marker) args[0].Objval;
+         MInputContext ic = (MInputContext) domain.context;
+         ((MInputContext) domain.context).move (m.Position (ic));
+       }
+      return args[0];
+    }
+
+    private static Xex.Term Fmark (Xex.Domain domain, Xex.Variable vari,
+                                  Xex.Term[] args)
+    {
+      Marker m = (Marker) args[0].Objval;
+      m.Mark ((MInputContext) domain.context);
+      return args[0];
+    }
+
+    private static Xex.Term Fpushback (Xex.Domain domain, Xex.Variable vari,
+                                      Xex.Term[] args)
+    {
+      MInputContext ic = (MInputContext) domain.context;
+
+      if (args[0].IsInt)
+       ic.pushback (args[0].Intval);
+      else if (args[0].IsStr)
+       ic.pushback (new KeySeq (args[0].Strval));
+      else
+       ic.pushback ((KeySeq) args[0].Objval);
+      return args[0];
+    }
+
+    private static Xex.Term Fpop (Xex.Domain domain, Xex.Variable vari,
+                                 Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).pop ();
+      return Tnil;
+    }
+
+    private static Xex.Term Fundo (Xex.Domain domain, Xex.Variable vari,
+                                  Xex.Term[] args)
+    {
+      int n = args.Length == 0 ? -2 : args[0].Intval;
+      ((MInputContext) domain.context).undo (n);
+      return Tnil;
+    }
+
+    private static Xex.Term Fcommit (Xex.Domain domain, Xex.Variable vari,
+                                    Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).commit ();
+      return Tnil;
+    }
+
+    private static Xex.Term Funhandle (Xex.Domain domain, Xex.Variable vari,
+                                      Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).commit ();
+      args = new Xex.Term[2];
+      args[0] = args[1] = Tcatch_tag;
+      return Xex.Fthrow (domain, vari, args);
+    }
+
+    private static Xex.Term Fshift (Xex.Domain domain, Xex.Variable vari,
+                                   Xex.Term[] args)
+    {
+      ((MInputContext) domain.context).shift (args[0].Symval);
+      return args[0];
+    }
+
+    public override string ToString ()
+    {
+      string str = (String.Format ("({0} (title \"{1}\")", tag, title));
+      if (commands != null)
+       {
+         str += " (commands";
+         foreach (Command cmd in commands)
+           str += " " + cmd;
+         str += ")";
+       }
+      if (var_names != null)
+       {
+         str += " (variables";
+         foreach (Xex.Symbol var in var_names)
+           str += " " + var;
+         str += ")";
+       }
+      if (plugins != null)
+       {
+         str += " (modules";
+         foreach (KeyValuePair<MSymbol, Plugin> kv in plugins)
+           str += " " + kv.Value;
+         str += ")";
+       }
+      str += " (maps";
+      foreach (KeyValuePair<MSymbol, Map> kv in maps)
+       str += " " + kv.Value;
+      str += ") (states";
+      foreach (MPlist p in states)
+       str += " " + p.val;
+      return str + "))";
+    }
+  }
+
+  public class MInputContext
+  {
+    internal static Xex.Symbol Ncandidates_group_size = "candidates-group-size";
+    private static MSymbol Mat_less_than = "@<";
+    private static MSymbol Mat_greater_than = "@>";
+    private static MSymbol Mat_minus = "@-";
+    private static MSymbol Mat_plus = "@+";
+    private static MSymbol Mat_open_square_bracket = "@[";
+    private static MSymbol Mat_close_square_bracket = "@]";
+
+    public MInputMethod im;
+    private MText produced;
+    private bool active;
+    private MText status;
+    internal MText preedit;
+    internal int cursor_pos;
+    internal Mim.Candidates candidates;
+    private MPlist candidate_group;
+    private int candidate_index;
+    private int candidate_from, candidate_to;
+    private bool candidate_show;
+
+    private Stack<Mim.State> states;
+    internal Mim.KeySeq keys;
+    private int key_head;
+    private int state_key_head;
+    private object state_var_values;
+    private int commit_key_head;
+    private MText state_preedit;
+    private int state_pos;
+    internal MPlist markers = new MPlist ();
+    internal MText preceding_text = new MText ();
+    internal MText following_text = new MText ();
+    private bool key_unhandled;
+
+    internal Xex.Domain domain;
+
+    internal Mim.ChangedStatus changed;
+
+    public Mim.ChangedStatus Changed { get { return changed; } }
+
+    public MInputContext (MInputMethod im)
+    {
+      this.im = im;
+      domain = new Xex.Domain (im.domain, this);
+      states = new Stack<Mim.State> ();
+      states.Push ((Mim.State) im.states.val);
+      keys = new Mim.KeySeq ();
+    }
+
+    private void adjust_markers (int from, int to, object inserted)
+    {
+      int ins = (inserted == null ? 0
+                : inserted is int ? 1
+                : ((MText) inserted).Length);
+      int diff = ins - (to - from);
+
+      for (MPlist plist = markers; ! plist.IsEmpty; plist = plist.next)
+       {
+         int pos = plist.Integer;
+         if (pos > from)
+           {
+             if (pos >= to)
+               plist.val = pos + diff;
+             else
+               plist.val = from;
+           }
+       }
+      if (cursor_pos >= to)
+       cursor_pos += diff;
+      else if (cursor_pos > from)
+       cursor_pos = from;
+    }
+
+    private void preedit_replace (int from, int to, int c)
+    {
+      preedit.Del (from, to);
+      preedit.Ins (from, c);
+      adjust_markers (from, to, c);
+    }
+
+    private void preedit_replace (int from, int to, MText mt)
+    {
+      preedit[from, to] = mt;
+      adjust_markers (from, to, mt);
+    }
+
+    internal void insert (Xex.Term arg)
+    {
+      if (arg.IsInt)
+       preedit_replace (cursor_pos, cursor_pos, arg.Intval);
+      else
+       preedit_replace (cursor_pos, cursor_pos, new MText (arg.Strval));
+      changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
+    }
+
+    private void update_candidate ()
+    {
+      object candidate = candidates.Current;
+
+      if (candidate is MText)
+       {
+         preedit_replace (candidate_from, candidate_to, (MText) candidate);
+         candidate_to = candidate_from + ((MText) candidate).Length;
+       }
+      else
+       {
+         preedit_replace (candidate_from, candidate_to, (int) candidate);
+         candidate_to = candidate_from + 1;
+       }
+      preedit.PushProp (candidate_from, candidate_to,
+                       Mim.Mcandidates, this);
+      cursor_pos = candidate_from;
+      changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                 | CandidateAll);
+    }
+
+    internal void insert_candidates (Xex.Term arg)
+    {
+      int column = 0;
+      Xex.Variable v = domain.GetVar (Ncandidates_group_size, false);
+
+      if (v != null)
+       column = v.Value.Intval;
+      candidates = new Mim.Candidates (arg.Listval, column);
+      candidate_from = candidate_to = cursor_pos;
+      update_candidate ();
+    }
+
+    internal void select (int n)
+    {
+      if (candidates != null)
+       {
+         candidates.Select (n);
+         update_candidate ();
+       }
+    }
+
+    internal int marker (MSymbol sym)
+    {
+      int pos = cursor_pos;
+
+      if (sym.Name.Length == 2 && sym.Name[0] == '@')
+       {
+         switch (sym.Name[0])
+           {
+           case '<': pos = 0; break;
+           case '>': pos = preedit.Length; break;
+           case '-': pos = cursor_pos - 1; break;
+           case '+': pos = cursor_pos + 1; break;
+           case '[':
+             if (pos > 0)
+               {
+                 int to;
+                 preedit.FindProp (Mim.Mcandidates, pos - 1,
+                                   out pos, out to);
+               }
+             else
+               pos = 0;
+             break;
+           case ']':
+             if (cursor_pos < preedit.Length - 1)
+               {
+                 int from;
+                 preedit.FindProp (Mim.Mcandidates, pos,
+                                   out from, out pos);
+               }
+             else
+               pos = preedit.Length;
+             break;
+           default:
+             if (sym.Name[0] >= '0' && sym.Name[0] <= '9')
+               pos = sym.Name[0];
+             break;
+           }
+       }
+      else if (sym.Name.Length >= 3 && sym.Name[0] == '@')
+       {
+         pos = int.Parse (sym.Name.Substring (2));
+       }
+      else
+       {
+         object val = markers.Get (sym);
+
+         if (val is int)
+           pos = (int) val;
+       }
+      return pos;
+    }
+
+    internal int char_at (int pos)
+    {
+      int c;
+
+      pos += cursor_pos;
+      if (pos < 0)
+       {
+         if (preceding_text.Length < -pos)
+           {
+             MPlist plist = new MPlist ();
+             plist.Push (MSymbol.integer, pos);
+             if (Mim.GetSurroundingText != null
+                 && Mim.GetSurroundingText (this, plist)
+                 && plist.IsMText
+                 && preceding_text.Length < plist.Text.Length)
+               preceding_text = plist.Text;
+           }
+         c = (-pos < preceding_text.Length
+              ? preceding_text[preceding_text.Length + pos] : -1);
+       }
+      else if (pos >= 0 && pos < preedit.Length)
+       c = preedit[pos];
+      else
+       {
+         pos -= preedit.Length;
+         if (pos >= following_text.Length)
+           {
+             MPlist plist = new MPlist ();
+             plist.Push (MSymbol.integer, pos + 1);
+             if (Mim.GetSurroundingText != null
+                 && Mim.GetSurroundingText (this, plist)
+                 && plist.IsMText
+                 && following_text.Length < plist.Text.Length)
+               following_text = plist.Text;
+           }
+         c = (pos < following_text.Length ? following_text[pos] : -1);
+       }
+      return c;
+    }
+
+    internal void delete (int pos)
+    {
+      if (pos < cursor_pos)
+       preedit_replace (pos, cursor_pos, null);
+      else
+       preedit_replace (cursor_pos, pos, null);
+      changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
+    }
+
+    internal void show ()
+    {
+      candidate_show = true;
+      changed |= ChangedStatus.CandidateShow;
+    }
+
+    internal void hide ()
+    {
+      candidate_show = false;
+      changed |= ChangedStatus.CandidateShow;
+    }
+
+    internal void move (int pos)
+    {
+      if (pos < 0)
+       pos = 0;
+      else if (pos > preedit.Length)
+       pos = preedit.Length;
+      if (pos != cursor_pos)
+       {
+         cursor_pos = pos;
+         changed |= ChangedStatus.Preedit;
+       }
+    }
+
+    internal void mark (MSymbol sym)
+    {
+      MPlist slot = markers.Find (sym);
+
+      if (slot == null)
+       markers.Push (sym, cursor_pos);
+      else
+       slot.val = cursor_pos;
+    }
+
+    internal void pushback (int n)
+    {
+      if (n > 0)
+       {
+         key_head -= n;
+         if (key_head < 0)
+           key_head = 0;
+       }
+      else if (n == 0)
+       key_head = 0;
+      else
+       {
+         key_head = - n;
+         if (key_head > keys.keyseq.Count)
+           key_head = keys.keyseq.Count;
+       }
+    }
+
+    internal void pushback (Mim.KeySeq keyseq)
+    {
+      if (key_head > 0)
+       key_head--;
+      if (key_head < keys.keyseq.Count)
+       keys.keyseq.RemoveRange (key_head, keys.keyseq.Count - key_head);
+      for (int i = 0; i < keyseq.keyseq.Count; i++)
+       keys.keyseq.Add (keyseq.keyseq[i]);
+    }
+
+    internal void pop ()
+    {
+      if (key_head < keys.keyseq.Count)
+       keys.keyseq.RemoveRange (key_head, 1);
+    }
+
+    internal void undo (int n)
+    {
+      if (n < 0)
+       keys.keyseq.RemoveRange (keys.keyseq.Count + n, - n);
+      else
+       keys.keyseq.RemoveRange (n, keys.keyseq.Count  - n);
+      reset ();
+    }
+
+    internal void commit ()
+    {
+      produced.Cat (preedit);
+      preedit.Del ();
+      changed |= ChangedStatus.Preedit;
+    }
+
+    internal void shift (MSymbol sym)
+    {
+      Mim.State state;
+
+      if (sym == MSymbol.t)
+       {
+         if (states.Count > 1)
+           state = states.Pop ();
+         else
+           state = states.Peek ();
+       }
+      else
+       {
+         state = (Mim.State) im.states.Get (sym);
+         if (state == null)
+           throw new Exception ("Unknown state: " + state.name);
+       }
+      if (state == null)
+       state = states.Pop ();
+      if (state == (Mim.State) im.states.val)
+       {
+         commit ();
+         reset ();
+       }
+      else
+       {
+         state_key_head = key_head;
+         state_pos = cursor_pos;
+         state_preedit = preedit.Dup ();
+         if (state != states.Peek ())
+           {
+             states.Push (state);
+             state_var_values = domain.SaveValues ();
+             status = state.title;
+             if (status == null)
+               status = im.title;
+             changed |= ChangedStatus.StateTitle;
+             Xex on_entry
+               = (Xex) state.branches.Get (MSymbol.t);
+             if (on_entry != null)
+               on_entry.Eval (domain);
+           }
+       }
+    }
+
+    internal void reset ()
+    {
+      preedit.Del ();
+      state_preedit.Del ();
+      produced.Del ();
+      markers.Clear ();
+      cursor_pos = 0;
+      key_head = commit_key_head = 0;
+      states.Clear ();
+      states.Push ((Mim.State) im.states.Val);
+      state_key_head = 0;
+      state_pos = 0;
+    }
+
+    internal object GetCandidates (out int column)
+    {
+      column = 0;
+      if (cursor_pos == 0)
+       return null;
+      Mim.Candidates candidates
+       = (Mim.Candidates) preedit.GetProp (cursor_pos - 1, Mim.Mcandidates);
+      if (candidates == null)
+       return null;
+      column = candidates.Column;
+      return candidates.Current;
+    }
+
+    internal void HandleKey ()
+    {
+    }
+
+    public bool Toggle ()
+    {
+      active = ! active;
+      return active;
     }
   }
 }