*** empty log message ***
[m17n/m17n-lib-cs.git] / MInputMethod.cs
index fcc0973..60e48d0 100644 (file)
@@ -9,84 +9,78 @@ using M17N;
 using M17N.Core;
 using M17N.Input;
 
+using Xex = System.Xml.Expression.Xexpression;
+
 namespace M17N.Input
 {
-  using Xex = System.Xml.Expression.Xexpression;
-
   public class MInputMethod
   {
     // Delegaes
-    public delegate bool Callback (MInputContext ic, MPlist args);
+    public delegate bool Callback (Context 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 event Callback PreeditChanged;
+    public static event Callback StatusChanged;
+    public static event Callback CandidateChanged;
     public static Callback GetSurroundingText;
     public static Callback DeleteSurroundingText;
 
-    internal static Xex.Domain im_domain = new Xex.Domain (null);
+    internal static Xex.Domain im_domain
+      = new Xex.Domain ("input-method", 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 Mmodule_list = "module-list";
     private static MSymbol Mtitle = "title";
     private static MSymbol Minclude = "include";
     private static MSymbol Mmacro = "macro";
-    private static MSymbol Mmacro_list = "macro-list";
     private static MSymbol Mmap = "map";
     private static MSymbol Mmap_list = "map-list";
     private static MSymbol Mstate = "state";
-    private static MSymbol Mstate_list = "state-list";
     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 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 MSymbol Mat_minus_zero = "@-0";
+
+    private static Xex.Symbol Qmap = "map";
+    private static Xex.Symbol Qrule = "rule";
+    private static Xex.Symbol Qkeyseq = "keyseq";
+    private static Xex.Symbol Qprogn = "progn";
+    private static Xex.Symbol Qcatch = "catch";
+    private static Xex.Symbol Qinsert = "insert";
+    private static Xex.Symbol Qinsert_candidates = "insert-candidates";
+    private static Xex.Symbol Qchar_at = "char-at";
+    private static Xex.Symbol Qselect = "select";
+    private static Xex.Symbol Qdelete = "delete";
+    private static Xex.Symbol Qshift = "shift";
+    private static Xex.Symbol Qmove = "move";
+    private static Xex.Symbol Qmark = "mark";
+    private static Xex.Symbol Qset = "set";
+    private static Xex.Symbol Qadd = "add";
+    private static Xex.Symbol Qsub = "sub";
+    private static Xex.Symbol Qmul = "mul";
+    private static Xex.Symbol Qdiv = "div";
+    private static Xex.Symbol Qcond = "cond";
+    private static Xex.Symbol Qsname = "sname";
+    private static Xex.Symbol Qmname = "mname";
+    private static Xex.Symbol Qstate_hook = "state-hook";
+    private static Xex.Symbol Qcatch_all_branch = "catch-all-branch";
+    private static Xex.Symbol Qbranch = "branch";
+    private static Xex.Symbol Qstate = "state";
+    private static Xex.Symbol Qtitle = "title";
+    private static Xex.Symbol Qeq = "=";
+    private static Xex.Symbol Qeqeq = "==";
+    private static Xex.Symbol Qcandidates_group_size = "candidates-group-size";
+
+    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> ();
 
-    // Sub classes
-    private class Exception : System.Exception
-    {
-      bool error;
-
-      public Exception (string msg) : base (msg)
-       {
-         error = true;
-       }
-
-      public Exception (string fmt, params object[] args)
-       : base (String.Format (fmt, args))
-       {
-         error = true;
-       }
-
-      public Exception (string msg, bool error) : base (msg)
-       {
-         this.error = error;
-       }
-    }
+    internal static MInputMethod im_global = null;
 
     [FlagsAttribute]
-    private enum LoadStatus
+    protected enum LoadStatus
     {
       None =   0x00,
       Header = 0x01,
@@ -96,736 +90,1174 @@ namespace M17N.Input
     };
 
     [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;
+    public enum ChangedStatus
+    {
+      None =           0x00,
+      StateTitle =     0x01,
+      PreeditText =    0x02,
+      CursorPos =      0x04,
+      CandidateList =  0x08,
+      CandidateIndex = 0x10,
+      CandidateShow =  0x20,
+      Preedit =                PreeditText | CursorPos,
+      Candidate =      CandidateList | CandidateIndex | CandidateShow,
+    }
+
+    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);
+       public static Key Reload;
+
+       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;
+        Reload = new Key (keysym_base);
+        keysyms["-reload"] = keysym_base++;
+       }
+
+       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 static bool operator== (Key k1, Key k2)
+        {
+          return k1.key == k2.key;
+        }
+
+       public static bool operator!= (Key k1, Key k2)
+        {
+          return k1.key != k2.key;
+        }
+
+       public override bool Equals (object o) { return key == ((Key) o).key; }
+
+       public override int GetHashCode () { return (int) key; }
+
+       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 int ToChar ()
+       {
+        return (int) (key & 0x1FFFFF);
+       }
+
+       public override string ToString ()
+       {
+        int c = ToChar ();
+        MText mt = null;
+        if (c < 0x20)
+          foreach (KeyValuePair<string, uint> kv in keysyms)
+            if ((uint) c == kv.Value)
+              {
+                mt = kv.Key;
+                break;
+              }
+        if (mt == null)
+          mt = new MText (c);
+
+        KeyModifier m = ((KeyModifier) key) & KeyModifier.All;
+
+        if (m != KeyModifier.None)
+          {
+            if ((m & KeyModifier.Shift) != KeyModifier.None)
+              mt.Ins (0, "S-");
+            if ((m & KeyModifier.Control) != KeyModifier.None)
+              mt.Ins (0, "C-");
+            if ((m & KeyModifier.Alt) != KeyModifier.None)
+              mt.Ins (0, "A-");
+            if ((m & KeyModifier.AltGr) != KeyModifier.None)
+              mt.Ins (0, "G-");
+            if ((m & KeyModifier.Super) != KeyModifier.None)
+              mt.Ins (0, "s-");
+            if ((m & KeyModifier.Hyper) != KeyModifier.None)
+              mt.Ins (0, "H-");
+          }
+        return (string) mt;
+       }
+     }
+
+     internal 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]));
+       }
+
+       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 ()
+       {
+        MText mt;
+        foreach (Key key in keyseq)
+          if (key.HasModifier || key.ToChar () < 0x20)
+            {
+              mt = "(";
+              foreach (Key k in keyseq)
+                {
+                  if (mt.Length > 1)
+                    mt.Cat (' ');
+                  mt.Cat (k.ToString ());
+                }
+              return (string) mt.Cat (")");
+            }
+        mt = "\"";
+        foreach (Key k in keyseq)              
+          {
+            int c = k.ToChar ();
+
+            if (c == '\\' || c == '"')
+              mt.Cat ('\\');
+            mt.Cat (c);
+          }        
+        return (string) mt.Cat ("\"");
+       }
+     }
+
+     public class Command
+     {
+       public MSymbol name;
+       public MText description;
+       internal 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> ();
+        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 + ")";
+       }
+     }
+
+     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;
+
+       private Marker (MSymbol name)
+        {
+          this.name = name;
+        }
+
+       public abstract int Position (Context ic);
+
+       public virtual void Mark (Context ic)
+        {
+          throw new Exception ("Can't set predefined marker: " + name);
+        }
+       public virtual int CharAt (Context ic)
+        {
+          return ic.preedit[Position (ic)];
+        }
+
+       public override string ToString ()
+       {
+        return "<marker>" + name.Name + "</marker>";
+       }
+
+       public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+       {
+        return Get ((MSymbol) node.InnerText);
+       }
+
+       public class Named : Marker
+       {
+        public Named (MSymbol name) : base (name) { }
+
+        public override int Position (Context ic)
+        {
+          MPlist p =  ic.marker_positions.Find (name);
+          return (p == null ? 0 : p.Integer);
+        }
+
+        public override void Mark (Context ic)
+        {
+          ic.marker_positions.Put (name, ic.cursor_pos);
+        }
+       }
+
+       public class Predefined : Marker
+       {
+        char tag;
+
+        public Predefined (MSymbol name) : base (name)
+          {
+            tag = ((string) name)[1];
+          }
+
+        public override int Position (Context ic)
+        {
+          switch (tag) {
+          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 tag - '0';
+          }
+        }
+       }
+
+       public class PredefinedAbsolute : Marker
+       {
+        private int pos;
+
+        public PredefinedAbsolute (MSymbol name) : base (name)
+          {
+            if (! int.TryParse (((string) name).Substring (1), out pos))
+              throw new Exception ("Invalid marker name: " + name);
+          }
+
+        public override int Position (Context ic)
+        {
+          return (pos < ic.preedit.Length ? pos : ic.preedit.Length);
+        }
+       }
+
+       public class PredefinedSurround : Marker
+       {
+        private int distance;
+
+        public PredefinedSurround (MSymbol name) : base (name)
+          {
+            if (! int.TryParse (((string) name).Substring (2), out distance))
+              throw new Exception ("Invalid marker name: " + name);
+            if (distance > 0)
+              distance--;
+          }
+
+        public override int Position (Context ic)
+        {
+          return ic.cursor_pos + distance;
+        }
+
+        public override int CharAt (Context ic)
+          {
+            int pos = ic.cursor_pos + distance;
+            if (pos < 0)
+              return ic.GetSurroundingChar (pos);
+            else if (pos >= ic.preedit.Length)
+              return ic.GetSurroundingChar (pos - ic.preedit.Length);
+            return ic.preedit[pos];
+          }
+       }
+
+       static internal Dictionary<MSymbol,Predefined> predefined_markers;
+
+       static Marker ()
+       {
+        predefined_markers = new Dictionary<MSymbol, Predefined> ();
+        MSymbol[] symlist = new MSymbol[] {"@<", "@>", "@-", "@+", "@[", "@]" };
+        foreach (MSymbol s in symlist)
+          predefined_markers[s] = new Predefined (s);
+       }
+
+       public static Marker Get (MSymbol name)
+       {
+        string str = name.Name;
+        if (str[0] == '@')
+          {
+            Predefined pred;
+            if (predefined_markers.TryGetValue (name, out pred))
+              return pred;
+            if (str.Length == 1)
+              throw new Exception ("Invalid marker name: " + name);
+            if (Char.IsDigit (str[1]))
+              return new PredefinedAbsolute (name);
+            if (str.Length == 2 || name == Mat_minus_zero
+                || ! (str[1] == '-' || str[1] == '+'))
+              throw new Exception ("Invalid marker name: " + name);
+            return new PredefinedSurround (name);
+          }
+        return new Named (name);
+       }
+     }
+
+     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 (Xex.Term[] candidates, int column)
+       {
+        int nblocks = candidates.Length;
+
+        blocks = new Block[nblocks];
+        for (int i = 0, start = 0; i < nblocks; i++)
+          start += (blocks[i] = new Block (index, candidates[i])).Count;
+        if (column > 0)
+          group = new object[column];
+       }
+
+       public static void Detach (Context ic)
+       {
+        ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+        ic.candidates = null;
+        ic.changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                       | CandidateAll);
       }
 
-      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;
-      }
+      // Fill the array "group" by candidates stating from START.
+      // START must be a multiple of "column".  Return the number of
+      // valid candidates in "group".
 
-      private static uint combine_modifiers (uint c, KeyModifier modifiers)
+      private int fill_group (int start)
       {
-       if (c < 0x7F && c != 0x20)
+       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 ((modifiers & KeyModifier.Shift) != KeyModifier.None
-               && Char.IsLower ((char) c))
-             {
-               modifiers &= ~KeyModifier.Shift;
-               c = Char.ToUpper ((char) c);
-             }
-           if ((modifiers & KeyModifier.Control) != KeyModifier.None)
+           if (start >= count)
              {
-               modifiers &= ~KeyModifier.Control;
-               c &= 0x1F;
+               r++;
+               if (r == blocks.Length)
+                 return i;
+               b = blocks[r];
+               count = b.Count;
+               start = 0;
              }
-         }     
-       return c | (uint) modifiers;
+           group[i] = b[start];
+         }
+       return nitems;
       }
 
-      public Key (uint c)
-      {
-       key = c;
-      }
+      // 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 Key (uint c, KeyModifier modifiers)
+      public int PrevGroup ()
       {
-       key = combine_modifiers (c, modifiers);
+       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 Key (MSymbol keysym, KeyModifier modifiers)
+      public int NextGroup ()
       {
-       key = combine_modifiers (decode_keysym (keysym), modifiers);
+       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 Key (MSymbol keysym)
+      public void Prev ()
       {
-       string str = keysym.Name;
-       int len = str.Length;
-       int i;
-       KeyModifier modifiers = KeyModifier.None;
+       int col = Column;
 
-       for (i = 0; i + 2 < len && str[i + 1] == '-'; i += 2)
+       if (col == 0)
          {
-           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;
+           int nitems = PrevGroup ();
+           index += col < nitems - 1 ? col : nitems - 1;
          }
-       if (i + 1 == len)
-         key = combine_modifiers (str[i], modifiers);
        else
-         key = combine_modifiers (decode_keysym (keysym), modifiers);
+         index--;
       }
 
-      public Key (MPlist plist)
+      public void Next ()
       {
-       KeyModifier modifiers = KeyModifier.None;
-       MPlist p;
+       int col = Column;
+       int nitems = GroupLength;
 
-       for (p = plist; ! p.IsEmpty; p = p.next)
+       if (col == nitems - 1)
          {
-           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;
-             }
+           nitems = NextGroup ();
+           index -= Column;
          }
-       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);
+         index++;
       }
 
-      public bool HasModifier
+      public void First ()
       {
-       get { return ((key & (uint) KeyModifier.All) != 0); }
+       index -= Column;
       }
 
-      public bool Match (Key k)
+      public void Last ()
       {
-       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)));
+       index += GroupLength - (Column + 1);
       }
 
-      public override string ToString ()
+      public object Select (int col)
       {
-       string str = Char.ToString ((char) key);
-       KeyModifier m = ((KeyModifier) key) & KeyModifier.All;
+       int maxcol = GroupLength - 1;
+       if (col > maxcol)
+         col = maxcol;
+       index = index - Column + col;
+       return Current;
+      }
 
-       if (m != KeyModifier.None)
+      public object Select (Selector selector)
+      {
+       switch (selector.Tag)
          {
-           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;
+         case '<': First (); break;
+         case '>': Last (); break;
+         case '-': Prev (); break;
+         case '+': Next (); break;
+         case '[': PrevGroup (); break;
+         case ']': NextGroup (); break;
+         default: break;
          }
-       return str;
+       return Current;
+      }
+
+      public override string ToString ()
+      {
+       return (String.Format ("<candidates row={0} col={1}>", row, index)
+               + Group
+               + "</candidates>");
       }
     }
 
-    public class KeySeq : Xex.Object
+    internal class Selector : Xex.TermValue
     {
-      List<Key> keyseq = new List<Key> ();
+      static new Dictionary<MSymbol, Selector> selectors;
+
+      static Selector ()
+       {
+         selectors = new Dictionary<MSymbol, Selector> ();
+         selectors ["@<"] = selectors["@first"] = new Selector ('<');
+         selectors ["@="] = selectors["@current"] = new Selector ('=');
+         selectors ["@>"] = selectors["@last"] = new Selector ('>');
+         selectors ["@-"] = selectors["@previous"] = new Selector ('-');
+         selectors ["@+"] = selectors["@next"] = new Selector ('+');
+         selectors ["@["] = selectors["@previous-candidate-change"]
+           = new Selector ('[');
+         selectors ["@]"] = selectors["@next-candidate-change"]
+           = new Selector (']');
+       }
 
-      public KeySeq () { }
+      private readonly char tag;
 
-      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 char Tag { get { return tag; } }
 
-      public KeySeq (MText mt) : base ()
+      private Selector (char tag) { this.tag = tag; }
+
+      public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
       {
-       for (int i = 0; i < mt.Length; i++)
-         keyseq.Add (new Key ((uint) mt[i]));
+       return Get ((MSymbol) node.InnerText);
       }
 
-      private static uint parse_integer (string str)
+      public static Xex.TermValue Get (MSymbol name)
       {
-       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;
+       Selector selector;
+       if (! selectors.TryGetValue (name, out selector))
+         throw new Exception ("Invalid selector name: " + name);
+       return selector;
       }
+    }
 
-      public KeySeq (XmlNode node)
-       {
-         XmlAttributeCollection acol = node.Attributes;
-         XmlNode n;
+    internal class Map
+    {
+      public MSymbol name;
+      public List<Entry> entries = new List<Entry> ();
 
-         if (acol != null && (n = acol["keys"]) != null)
-           {
-             foreach (char c in  n.Value)
-               keyseq.Add (new Key ((uint) c));
-           }
+      public Map (MSymbol name) { this.name = name; }
 
-         for (node = node.FirstChild; node != null; node = node.NextSibling)
-           {
-             if (node.Name == "key-event")
-               keyseq.Add (new Key ((MSymbol) node.InnerText));
-             else
-               keyseq.Add (new Key (parse_integer (node.InnerText)));
-           }
-       }
+      public class Entry
+      {
+       public KeySeq keyseq;
+       public Xex.Term[] actions;
 
-      public KeySeq (Xex.Term[] args)
+       public Entry (Xex.Domain domain, KeySeq keyseq, Xex.Term[] actions)
        {
-         foreach (Xex.Term term in args)
-           {
-             if (term.IsName)
-               keyseq.Add (new Key ((MSymbol) term.Nameval.name));
-             else
-               keyseq.Add (new Key (term.Strval));
-           }
+         this.keyseq = keyseq;
+         this.actions = actions;
        }
+      }
 
       public override string ToString ()
       {
-       string str;
-
-       foreach (Key key in this)
-         if (key.HasModifier)
-           {
-             str = "(keyseq";
-             foreach (Key k in this)
-               str += " " + k.ToString ();
-             return str + ")";
-           }
-       str = "\"";
-       foreach (Key key in this)               
-         str += key.ToString ();
-       return str + "\"";
+       string str = "(" + name;
+       foreach (Entry e in entries)
+         str += " " + e.keyseq.ToString ();
+       return str + ")";
       }
     }
 
-    public class Variable
+    internal class Keymap
     {
-      public MSymbol name;
-      public MText description;
-      public Type type;
-      public object value;
-      public object[] candidates;
+      public Dictionary<Key, Keymap> submaps;
+      public Xex.Term[] map_actions, branch_actions;
 
-      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;
-      }
+      public Keymap () { }
 
-      private static Type parse_value (XmlNode node, out object value)
+      public void Add (KeySeq keys, int index, 
+                      Xex.Term[] map_actions, Xex.Term[] branch_actions)
       {
-       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")
+       if (index == keys.keyseq.Count)
          {
-           MText mt = node.InnerText;
-           value = mt;
-           type = typeof (MText);
-         }
-       else if (typename == "symbol")
-         {
-           MSymbol sym = node.InnerText;
-           value = sym;
-           type = typeof (MSymbol);
+           this.map_actions = map_actions;
+           this.branch_actions = branch_actions;
          }
        else
          {
-           value = null;
-           type = typeof (object);
+           Key key = keys.keyseq[index];
+           Keymap sub = null;
+
+           if (submaps == null)
+             submaps = new Dictionary<Key, Keymap> ();
+           else
+             submaps.TryGetValue (key, out sub);
+           if (sub == null)
+             submaps[key] = sub = new Keymap ();
+           sub.Add (keys, index + 1, map_actions, branch_actions);
          }
-       return type;
       }
 
-      public Variable (XmlNode node)
+      public void AddMap (Map map, Xex.Term[] branch_actions)
       {
-       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;
-                   }
-               }
-           }
+       foreach (Map.Entry entry in map.entries)
+         Add (entry.keyseq, 0, entry.actions, branch_actions);
       }
 
-      public override string ToString ()
+      public Keymap Lookup (KeySeq keys, ref int index)
       {
-       return ("(" + name + " \"" + (string) description
-               + "\" " + type + " " + value + " " + candidates + ")");
-      }
-    }
-
-    public class Command
-    {
-      public MSymbol name;
-      public MText description;
-      public List<KeySeq> keys;
+       Keymap sub;
 
-      public Command (MPlist p)
-      {
-       name = p.Symbol;
-       p = p.Next;
-       description = parse_description (p);
-       if (description == null)
-         description = "No description";
-       keys = new List<KeySeq> ();
-       for (p = p.next; ! p.IsEmpty; p = p.next)
+       if (index < keys.keyseq.Count
+           && submaps != null
+           && submaps.TryGetValue (keys.keyseq[index], out sub))
          {
-           if (p.IsMText)
-             keys.Add (new KeySeq (p.Text));
-           else if (p.IsPlist)
-             keys.Add (new KeySeq (p.Plist));
+           index++;
+           return sub.Lookup (keys, ref index);
          }
+       return this;
       }
 
-      public Command (XmlNode node)
+      private void describe (MText mt, KeySeq keyseq)
       {
-       name = node.Attributes["id"].Value;
-       keys = new List<KeySeq> ();
-       for (node = node.FirstChild; node != null; node = node.NextSibling)
+       if (map_actions != null || branch_actions != null)
          {
-           if (node.Name == "description")
-             description = parse_description (node);
-           else if (node.Name == "keyseq")
-             keys.Add (new KeySeq (node));
+           if (mt.Length > 0)
+             mt.Cat (" ");
+           mt.Cat ('(').Cat (keyseq.ToString ());
+           if (map_actions != null)
+             foreach (Xex.Term term in map_actions)
+               mt.Cat (' ').Cat (term.ToString ());
+           if (branch_actions != null)
+             foreach (Xex.Term term in branch_actions)
+               mt.Cat (' ').Cat (term.ToString ());
+           mt.Cat (')');
          }
+       if (submaps != null)
+         foreach (KeyValuePair<Key, Keymap> kv in submaps)
+           {
+             keyseq.keyseq.Add (kv.Key);
+             kv.Value.describe (mt, keyseq);
+             keyseq.keyseq.RemoveAt (keyseq.keyseq.Count - 1);
+           }
       }
 
       public override string ToString ()
       {
-       string str = "(" + name + " \"" + (string) description;
-       foreach (KeySeq keyseq in keys)
-         str += " " + keyseq;
-       return str + ")";
+       MText mt = "";
+       KeySeq keyseq = new KeySeq ();
+
+       describe (mt, keyseq);
+       return (string) mt;
       }
     }
 
-    internal class Plugin
+    internal class State
     {
-      public string name;
-      public Assembly assembly;
-      public MPlist methods;
+      public Xex.Symbol name;
+      public MText title;
+      public Xex.Term[] enter_actions, fallback_actions;
+      public Keymap keymap = new Keymap ();
 
-      public override string ToString ()
+      public State (Xex.Symbol name, MText title)
       {
-       string str = "(" + name;
-       for (MPlist p = methods; ! p.IsEmpty; p = p.next)
-         str += " " + p.key;
-       return str + ")";
+       this.name = name;
+       this.title = title;
       }
 
-      public Xex.Term Call (MSymbol method, MInputContext ic, Xex.Term[] args)
+      public State (MInputMethod im, XmlNode node)
       {
-       if (assembly == null)
+       this.name = node.Attributes[Qsname].Value;
+       XmlAttribute attr = node.Attributes[Qtitle];
+       if (attr != null)
+         title = (MText) attr.Value;
+       else
+         title = im.title;
+       keymap = new Keymap ();
+       for (node = node.FirstChild; node != null; node = node.NextSibling)
          {
-           try {
-             assembly = Assembly.LoadFrom (name + ".dll");
-           } catch {
-             return Xex.Zero;
-           }
-           Type t = assembly.GetType ("Plugin");
-           for (MPlist p = plugin.methods; ! p.IsEmpty; p = p.next)
-             p.Set (p.key, t.GetMethod (p.key.Name));
+           if (node.Name == Qstate_hook)
+             enter_actions = Xex.ParseTerms (im.domain, node.FirstChild);
+           else if (node.Name == Qcatch_all_branch)
+             fallback_actions = Xex.ParseTerms (im.domain, node.FirstChild);
+           else if (node.Name == Qbranch)
+             {
+               MSymbol mapname = node.Attributes[Qmname].Value;
+               Map map;
+               if (im.maps.TryGetValue (mapname, out map))
+                 keymap.AddMap (map, Xex.ParseTerms (im.domain,
+                                                     node.FirstChild));
+               else
+                 throw new Exception ("Unknown map: " + mapname);
+             }
          }
-
-       MethodInfo method_info = (MethodInfo) methods.Get (method);
-       if (method_info == null)
-         return Xex.Zero;
-       Xex.Term result = (Xex.Term) method_info.Invoke (null, args);
-       return result.Eval ();
       }
-    }
 
-    internal class PluginMethod : Function
-    {
-      public Plugin plugin;
-      public MSymbol method;
-
-      public PluginMethod (Plugin plugin, MSymbol name)
-       : base ((Name) name.name, 0, -1)
-       {
-         this.plugin = plugin;
-         method = name.name.Substring (plugin.name.Length + 1);
-       }
-
-      public override Term Call (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+      public State (MInputMethod im, MPlist plist)
       {
-       Xex.Term[] args = (Xex.Term[]) args.Clone ();
-       for (int i = 0; i < args.Length; i++)
+       if (! plist.IsSymbol)
+         throw new Exception ("Invalid state: " + plist);
+       this.name = plist.Symbol.Name;
+       plist = plist.next;
+       if (plist.IsMText)
          {
-           args[i] = args[i].Eval (domain);
-           if (domain.Thrown)
-             return args[i];
+           title = plist.Text;
+           plist = plist.next;
          }
-       return plugin.Call (method, (MIntutContext) domain.context, args);
+       else
+         title = im.title;
+       keymap = new Keymap (); 
+       for (; ! plist.IsEmpty; plist = plist.next)
+         {
+           if (! plist.IsPlist)
+             throw new Exception ("Invalid branch: " + plist);
+           MPlist p = plist.Plist;
+           if (! p.IsSymbol)
+             throw new Exception ("Invalid branch: " + p);
+           MSymbol mapname = p.Symbol;
+           if (mapname == MSymbol.t)
+             enter_actions = im.parse_actions (p.next, false);
+           else if (mapname == MSymbol.nil)
+             fallback_actions = im.parse_actions (p.next, false);
+           else
+             {
+               Map map;
+               if (im.maps.TryGetValue (mapname, out map))
+                 keymap.AddMap (map, im.parse_actions (p.next, false));
+               else
+                 throw new Exception ("Unknown map: " + mapname);
+             }
+           }
       }
-    }
-
-    internal class PluginCall : Xex.TermValue
-    {
-      PluginMethod method;
-      Xex.Term[] args;
 
-      public PluginCall (Plugin plugin, MSymbol entry, Xex.Term[] args)
-       {
-         this.plugin = plugin;
-         this.entry = entry;
-         this.args = args;
-       }
+      public override string ToString ()
+      {
+       MText mt = "(" + name;
 
-      public override Xex.Term Eval (Xex.Domain domain)
-       {
-         Xex.Term[] args = new Xex.Term[this.args.Length];
-         for (int i = 0; i < args.Length; i++)
-           args[i] = this.args[i].Eval (domain);
-         return plugin.Call (entry, args);
-       }
+       if (title != null)
+         mt.Cat (" \"" + title + "\"");
+       mt.Cat (keymap.ToString ());
+       return (string) mt + ")";
+      }
     }
 
-    internal abstract class Marker : Xex.TermValue
-    {
-      MSymbol name;
-      MInputContext ic;
+    // Instance members
+    internal Xex.Domain domain;
 
-      public Marker (MSymbol name, MInputContext ic)
-       {
-         this.name = name;
-         this.ic = ic;
-       }
+    protected LoadStatus load_status = LoadStatus.None;
+    protected MDatabase.Tag tag;
+    private MDatabase mdb;
 
-      public abstract int Position { get; set; }
-    }
-
-    internal class NamedMarker : Marker
-    {
-      int position;
-
-      public NamedMaker (MSymbol name, MInputContext ic) : base (name, ic)
-       {
-         ic.markers.Put (name, this);
-       }
-
-      public override int Position {
-       get { return position; }
-       set { position = value; }
-      }
-    }
-     
-    internal class SystemMarker : Marker
-    {
-      public SystemMarker (MSymbol name, MInputContext ic) : base (name, ic)
-       {
-       }
-       
-      public override int Position {
-       get {
-         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_os;
-                 int to;
-                 ic.preedit.FindProp (MInputMethod.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 (MInputMethod.Mcandidates, pos,
-                                      out from, out pos);
-                 return pos;
-               }
-             return ic.preedit.Length;
-           default:
-             return name.Name[1] - '0';
-           }
-       }
-       set {
-         throw new Exception ("Can't set predefined marker: " + name);
-       }
-      }
-    }
-      
-    internal class Map
-    {
-      public MSymbol name;
-      public Dictionary<Key, Map> submaps;
-      public Xex actions;
-
-      public void Add (KeySeq keys, int index, Xex actions)
-      {
-       Map sub = null;
-
-       if (submaps == null)
-         submaps = new Dictionary<Key, Map> ();
-       else
-         submaps.TryGetValue (keys[index], out sub);
-       if (sub == null)
-         {
-           Key key = keys[index];
-           submaps[key] = sub = new Map ();
-         }
-       if (index + 1 < keys.Count)
-         sub.Add (keys, index + 1, actions);
-       else
-         this.actions = actions;
-      }
-
-      public Xex Lookup (KeySeq keys, int index)
-      {
-       Map sub;
-
-       if (index + 1 == keys.Count)
-         return actions;
-       if (submaps.TryGetValue (keys[index], out sub))
-         return sub.Lookup (keys, index + 1);
-       return null;
-      }
-
-      private void describe (MText mt, KeySeq keyseq)
-      {
-       if (keyseq.Count > 0)
-         {
-           mt.Cat (" (").Cat (keyseq.ToString ());
-           if (actions != null)
-             mt.Cat (' ').Cat (actions.ToString ());
-           mt.Cat (')');           
-         }
-       if (submaps != null)
-         foreach (KeyValuePair<Key, Map> kv in submaps)
-           {
-             keyseq.Add (kv.Key);
-             kv.Value.describe (mt, keyseq);
-             keyseq.RemoveAt (keyseq.Count - 1);
-           }
-      }
-
-      public override string ToString ()
-      {
-       MText mt = "(" + name.Name;
-       KeySeq keyseq = new KeySeq ();
-
-       describe (mt, keyseq);
-       mt.Cat (')');
-       return (string) mt;
-      }
-    }
-
-    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;
-
-    private LoadStatus load_status = LoadStatus.None;
-    private MDatabase.Tag tag;
-    private MDatabase mdb;
-
-    private MText description;
-    internal MText title;
-    internal Command[] commands;
-    internal Xex.Name[] var_names;
-    internal Dictionary<MSymbol, Plugin> plugins;
-    internal Dictionary<MSymbol, Map> maps;
-    internal MPlist states;
+    private MText description;
+    internal MText title;
+    internal Command[] commands;
+    internal Xex.Symbol[] var_names;
+    internal Dictionary<MSymbol, Plugin> plugins;
+    internal Dictionary<MSymbol, Map> maps;
+    internal Dictionary<Xex.Symbol, State> states;
+    internal State initial_state;
 
     static MInputMethod ()
     {
-      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);
-      im_domain.DefSubr (Fkeyseq, "keyseq", true, 1, -1);
+      im_domain.DefTerm ("keyseq", KeySeq.parser);
+      im_domain.DefTerm ("marker", Marker.parser);
+      im_domain.DefTerm ("selector", Selector.parser);
+
+      im_domain.DefSubr (Finsert, "insert", false, 1, 1);
+      im_domain.DefSubr (Finsert_candidates, "insert-candidates", false, 1, -1);
+      im_domain.DefSubr (Fdelete, "delete", false, 1, 1);
+      im_domain.DefSubr (Fselect, "select", false, 1, 1);
+      im_domain.DefSubr (Fshow, "show", false, 0, 0);
+      im_domain.DefSubr (Fhide, "hide", false, 0, 0);
+      im_domain.DefSubr (Fmove, "move", false, 1, 1);
+      im_domain.DefSubr (Fmark, "mark", false, 1, 1);
+      im_domain.DefSubr (Fpushback, "pushback", false, 1, 1);
+      im_domain.DefSubr (Fpop, "pop", false, 0, 0);
+      im_domain.DefSubr (Fundo, "undo", false, 0, 1);
+      im_domain.DefSubr (Fcommit, "commit", false, 0, 0);
+      im_domain.DefSubr (Funhandle, "unhandle", false, 0, 0);
+      im_domain.DefSubr (Fshift, "shift", false, 1, 1);
+      im_domain.DefSubr (Fshiftback, "shiftback", false, 0, 0);
+      im_domain.DefSubr (Fchar_at, "char-at", false, 1, 1);
+      im_domain.DefSubr (Fkey_count, "key-count", false, 1, 1);
+      im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag",
+                        false, 0, 0);
 
       MDatabase.Tag tag = new MDatabase.Tag (Minput_method, "*", "*", "*");
       List<MDatabase> list = MDatabase.List (tag);
@@ -838,6 +1270,7 @@ namespace M17N.Input
     private MInputMethod (MDatabase.Tag tag)
     {
       this.tag = tag;
+      domain = new Xex.Domain (tag[1].Name, im_domain, null);
     }
 
     // Instance Properties
@@ -861,10 +1294,15 @@ namespace M17N.Input
        }
       description = this.description;
       title = this.title;
-      variables = new Xex.Variable[var_names.Length];
-      int i = 0;
-      foreach (Xex.Name name in var_names)
-       variables[i++] = domain.GetVar (name);
+      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;
     }
@@ -884,7 +1322,7 @@ namespace M17N.Input
       return (im_table.TryGetValue (tag, out im) ? im : null);
     }
 
-    public bool Open ()
+    private bool Open ()
     {
       return ((load_status == LoadStatus.Full) || load_body ());
     }
@@ -904,6 +1342,7 @@ namespace M17N.Input
       mdb = MDatabase.Find (tag);
       if (mdb == null)
        return false;
+      mdb.name_table = Xex.Symbol.Table;
       try {
        MSymbol format = mdb.Format;
 
@@ -925,10 +1364,10 @@ namespace M17N.Input
 
     private bool load_body ()
     {
-      domain = new Xex.Domain (domain, null);
       mdb = MDatabase.Find (tag);
       if (mdb == null)
        return false;
+      mdb.name_table = Xex.Symbol.Table;
       try {
        object obj = mdb.Load ();
        if (obj is MPlist)
@@ -944,10 +1383,19 @@ namespace M17N.Input
       return true;
     }
 
+    private void add_default_state ()
+    {
+      Xex.Symbol Qinit = "init";
+      State state = new State (Qinit, title);
+      foreach (KeyValuePair<MSymbol, Map>kv in maps)
+       state.keymap.AddMap (kv.Value, null);
+      states[Qinit] = initial_state = state;
+    }
+
     private void load (MPlist plist, bool full)
     {
       maps = new Dictionary<MSymbol, Map> ();
-      states = new MPlist ();
+      states = new Dictionary<Xex.Symbol, State> ();
 
       for (; ! plist.IsEmpty; plist = plist.next)
        if (plist.IsPlist)
@@ -959,11 +1407,7 @@ namespace M17N.Input
 
                pl = pl.next;
                if (sym == Mdescription)
-                 {
-                   description = parse_description (pl);
-                   if (description == null)
-                     description = new MText ("No description");
-                 }
+                 description = parse_description (pl);
                else if (sym == Mtitle)
                  {
                    if (pl.IsMText)
@@ -992,20 +1436,12 @@ namespace M17N.Input
        description = (MText) "No description";
       if (title == null)
        title = new MText (tag[2].Name);
-      if (variables == null)
-       variables = new Variable[0];
       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, new Xex (plist, domain));
-         states.Add (state.name, state);
-       }
+      if (states.Count == 0)
+       add_default_state ();
     }
 
     private void load (XmlNode node, bool full)
@@ -1013,7 +1449,7 @@ namespace M17N.Input
       bool skip_header = load_status == LoadStatus.Header;
 
       maps = new Dictionary<MSymbol, Map> ();
-      states = new MPlist ();
+      states = new Dictionary<Xex.Symbol, State> ();
 
       if (node.NodeType == XmlNodeType.Document)
        node = node.FirstChild;
@@ -1050,140 +1486,12 @@ namespace M17N.Input
        description = (MText) "No description";
       if (title == null)
        title = new MText (tag[2].Name);
-      if (variables == null)
-       variables = new Variable[0];
       if (commands == null)
        commands = new Command[0];
       if (! full)
        return;
-      if (states.IsEmpty)
-       {
-         State state = new State ((MSymbol) "init");
-         MPlist plist = new MPlist ();
-         foreach (KeyValuePair<MSymbol, Map>kv in maps)
-           state.branches.Add (kv.Key, new Xex (plist, domain));
-         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);
-               }
-           }
-       }
+      if (states.Count == 0)
+       add_default_state ();
     }
 
     private static MText parse_description (MPlist plist)
@@ -1212,18 +1520,18 @@ namespace M17N.Input
       return node.InnerText;
     }
 
-    private void new_variable (Xex.Name name, string desc, int val,
+    private void new_variable (Xex.Symbol name, string desc, int val,
                               MPlist pl, Xex.Variable vari)
     {
-      Xex.Term term = new Xex.Term (val);
       int[] range;
 
       if (pl.IsEmpty)
        range = null;
       else
        {
-         range = new int[pl.Count * 2];
-         for (int i = 0; i < range.Length; i++)
+         int nrange = pl.Count;
+         range = new int[nrange * 2];
+         for (int i = 0; i < nrange; i++)
            {
              if (pl.IsPlist)
                {
@@ -1237,23 +1545,23 @@ namespace M17N.Input
              else if (pl.IsInteger)
                range[i * 2] = range[i * 2 + 1] = pl.Integer;
              else
-               throw new Exception ("Invalid range: " + p);
+               throw new Exception ("Invalid range: " + pl);
            }
        }
       if (vari == null)
-       domain.Defvar (new Xex.Variable.Int (name, desc, term, range));
+       domain.Defvar (new Xex.Variable.Int (domain, 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.Name name, string desc, MText val,
+    private void new_variable (Xex.Symbol name, string desc, MText val,
                               MPlist pl, Xex.Variable vari)
     {
-      Xex.Term term = new Xex.Term ((string) val);
       string[] range;
 
       if (pl.IsEmpty)
@@ -1264,90 +1572,112 @@ namespace M17N.Input
          for (int i = 0; i < range.Length; i++)
            {
              if (pl.IsMText)
-               range[i] = pl.Text;
+               range[i] = (string) pl.Text;
              else
-               throw new Exception ("Invalid range: " + p);
+               throw new Exception ("Invalid range: " + pl);
            }
        }
       if (vari == null)
-       domain.Defvar (new Xex.Variable.Str (name, desc, term, range));
+       domain.Defvar (new Xex.Variable.Str (domain, 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.Name name, string desc, MSymbol val,
+    private void new_variable (Xex.Symbol name, string desc, MSymbol val,
                               MPlist pl, Xex.Variable vari)
     {
-      Xex.Term term = new Xex.Term ((Xex.Name) val.name);
-      Xex.Name[] range;
+      Xex.Symbol[] range;
+      Xex.Symbol sym = val.Name;
 
       if (pl.IsEmpty)
        range = null;
       else
        {
-         range = new Xex.Name[pl.Count * 2];
+         range = new Xex.Symbol[pl.Count * 2];
          for (int i = 0; i < range.Length; i++)
            {
              if (pl.IsSymbol)
-               range[i] = pl.Symbol.name;
+               range[i] = pl.Symbol.Name;
              else
-               throw new Exception ("Invalid range: " + p);
+               throw new Exception ("Invalid range: " + pl);
            }
        }
       if (vari == null)
-       domain.Defvar (new Xex.Variable.Sym (name, desc, term, range));
+       domain.Defvar (new Xex.Variable.Sym (domain, 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)
     {
-      variables = new Variable[plist.Count];
+      var_names = new Xex.Symbol[plist.Count];
 
-      for (int i = 0; ! plist.IsEmpty; plist = plist.next)
-       if (plist.IsPlist && plist.Plist.IsSymbol)
-         {
-           MPlist p = plist.Plist;
-           Xex.Name name = (Name) p.Symbol.name;
+      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;
-           string desc = parse_description (p);
-           Xex.Variable vari = null;
-           Xex.Term val;
-
-           if (im_global.domain.GetVar (name, out vari))
-             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 ("Unknown type: " + p.val);
-             }
-         }
+         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;
 
-      variables = new Variable[node_list.Count];
+      var_names = new Xex.Symbol[node_list.Count];
       for (int i = 0; i < node_list.Count; i++)
-       if (node_list[i].NodeType == XmlNodeType.Element)
-         variables[i] = new Variable (node_list[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)
@@ -1379,13 +1709,13 @@ namespace M17N.Input
        {
          MPlist p = plist.Plist;
          MSymbol sym = p.Symbol;
-         Plugin plugin = new Plugin ();
+         Plugin plugin = new Plugin (sym.Name);
 
-         plugin.name = sym.Name;
-         plugin.methods = new MPlist ();
          for (p = p.next; ! p.IsEmpty; p = p.next)
-           plugin.methods.Add (p.Symbol, null);
-         plugins.Add (sym, plugin);
+           {
+             Xex.Function func = new PluginMethod (plugin, p.Symbol.Name);
+             domain.Defun (func);
+           }
        }
     }
 
@@ -1395,58 +1725,69 @@ namespace M17N.Input
 
       foreach (XmlNode n in node.ChildNodes)
        {
-         Plugin plugin = new Plugin ();
-         plugin.name = n.Attributes["id"].Value;
-         plugin.methods = new MPlist ();
+         Plugin plugin = new Plugin (n.Attributes[0].Value);
          foreach (XmlNode nn in n.ChildNodes)
-           plugin.methods.Add ((MSymbol) nn.Attributes["id"].Value,
-                               null);
-         plugins.Add (plugin.name, plugin);
+           {
+             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
-           && nn.Name == "xi:include")
-         {
-           XmlNode n = nn.FirstChild.FirstChild;
-           MSymbol language = n.InnerText;
-           n = n.NextSibling;
-           MSymbol name = n.InnerText;
-           n = n.NextSibling;
-           MSymbol subname = (n != null ? n.InnerText : MSymbol.nil);
-           n = n.ParentNode.NextSibling;
-           MSymbol section = n.InnerText;
-           n = n.NextSibling;
-           MSymbol id = (n != null ? n.InnerText : MSymbol.nil);
-
-           MInputMethod im = MInputMethod.Find (language, name, subname);
-           if (im == null || ! im.Open ())
-             continue;
-           if (id == MSymbol.nil)
-             im.domain.CopyFunc (domain);
-           else
-             im.domain.CopyFunc (domain, id);
-         }
-      for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
-       if (nn.NodeType == XmlNodeType.Element
-           && nn.Name != "xi:include")
-         domain.Defun ((MSymbol) node.GetAttribute ("id"));
+       if (nn.NodeType == XmlNodeType.Element)
+         domain.Defun (nn, true);
       for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
-       if (nn.NodeType == XmlNodeType.Element
-           && nn.Name != "xi:include")
-         domain.Defun ((MSymbol) node.GetAttribute ("id"), null,
-                             nn.FirstChild);
+       if (nn.NodeType == XmlNodeType.Element)
+         domain.Defun (nn, false);
     }
 
     private void parse_maps (XmlNode node)
     {
+      for (node = node.FirstChild; node != null; node = node.NextSibling)
+       if (node.Name == Qmap)
+         {
+           MSymbol name = node.Attributes[0].Value;
+           Map map = new Map (name);
+           maps[name] = map;
+           for (XmlNode nd = node.FirstChild; nd != null; nd = nd.NextSibling)
+             if (nd.Name == Qrule)
+               {
+                 XmlNode n = nd.FirstChild;
+                 if (n.Name != Qkeyseq)
+                   continue;
+                 KeySeq keyseq = (KeySeq) KeySeq.parser (domain, n);
+                 Xex.Term[] actions = Xex.ParseTerms (domain, n.NextSibling);
+                 map.entries.Add (new Map.Entry (domain, keyseq, actions));
+               }
+         }
+    }
+
+    private void parse_states (MPlist plist)
+    {
+      for (; ! plist.IsEmpty; plist = plist.next)
+       if (plist.IsPlist)
+         {
+           State state = new State (this, plist.Plist);
+           states[state.name] = state;     
+           if (initial_state == null)
+             initial_state = state;
+         }
     }
 
     private void parse_states (XmlNode node)
     {
+      for (node = node.FirstChild; node != null; node = node.NextSibling)
+       if (node.Name == Qstate)
+         {
+           State state = new State (this, node);
+           states[state.name] = state;
+           if (initial_state == null)
+             initial_state = state;
+         }
     }
 
     private void parse_include (MPlist plist)
@@ -1487,7 +1828,7 @@ namespace M17N.Input
          if (target_name == MSymbol.nil)
            im.domain.CopyFunc (domain);
          else
-           im.domain.CopyFunc (domain, target_name);
+           im.domain.CopyFunc (domain, (Xex.Symbol) target_name.Name);
        }
       else if (target_type == Mmap)
        {
@@ -1507,16 +1848,190 @@ namespace M17N.Input
        {
          if (target_name == MSymbol.nil)
            {
-             for (p = im.states; ! p.IsEmpty; p = p.next)
-               states.Add (p.key, p.val);
+             foreach (KeyValuePair<Xex.Symbol, State> kv in im.states)
+               states[kv.Key] = kv.Value;
+           }
+         else
+           {
+             Xex.Symbol state_name = target_name.Name;
+             State state;
+             if (im.states.TryGetValue (state_name, out state))
+               states[state_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> ();
+         arg.Add (parse_action (p, true));
+         for (p = p.next; ! p.IsEmpty; p = p.next)
+           arg.Add (parse_action (p, false));
+         args[i] = new Xex.Term (arg);
+       }
+      return new Xex.Term (domain, Qcond, args);
+    }
+
+    private Xex.Term parse_insert (MPlist plist)
+    {
+      Xex.Term[] args;
+      Xex.Term arg;
+      if (plist.IsSymbol)
+       arg = new Xex.Term (domain, (Xex.Symbol) plist.Symbol.Name);
+      else if (plist.IsMText)
+       arg = new Xex.Term ((string) plist.Text);
+      else if (plist.IsInteger)
+       arg = new Xex.Term (plist.Integer);
+      else if (plist.IsPlist)
+       {
+         MPlist pl = plist.Plist;
+
+         args = new Xex.Term[pl.Count];
+         int i;
+         for (i = 0; ! pl.IsEmpty; i++, pl = pl.next)
+           {
+             if (pl.IsMText)
+               args[i] = new Xex.Term ((string) pl.Text);
+             else if (pl.IsPlist)
+               {
+                 List<Xex.Term> list = new List<Xex.Term> ();
+                 for (MPlist p = pl.Plist; ! p.IsEmpty; p = p.next)
+                   {
+                     if (p.IsMText)
+                       list.Add (new Xex.Term ((string) p.Text));
+                     else
+                       throw new Exception ("Invalid candidates: " + p);
+                   }                 
+               }
+             else
+               throw new Exception ("Invalid candidates: " + pl);
+           }
+         return new Xex.Term (domain, Qinsert_candidates, args);
+       }
+      else
+       throw new Exception ("Invalid arg to insert: " + plist);
+      args = new Xex.Term[1];
+      args[0] = arg;
+      return new Xex.Term (domain, Qinsert, args);
+    }
+
+    private Xex.Term parse_select (MPlist plist)
+    {
+      Xex.Term[] args = new Xex.Term[1];
+      if (plist.IsInteger)
+       args[0] = new Xex.Term (plist.Integer);
+      else if (! plist.IsSymbol)
+       throw new Exception ("Invalid arg to select: " + plist);
+      else if (plist.Symbol.Name[0] == '@')
+       args[0] = new Xex.Term (Selector.Get (plist.Symbol));
+      else
+       args[0] = new Xex.Term (domain, (Xex.Symbol) plist.Symbol.Name);
+      return new Xex.Term (domain, Qselect, args);
+    }
+
+    private Xex.Term parse_funcall_with_marker (MPlist plist, Xex.Symbol func)
+    {
+      Xex.Term[] args = new Xex.Term[1];
+      if (plist.IsInteger && func != Qmark)
+       args[0] = new Xex.Term (plist.Integer);
+      else if (plist.IsSymbol)
+       args[0] = new Xex.Term (Marker.Get (plist.Symbol));
+      else
+       throw new Exception ("Invalid arg to " + func + ": " + plist);
+      return new Xex.Term (domain, func, args);
+    }
+
+    private Xex.Term parse_char_at (MSymbol name)
+    {
+      Xex.Term[] args = new Xex.Term[1];
+      args[0] = new Xex.Term (Marker.Get (name));
+      return new Xex.Term (domain, Qchar_at, args);
+    }
+
+    private Xex.Term parse_shift (MPlist plist)
+    {
+      Xex.Term[] args = new Xex.Term[1];
+      if (! plist.IsSymbol)
+       throw new Exception ("Invalid arg to shift: " + plist);
+      args[0] = new Xex.Term ((Xex.Symbol) plist.Symbol.Name);
+      return new Xex.Term (domain, Qshift, args);
+    }
+
+    private Xex.Term parse_action (MPlist plist, bool as_funarg)
+    {
+      if (plist.IsPlist)
+       {
+         MPlist p = plist.Plist;
+             
+         if (p.IsMText || p.IsPlist)
+           return parse_insert (plist);
+         if (! p.IsSymbol)
+           throw new Exception ("Invalid action: " + p);
+         MSymbol sym = p.Symbol;
+         Xex.Symbol name = sym.Name;
+         p = p.next;
+         if (name == Qcond)
+           return parse_cond (p);
+         if (name == Qinsert)
+           return parse_insert (p);
+         if (name == Qselect)
+           return parse_select (p);
+         if (name == Qdelete || name == Qmove || name == Qmark)
+           return parse_funcall_with_marker (p, name);
+         if (name == Qshift)
+           return parse_shift (p);
+         if (((string) name)[0] == '@')
+           return parse_char_at (sym);
+         if (name == Qset || name == Qadd || name == Qsub
+                  || name == Qmul || name == Qdiv)
+           {
+             if (! p.IsSymbol)
+               throw new Exception ("Invalid action: " + p);
+             Xex.Symbol varname = p.Symbol.Name;
+             Xex.Term[] args = new Xex.Term[1];
+             args[0] = parse_action (p.next, true);
+             return new Xex.Term (domain, name, varname, args);
            }
          else
            {
-             object state = im.states.Get (target_name);
-             if (state != null)
-               states.Add (target_name, state);
+             if (name == Qeq)
+               name = Qeqeq;
+             if (p.IsEmpty)
+               return new Xex.Term (domain, name, null);
+             else
+               return new Xex.Term (domain, name, parse_actions (p, true));
            }
        }
+      else if (plist.IsSymbol)
+       {
+         if (plist.Symbol.Name[0] == '@')
+           return parse_char_at (plist.Symbol);
+         return new Xex.Term (domain, (Xex.Symbol) plist.Symbol.Name);
+       }
+      else if (plist.IsMText)
+       return (as_funarg ? new Xex.Term ((string) plist.Text)
+               : parse_insert (plist));
+      else if (plist.IsInteger)
+       return (as_funarg ? new Xex.Term (plist.Integer)
+               : parse_insert (plist));
+      else
+       throw new Exception ("Invalid action: " + plist);
+    }
+
+    private Xex.Term[] parse_actions (MPlist plist, bool as_funarg)
+    {
+      Xex.Term[] terms = new Xex.Term[plist.Count];
+
+      for (int i = 0; ! plist.IsEmpty; i++, plist = plist.next)
+       terms[i] = parse_action (plist, as_funarg);
+      return terms;
     }
 
     private void parse_macros (MPlist plist)
@@ -1528,7 +2043,7 @@ namespace M17N.Input
 
            if (! p.IsSymbol)
              continue;
-           domain.Defun (p.Symbol, null, null);
+           domain.Defun ((Xex.Symbol) p.Symbol.Name, null, null, true);
          }
       for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
        if (pl.IsPlist)
@@ -1537,8 +2052,8 @@ namespace M17N.Input
 
            if (! p.IsSymbol)
              continue;
-           transform (p.next);
-           domain.Defun (p.Symbol, null, p.next);
+           domain.Defun ((Xex.Symbol) p.Symbol.Name, null,
+                         parse_actions (p.next, false), false);
          }
     }
 
@@ -1551,9 +2066,8 @@ namespace M17N.Input
          
            if (! pl.IsSymbol)
              continue;
-           Map map = new Map ();
-           map.name = pl.Symbol;
-           maps[map.name] = map;
+           Map map = new Map (pl.Symbol);
+           maps[pl.Symbol] = map;
            for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
              {
                if (! pl.IsPlist)
@@ -1567,56 +2081,20 @@ namespace M17N.Input
                else
                  continue;
                p = p.next;
-               if (p.IsEmpty)
-                 continue;
-               transform (p);
-               MXex expr = new MXex (p, domain);
-               map.Add (keys, 0, expr);
+               Xex.Term[] actions
+                 = p.IsEmpty ? null : parse_actions (p, false);
+               map.entries.Add (new Map.Entry (domain, keys, actions));
              }
          }
     }
 
-    private void parse_states (MPlist plist)
+    private static Xex.Term Finsert (Xex.Domain domain, Xex.Variable vari,
+                                    Xex.Term[] args)
     {
-      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;
-               transform (p);
-               state.branches.Add (map_name,
-                                   new MXex (p, domain));
-             }
-         }
-    }
-
-    private static Xex.Term Finsert (Xex.Domain domain, Xex.Variable vari,
-                                    Xex.Term[] args)
-    {
-      ((MInputContext) domain.context).insert (args[0]);
+      if (args[0].IsInt)
+       ((Context) domain.context).insert (args[0].Intval, null);
+      else
+       ((Context) domain.context).insert ((MText) args[0].Strval, null);
       return args[0];
     }
 
@@ -1624,81 +2102,106 @@ namespace M17N.Input
                                                Xex.Variable vari,
                                                Xex.Term[] args)
     {
-      ((MInputContext) domain.context).insert_candidates (args[0]);
+      Context ic = (Context) domain.context;
+      Xex.Variable v = ic.domain.GetVar (Qcandidates_group_size, false);
+      int column = (v == null ? 0 : v.Value.Intval);
+      Candidates candidates = new Candidates (args, column);
+      object candidate = candidates.Current;
+
+      if (candidate is MText)
+       ic.insert ((MText) candidate, candidates);
+      else
+       ic.insert ((int) candidate, candidates);
       return args[0];
     }
 
-    private static Xex.Term Fmarker (Xex.Domain domain, Xex.Variable vari,
-                                    Xex.Term[] args)
+    private static Xex.Term Fchar_at (Xex.Domain domain, Xex.Variable vari,
+                                     Xex.Term[] args)
     {
-      MSymbol sym = (MSymbol) args[0].Args[0].Val;
+      Context ic = (Context) domain.context;
+      Marker m = (Marker) args[0].Objval;
 
-      return ((MInputContext) domain.context).marker (sym);
+      return new Xex.Term (m.CharAt (ic));
     }
 
-    private static Xex.Term char_at (Xex.Domain domain, Xex.Variable vari,
+    private static Xex.Term Fdelete (Xex.Domain domain, Xex.Variable vari,
                                     Xex.Term[] args)
     {
-      return ((MInputContext) domain.context).char_at ((int) args[0].Val);
-    }
+      Context ic = (Context) domain.context;
+      int pos;
 
-    private static Xex.Term Fdelete (Xex.Domain domain, Xex.Variable vari,
-                                  Xex.Term[] args)
-    {
-      ((MInputContext) domain.context).delete ((int) args[0].Intval);
-      return true;
+      if (args[0].IsInt)
+       pos = args[0].Intval;
+      else
+       {
+         Marker m = (Marker) args[0].Objval;
+         pos = m.Position (ic);
+       }
+      return new Xex.Term (ic.delete (pos));
     }
 
     private static Xex.Term Fselect (Xex.Domain domain, Xex.Variable vari,
-                                  Xex.Term[] args)
+                                    Xex.Term[] args)
     {
-      MInputContext ic = (MInputContext) domain.context;
+      Context ic = (Context) domain.context;
+      Candidates can = ic.candidates;
 
-      if (args[0].IsInt)
-       ic.select (args[0].Intval);
-      else
-       ic.select ((MSYmbol) ((string) args[0].Nameval));
+      if (can != null)
+       {
+         object candidate = can.Current;
+
+         if (candidate is MText)
+           ic.delete (ic.cursor_pos - ((MText) candidate).Length);
+         else
+           ic.delete (ic.cursor_pos - 1);
+         if (args[0].IsInt)
+           candidate = can.Select (args[0].Intval);
+         else
+           candidate = can.Select ((Selector) args[0].Objval);
+         if (candidate is MText)
+           ic.insert ((MText) candidate, can);
+         else
+           ic.insert ((int) candidate, can);
+       }
       return args[0];
     }
 
     private static Xex.Term Fshow (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+                                  Xex.Term[] args)
     {
-      ((MInputContext) domain.context).show ();
-      return Xex.Zero;
+      ((Context) domain.context).show ();
+      return Tnil;
     }
 
     private static Xex.Term Fhide (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+                                  Xex.Term[] args)
     {
-      ((MInputContext) domain.context).hide ();
-      return Xex.Zero;
+      ((Context) domain.context).hide ();
+      return Tnil;
     }
 
     private static Xex.Term Fmove (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+                                  Xex.Term[] args)
     {
-      ((MInputContext) domain.context).move (args[0].Intval);
+      Context ic = (Context) domain.context;
+      int pos = (args[0].IsInt ? args[0].Intval
+                : ((Marker) args[0].Objval).Position (ic));
+      ic.move (pos);
       return args[0];
     }
 
     private static Xex.Term Fmark (Xex.Domain domain, Xex.Variable vari,
                                   Xex.Term[] args)
     {
-      ((MInputContext) domain.context).mark ((Marker) args[0].Objval);
+      Marker m = (Marker) args[0].Objval;
+      m.Mark ((Context) domain.context);
       return args[0];
     }
 
-    private static Xex.Term Fkeyseq (Xex.Domain domain, Xex.Variable vari,
-                                    Xex.Term[] args)
-    {
-      return new KeySeq (args);
-    }
-
     private static Xex.Term Fpushback (Xex.Domain domain, Xex.Variable vari,
-                                      Xex[] args)
+                                      Xex.Term[] args)
     {
-      MInputContext ic = (MInputContext) domain.context;
+      Context ic = (Context) domain.context;
 
       if (args[0].IsInt)
        ic.pushback (args[0].Intval);
@@ -1712,43 +2215,69 @@ namespace M17N.Input
     private static Xex.Term Fpop (Xex.Domain domain, Xex.Variable vari,
                                  Xex.Term[] args)
     {
-      ((MInputContext) domain.context).pop ();
-      return Xex.Zero;
+      ((Context) 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 Xex.Zero;
+      ((Context) 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 Xex.Zero;
+      ((Context) domain.context).commit ();
+      return Tnil;
     }
 
     private static Xex.Term Funhandle (Xex.Domain domain, Xex.Variable vari,
                                       Xex.Term[] args)
     {
-      ((MInputContext) domain.context).commit ();
+      ((Context) domain.context).commit ();
       args = new Xex.Term[2];
-      args[0] = args[1] = catch_tag;
+      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);
+      Context ic = (Context) domain.context;
+      State state;
+      if (ic.im.states.TryGetValue (args[0].Symval, out state))
+       ((Context) domain.context).shift (state);
+      else
+       throw new Exception ("Unknown state: " + args[0].Symval);
       return args[0];
     }
 
+    private static Xex.Term Fshiftback (Xex.Domain domain, Xex.Variable vari,
+                                       Xex.Term[] args)
+    {
+      ((Context) domain.context).shift (null);
+      return Tnil;
+    }
+
+    private static Xex.Term Fkey_count (Xex.Domain domain, Xex.Variable vari,
+                                       Xex.Term[] args)
+    {
+      return new Xex.Term (((Context) domain.context).key_head);
+    }
+
+    private static Xex.Term Fsurrounding_flag (Xex.Domain domain,
+                                              Xex.Variable vari,
+                                              Xex.Term[] args)
+    {
+      return new Xex.Term (GetSurroundingText == null ? 0 : 1);
+    }
+
     public override string ToString ()
     {
+      this.Open ();
       string str = (String.Format ("({0} (title \"{1}\")", tag, title));
       if (commands != null)
        {
@@ -1757,10 +2286,10 @@ namespace M17N.Input
            str += " " + cmd;
          str += ")";
        }
-      if (variables != null)
+      if (var_names != null)
        {
          str += " (variables";
-         foreach (Variable var in variables)
+         foreach (Xex.Symbol var in var_names)
            str += " " + var;
          str += ")";
        }
@@ -1775,712 +2304,531 @@ namespace M17N.Input
       foreach (KeyValuePair<MSymbol, Map> kv in maps)
        str += " " + kv.Value;
       str += ") (states";
-      foreach (MPlist p in states)
-       str += " " + p.val;
+      foreach (KeyValuePair<Xex.Symbol, State> kv in states)
+       {
+         str += " (" + kv.Key + " " + kv.Value.keymap + ")";
+       }
       return str + "))";
     }
-  }
-
-  public class MInputContext
-  {
-    internal static MSymbol Mcandidates_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;
-    private bool status_changed;
-    private MText preedit;
-    private bool preedit_changed;
-    private int cursor_pos;
-    private bool cursor_pos_changed;
-    private Candidates candidates;
-    private MPlist candidate_group;
-    private int candidate_index;
-    private int candidate_from, candidate_to;
-    private bool candidate_show;
-    private bool candidate_changed;
-
-    private Stack<MInputMethod.State> states;
-    internal MInputMethod.KeySeq keys;
-    private int key_head;
-    private int state_key_head;
-    private MPlist state_boundary;
-    private int commit_key_head;
-    private MText state_preedit;
-    private int state_pos;
-    internal MPlist markers = new MPlist ();
-    private MPlist vars;
-    private MPlist vars_saved;
-    internal MText preceding_text = new MText ();
-    internal MText following_text = new MText ();
-    private bool key_unhandled;
-
-    internal Xex.Domain domain;
 
-    public MInputContext (MInputMethod im)
+    public class Context
     {
-      this.im = im;
-      domain = new Xex.Domain (im.domain, this);
-      states = new Stack<MInputMethod.State> ();
-      states.Push ((MInputMethod.State) im.states.val);
-      keys = new MInputMethod.KeySeq ();
-    }
+      internal MInputMethod im;
+      internal Xex.Domain domain;
+      private bool active;
 
-    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);
+      private MText status;
+      private MText produced = new MText ();
+      internal MText preedit = new MText ();
+      internal int cursor_pos;
+      internal MPlist marker_positions = new MPlist ();
 
-      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;
-    }
+      internal Candidates candidates;
+      private bool candidate_show;
+      public bool CandidateShow { get { return candidate_show; } }
 
-    private void preedit_replace (int from, int to, int c)
-    {
-      preedit.Del (from, to);
-      preedit.Ins (from, c);
-      adjust_markers (from, to, c);
-    }
+      private State state, prev_state;
+      private MText state_preedit = new MText ();
+      private int state_key_head;
+      private object state_var_values, state_initial_var_values;
+      private int state_pos;
 
-    private void preedit_replace (int from, int to, MText mt)
-    {
-      preedit[from, to] = mt;
-      adjust_markers (from, to, mt);
-    }
+      private Keymap keymap;
+      // Sequence of input keys.
+      internal KeySeq keys = new KeySeq ();
+      // Index into KEYS specifying the next key to handle.
+      internal int key_head;
 
-    internal void insert (Xex.Term arg)
-    {
-      if (arg.IsInt is int)
-       preedit_replace (cursor_pos, cursor_pos, arg.Intval);
-      else
-       preedit_replace (cursor_pos, cursor_pos, new MText (arg.Strval));
-      preedit_changed = true;
-      cursor_pos_changed = true;
-    }
+      internal MText preceding_text = new MText ();
+      internal MText following_text = new MText ();
 
-    private class Candidates
-    {
-      private class Block
-      {
-       public int Index;
-       public object Data;
+      // Set to false before calling the method 'handle_key', and set
+      // to true when some key is unhandled.
+      private bool key_unhandled;
 
-       public Block (int index, MPlist plist)
-       {
-         Index = index;
-         if (plist.IsMText)
-           Data = plist.Text;
-         else
-           Data = plist.Plist;
-       }
+      // The unhandled key.  It has the meaning only when
+      // 'key_unhandled' is true.
+      private Key unhandled_key;
 
-       public int Count
-       {
-         get { return (Data is MText
-                       ? ((MText) Data).Length
-                       : ((MPlist) Data).Count); }
-       }
+      internal ChangedStatus changed;
 
-       public object this[int i]
-       {
-         get {
-           if (Data is MText) return ((MText) Data)[i];
-           return  ((MPlist) Data)[i];
-         }
-       }
+      private void set_cursor (string prefix, int pos)
+      {
+       cursor_pos = pos;
+       if (cursor_pos > 0)
+         candidates = (Candidates) preedit.GetProp (cursor_pos - 1,
+                                                    Mcandidates);
+       else
+         candidates = null;
       }
 
-      private Block[] blocks;
-      private int row = 0;
-      private int index = 0;
-      public object[] group;
+      internal void reset ()
+      {
+       status = im.initial_state.title;
+       produced.Del ();
+       preedit.Del ();
 
-      private bool IsFixed { get { return group != null; } }
-      private int Total {
-       get {
-         Block last = blocks[blocks.Length - 1];
-         return last.Index + last.Count; }
-      }
+       set_cursor ("reset", 0);
+       marker_positions.Clear ();
+       candidates = null;
+       candidate_show = false;
 
-      public int Column {
-       get { return (IsFixed ? index % group.Length
-                     : index - blocks[row].Index); }
-      }
+       state = prev_state = null;
+       state_preedit.Del ();
+       state_var_values = state_initial_var_values;
+       state_pos = 0;
+       shift (im.initial_state);
+
+       preceding_text.Del ();
+       following_text.Del ();
 
-      public object Group {
-       get { return (IsFixed ? group : blocks[row].Data); }
+       changed = ChangedStatus.None;
       }
 
-      public int GroupLength
+      static Xex.Term[] catch_args = new Xex.Term[2];
+
+      private bool take_actions (Xex.Term[] actions)
       {
-       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;
-       }
+       catch_args[0] = Tcatch_tag;
+       catch_args[1]= new Xex.Term (domain, Qprogn, actions);
+       Xex.Term term = new Xex.Term (domain, Qcatch, catch_args);
+       term = term.Eval (domain);
+       return (! term.IsSymbol || term.Symval != Tcatch_tag.Symval);
       }
 
-      public object Current {
-       get {
-         return (IsFixed ? group[index % group.Length]
-                 : blocks[row][index - blocks[row].Index]);
-       }
+      static MPlist callback_arg = new MPlist ();
+
+      private bool get_surrounding_text (int len)
+      {
+       if (len < 0 ? -len <= preceding_text.Length
+           : len <= following_text.Length)
+         return true;
+       if (GetSurroundingText == null)
+         return false;
+       callback_arg.Set (MSymbol.integer, len);
+       if (! GetSurroundingText (this, callback_arg)
+           || ! callback_arg.IsMText)
+         return false;
+       if (len < 0)
+         {
+           preceding_text = callback_arg.Text;
+           return (-len <= preceding_text.Length);
+         }
+       following_text = callback_arg.Text;
+       return (len <= following_text.Length);
       }
 
-      public Candidates (MPlist list, int column)
+      internal int GetSurroundingChar (int pos)
+      {
+       if (! get_surrounding_text (pos < 0 ? pos : pos + 1))
+         return 0;
+       if (pos < 0)
+         return preceding_text[preceding_text.Length + pos];
+       return following_text[pos];
+      }        
+
+      private void adjust_markers (int from, int to, int inserted)
       {
-       int nblocks = list.Count;
+       int diff = inserted - (to - from);
 
-       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];
+       for (MPlist p = marker_positions; ! p.IsEmpty; p = p.next)
+         {
+           int pos = p.Integer;
+           if (pos > from)
+             p.Set (p.Key, pos >= to ? pos + diff : from);
+         }
+       if (cursor_pos >= to)
+         set_cursor ("adjust", cursor_pos + diff);
+       else if (cursor_pos > from)
+         set_cursor ("adjust", from);
       }
 
-      public Candidates (List<Xex.Term> list, int column)
+      private void preedit_replace (int from, int to, int c,
+                                   Candidates candidates)
       {
-       int nblocks = list.Count;
+       preedit.Del (from, to);
+       preedit.Ins (from, c);
+       if (candidates != null)
+         {
+           preedit.PushProp (from, from + 1, Mcandidates, candidates);
+           changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                       | CandidateAll);
+         }
+       adjust_markers (from, to, 1);
+      }
 
-       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];
+      private void preedit_replace (int from, int to, MText mt,
+                                   Candidates candidates)
+      {
+       preedit[from, to] = mt;
+       if (candidates != null)
+         {
+           preedit.PushProp (from, from + mt.Length, Mcandidates, candidates);
+           changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                       | CandidateAll);
+         }
+       adjust_markers (from, to, mt == null ? 0 : mt.Length);
       }
 
-      public static void Detach (MInputContext ic)
+      internal void insert (int c, Candidates candidates)
       {
-       ic.preedit.PopProp (0, ic.preedit.Length, MInputMethod.Mcandidates);
-       ic.candidates = null;
-       ic.preedit_changed = true;
-       ic.cursor_pos_changed = true;
-       ic.candidate_changed = true;
+       preedit_replace (cursor_pos, cursor_pos, c, candidates);
+       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
       }
 
-      // 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".
+      internal void insert (MText mt, Candidates candidates)
+      {
+       preedit_replace (cursor_pos, cursor_pos, mt, candidates);
+       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
+      }
 
-      private int fill_group (int start)
+      internal int delete (int pos)
       {
-       int nitems = group.Length;
-       int r = row;
-       Block b = blocks[r];
+       int deleted = pos - cursor_pos;
 
-       if (start < b.Index)
-         while (start < b.Index)
-            b = blocks[--r];
+       if (pos < cursor_pos)
+         {
+           if (pos < 0)
+             {
+               if (DeleteSurroundingText != null)
+                 {
+                   callback_arg.Set (MSymbol.integer, pos);
+                   if (DeleteSurroundingText (this, callback_arg))
+                     {
+                       if (callback_arg.IsInteger)
+                         deleted = callback_arg.Integer - cursor_pos;
+                       preceding_text.Del ();
+                     }
+                   else
+                     deleted = - cursor_pos;
+                 }
+               pos = 0;
+             }
+           if (pos < cursor_pos)
+             preedit_replace (pos, cursor_pos, null, null);
+         }
        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)
+           if (pos > preedit.Length)
              {
-               r++;
-               if (r == blocks.Length)
-                 return i;
-               b = blocks[r];
-               count = b.Count;
-               start = 0;
+               if (DeleteSurroundingText != null)
+                 {
+                   callback_arg.Set (MSymbol.integer, pos - preedit.Length);
+                   if (DeleteSurroundingText (this, callback_arg))
+                     {
+                       if (callback_arg.IsInteger)
+                         deleted = callback_arg.Integer - cursor_pos;
+                       preceding_text.Del ();
+                     }
+                   else
+                     deleted = preedit.Length - cursor_pos;
+                 }
+               pos = preedit.Length;
              }
-           group[i] = b[start];
+           if (pos > cursor_pos)
+             preedit_replace (cursor_pos, pos, null, null);
          }
-       return nitems;
+       if (deleted != 0)
+         changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
+       return deleted;
       }
 
-      // 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.
+      internal void show ()
+      {
+       candidate_show = true;
+       changed |= ChangedStatus.CandidateShow;
+      }
 
-      public int PrevGroup ()
+      internal void hide ()
       {
-       int nitems;
-       int col = Column;
+       candidate_show = false;
+       changed |= ChangedStatus.CandidateShow;
+      }
 
-       if (IsFixed)
-         {
-           nitems = group.Length;
-           if ((index -= col + nitems) < 0)
-             index = (Total / nitems) * nitems;
-           nitems = fill_group (index);
-         }
-       else
+      internal void move (int pos)
+      {
+       if (pos < 0)
+         pos = 0;
+       else if (pos > preedit.Length)
+         pos = preedit.Length;
+       if (pos != cursor_pos)
          {
-           row = row > 0 ? row-- : blocks.Length - 1;
-           nitems = blocks[row].Count;
-           index = blocks[row].Index;
+           set_cursor ("move", pos);
+           changed |= ChangedStatus.Preedit;
          }
-       index += col < nitems ? col : nitems - 1;
-       return nitems;
       }
 
-      public int NextGroup ()
+      internal void pushback (int n)
       {
-       int nitems;
-       int col = Column;
-
-       if (IsFixed)
+       if (n > 0)
          {
-           nitems = group.Length;
-           if ((index += nitems - col) >= Total)
-             index = 0;
-           nitems = fill_group (index);
+           key_head -= n;
+           if (key_head < 0)
+             key_head = 0;
          }
+       else if (n == 0)
+         key_head = 0;
        else
          {
-           row = row < blocks.Length - 1 ? row + 1 : 0;
-           nitems = blocks[row].Count;
-           index = blocks[row].Count;
+           key_head = - n;
+           if (key_head > keys.keyseq.Count)
+             key_head = keys.keyseq.Count;
          }
-       index += col < nitems ? col : nitems - 1;
-       return nitems;
       }
 
-      public void Prev ()
+      internal void pushback (KeySeq keyseq)
       {
-       int col = Column;
+       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]);
+      }
 
-       if (col == 0)
-         {
-           int nitems = PrevGroup ();
-           index += col < nitems - 1 ? col : nitems - 1;
-         }
+      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
-         index--;
+         keys.keyseq.RemoveRange (n, keys.keyseq.Count  - n);
+       reset ();
       }
 
-      public void Next ()
+      internal void commit ()
       {
-       int col = Column;
-       int nitems = GroupLength;
+       produced.Cat (preedit);
+       preedit_replace (0, preedit.Length, null, null);
+       changed |= ChangedStatus.Preedit;
+      }
 
-       if (col == nitems - 1)
+      internal void shift (State state)
+      {
+       if (state == null)
          {
-           nitems = NextGroup ();
-           index -= Column;
+           if (prev_state == null)
+             return;
+           state = prev_state;
+         }
+
+       if (state == im.initial_state)
+         {
+           commit ();
+           keys.keyseq.RemoveRange (0, key_head);
+           key_head = 0;
+           if (state != this.state)
+             {
+               domain.RestoreValues (state_initial_var_values);
+               if (state.enter_actions != null)
+                 take_actions (state.enter_actions);
+             }
+           prev_state = null;
          }
        else
-         index++;
+         {
+           if (state != this.state && state.enter_actions != null)
+             take_actions (state.enter_actions);
+           prev_state = this.state;
+         }
+       save_state ();
+       if (this.state == null || this.state.title != state.title)
+         this.changed |= ChangedStatus.StateTitle;
+       this.state = state;
+       keymap = state.keymap;
       }
 
-      public void First ()
+      public Context (MInputMethod im)
       {
-       index -= Column;
+       if (im.load_status != LoadStatus.Full
+           && ! im.Open ())
+         throw new Exception ("Openging " + im.tag + " failed");
+       this.im = im;
+       domain = new Xex.Domain ("context", im.domain, this);
+       state_initial_var_values = domain.SaveValues ();
+       reset ();
+       active = true;
+       if (PreeditChanged != null)
+         {
+           callback_arg.Set (MSymbol.mtext, preedit);
+           PreeditChanged (this, callback_arg);
+         }
+       if (StatusChanged != null)
+         {
+           callback_arg.Set (MSymbol.mtext, status);
+           StatusChanged (this, callback_arg);
+         }
       }
 
-      public void Last ()
+      public ChangedStatus Changed { get { return changed; } }
+
+      internal object GetCandidates (out int column)
       {
-       index += GroupLength - (Column + 1);
+       column = 0;
+       if (cursor_pos == 0)
+         return null;
+       Candidates candidates
+         = (Candidates) preedit.GetProp (cursor_pos - 1, Mcandidates);
+       if (candidates == null)
+         return null;
+       column = candidates.Column;
+       return candidates.Current;
       }
 
-      public void Select (int col)
+      private void save_state ()
       {
-       int maxcol = GroupLength - 1;
-       if (col > maxcol)
-         col = maxcol;
-       index = index - Column + col;
+       state_var_values = domain.SaveValues ();
+       state_preedit.Del ();
+       state_preedit.Ins (0, preedit);
+       state_key_head = key_head;
+       state_pos = cursor_pos;
       }
-    }
-
-    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,
-                       MInputMethod.Mcandidates, this);
-      cursor_pos = candidate_from;
-      preedit_changed = true;
-      cursor_pos_changed = true;
-      candidate_changed = true;
-    }
-
-    internal void insert_candidates (Xex.Term arg)
-    {
-      int column = 0;
-
-      if (domain.IsBound (Mcandidates_group_size))
-       {
-         object val = domain.GetValue (Mcandidates_group_size);
-         if (val is int)
-           column = (int) val;
-       }
-      candidates = new 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 void select (MSymbol sym)
-    {
-      if (candidates != null)
-       {
-         if (sym == Mat_less_than)
-           candidates.First ();
-         else if (sym == Mat_greater_than)
-           candidates.Last ();
-         else if (sym == Mat_minus)
-           candidates.Prev ();
-         else if (sym == Mat_plus)
-           candidates.Next ();
-         else if (sym == Mat_open_square_bracket)
-           candidates.PrevGroup ();
-         else if (sym == Mat_close_square_bracket)
-           candidates.NextGroup ();
-       }
-    }
-
-    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 (MInputMethod.Mcandidates, pos - 1,
-                                   out pos, out to);
-               }
-             else
-               pos = 0;
-             break;
-           case ']':
-             if (cursor_pos < preedit.Length - 1)
-               {
-                 int from;
-                 preedit.FindProp (MInputMethod.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 (MInputMethod.GetSurroundingText != null
-                 && MInputMethod.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 (MInputMethod.GetSurroundingText != null
-                 && MInputMethod.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);
-      preedit_changed = true;
-      cursor_pos_changed = true;
-    }
-
-    internal void show ()
-    {
-      candidate_show = true;
-      candidate_changed = true;
-    }
 
-    internal void hide ()
-    {
-      candidate_show = false;
-      candidate_changed = true;
-    }
-
-    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;
-         preedit_changed = true;
-       }
-    }
-
-    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.Count)
-           key_head = keys.Count;
-       }
-    }
+      private void restore_state ()
+      {
+       domain.RestoreValues (state_var_values);
+       preedit.Del ();
+       preedit.Ins (0, state_preedit);
+       set_cursor ("restore", state_pos);
+      }
 
-    internal void pushback (MInputMethod.KeySeq keyseq)
-    {
-      if (key_head > 0)
-       key_head--;
-      if (key_head < keys.Count)
-       keys.RemoveRange (key_head, keys.Count - key_head);
-      for (int i = 0; i < keyseq.Count; i++)
-       keys.Add (keyseq[i]);
-    }
+      private bool handle_key ()
+      {
+       Console.Write ("\nHandle ({0}[{1}]) in {2}",
+                      keys, key_head, state.name);
 
-    internal void pop ()
-    {
-      if (key_head < keys.Count)
-       keys.RemoveRange (key_head, 1);
-    }
+       Keymap sub = keymap.Lookup (keys, ref key_head);
 
-    internal void undo (int n)
-    {
-      if (n < 0)
-       keys.RemoveRange (keys.Count + n, - n);
-      else
-       keys.RemoveRange (n, keys.Count  - n);
-      reset ();
-    }
+       if (sub != keymap)
+         {
+           restore_state ();
+           keymap = sub;
+           if (keymap.map_actions != null)
+             {
+               if (! take_actions (keymap.map_actions))
+                 return false;
+             }
+           else if (keymap.submaps != null)
+             {
+               for (int i = state_key_head; i < key_head; i++)
+                 preedit_replace (cursor_pos, cursor_pos,
+                                  keys.keyseq[i].ToChar (), null);
+             }
+           if (keymap.submaps == null)
+             {
+               if (keymap.branch_actions != null)
+                 {
+                   if (! take_actions (keymap.branch_actions))
+                     return false;
+                 }
+               if (keymap != state.keymap)
+                 shift (state);
+             }
+         }
+       else
+         {
+           State current_state = state;
 
-    internal void commit ()
-    {
-      produced.Cat (preedit);
-      preedit.Del ();
-      preedit_changed = true;
-    }
+           if (keymap.branch_actions != null)
+             {
+               if (! take_actions (keymap.branch_actions))
+                 return false;
+             }
+           if (state == current_state)
+             {
+               if (state == im.initial_state
+                   && key_head < keys.keyseq.Count)
+                 return false;
+               if (keymap != state.keymap)
+                 shift (state);
+               else if (keymap.branch_actions == null)
+                 shift (im.initial_state);
+             }
+         }
+       return true;
+      }
 
-    internal void shift (MSymbol sym)
-    {
-      MInputMethod.State state;
+      public bool Toggle ()
+      {
+       active = ! active;
+       return active;
+      }
 
-      if (sym == MSymbol.t)
-       {
-         if (states.Count > 1)
-           state = states.Pop ();
-         else
-           state = states.Peek ();
-       }
-      else
-       {
-         state = (MInputMethod.State) im.states.Get (sym);
-         if (state == null)
-           throw new Exception ("Unknown state: " + state.name);
-       }
-      if (state == null)
-       state = states.Pop ();
-      if (state == (MInputMethod.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_boundary = domain.SetBoundary ();
-             status = state.title;
-             if (status == null)
-               status = im.title;
-             status_changed = true;
-             Xex on_entry
-               = (Xex) state.branches.Get (MSymbol.t);
-             if (on_entry != null)
-               on_entry.Eval (domain);
-           }
-       }
-    }
+      public bool UnhandledKey (out Key key)
+      {
+       key = unhandled_key;
+       return key_unhandled;
+      }
 
-    internal bool call (MSymbol module, MSymbol method, MPlist arglist)
-    {
-      MInputMethod.Plugin plugin;
+      public MText Preedit { get { return preedit; } }
+      public MText Produced { get { return produced; } }
 
-      if (! im.plugins.TryGetValue (module, out plugin))
-       return false;
-      if (plugin.assembly == null)
-       {
-         Assembly assembly;
+      // Return value:
+      //   true: All keys are handled and there's no text to commit.
+      //   false: Some key is left unhandled or there's a text to
+      //      commit.  The caller should refer to UnhandledKey and
+      //      Produced.
 
-         try {
-           assembly = Assembly.LoadFrom (module.Name + ".dll");
-         } catch {
+      public bool Filter (Key key)
+      {
+       if (! active)
+         {
+           key_unhandled = true;
+           unhandled_key = key;
            return false;
          }
-         Type t = assembly.GetType ("Plugin");
-         for (MPlist p = plugin.methods; ! p.IsEmpty; p = p.next)
-           p.Set (p.key, t.GetMethod (p.key.Name));
-       }
-
-      MethodInfo method_info = (MethodInfo) plugin.methods.Get (method);
-      if (method_info == null)
-       return false;
-      object[] method_arg = new object[1];
-      method_arg[0] = arglist;
-      bool result = (bool) method_info.Invoke (null, method_arg);
-      if (! result)
-       return false;
-      if (! arglist.IsEmpty)
-       (new Xex (arglist, domain)).Eval (domain);
-      return true;
-    }
-
-    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 ((MInputMethod.State) im.states.Val);
-      state_key_head = 0;
-      state_pos = 0;
-    }
+       if (key == Key.Reload)
+         return true;
+       changed = ChangedStatus.None;
+       produced.Del ();
+       preceding_text.Del ();
+       following_text.Del ();
+
+       key_unhandled = false;
+       keys.keyseq.Add (key);
+       int count = 0;
+       while (key_head < keys.keyseq.Count)
+         {
+           if (! handle_key ())
+             {
+               unhandled_key = keys.keyseq[key_head++];
+               key_unhandled = true;
+               break;
+             }
+           if (++count == 10)
+             break;
+         }
+       keys.keyseq.RemoveRange (0, key_head);
+       key_head = 0;
 
-    internal object GetCandidates (out int column)
-    {
-      column = 0;
-      if (cursor_pos == 0)
-       return null;
-      Candidates candidates
-       = (Candidates) preedit.GetProp (cursor_pos - 1,
-                                       MInputMethod.Mcandidates);
-      if (candidates == null)
-       return null;
-      column = candidates.Column;
-      return candidates.Current;
-    }
+       if ((changed & ChangedStatus.Preedit) != ChangedStatus.None
+           && PreeditChanged != null)
+         {
+           callback_arg.Set (MSymbol.mtext, preedit);
+           PreeditChanged (this, callback_arg);
+         }
+       if ((changed & ChangedStatus.StateTitle) != ChangedStatus.None
+           && StatusChanged != null)
+         {
+           callback_arg.Set (MSymbol.mtext, status);
+           StatusChanged (this, callback_arg);
+         }
+       if ((changed & ChangedStatus.Candidate) != ChangedStatus.None
+           && CandidateChanged != null)
+         {
+           CandidateChanged (this, callback_arg);
+         }
 
-    internal void HandleKey ()
-    {
-      MInputMethod.State state = states.Peek ();
+       Console.Write ("\nPreedit(\"{0}\"/{1}), Produced({2})",
+                      preedit, cursor_pos, produced);
 
-      
+       return (! key_unhandled && produced.Length == 0);
+      }
     }
   }
 }