*** empty log message ***
[m17n/m17n-lib-cs.git] / MInputMethod.cs
index ecee0a1..9e180d6 100644 (file)
@@ -41,24 +41,41 @@ namespace M17N.Input
     private static MSymbol Mmap_list = "map-list";
     private static MSymbol Mstate = "state";
     internal static MSymbol Mcandidates = "candidates";
-    private static MSymbol Minsert = "insert";
-    private static MSymbol Mdelete = "delete";
-    private static MSymbol Mmove = "move";
-    private static MSymbol Mmark = "mark";
-    private static MSymbol Mmarker = "marker";
-    private static MSymbol Mset = "set";
-    private static MSymbol Madd = "add";
-    private static MSymbol Msub = "sub";
-    private static MSymbol Mmul = "mul";
-    private static MSymbol Mdiv = "div";
-    private static MSymbol Mif = "if";
-    private static MSymbol Mcond = "cond";
-    private static MSymbol Mchar_at = "char-at";
-    private static MSymbol Msurrounding_flag = "surrounding-text-flag";
-    private static MSymbol Mpushback = "pushback"; 
-    private static MSymbol Mkeyseq = "keyseq"; 
-
-    private static Xex.Symbol Nprogn = "progn";
+
+
+    private static MSymbol Mget_surrounding_text = "get-surrounding-text";
+    private static MSymbol Mdel_surrounding_text = "del-surrounding-text";
+
+    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 Qat_minus_zero = "@-0";
+    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.Term Tnil = new Xex.Term ((Xex.Symbol) "nil");
     private static Xex.Term Tcatch_tag = new Xex.Term ((Xex.Symbol) "@mimtag");
 
@@ -122,6 +139,7 @@ namespace M17N.Input
        = new Dictionary<string, KeyModifier> ();
       private static uint keysym_base = 0x200000;
       private static uint char_mask = ~((uint) KeyModifier.All);
+      public static Key Reload;
 
       static Key ()
       {
@@ -144,6 +162,8 @@ namespace M17N.Input
        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)
@@ -257,6 +277,20 @@ namespace M17N.Input
        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)
@@ -277,31 +311,47 @@ namespace M17N.Input
                && ((m1 & KeyModifier.High) == (m2 & KeyModifier.High)));
       }
 
+      public int ToChar ()
+      {
+       return (int) (key & 0x1FFFFF);
+      }
+
       public override string ToString ()
       {
-       string str = Char.ToString ((char) key);
+       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)
-             str = "S-" + str;
+             mt.Ins (0, "S-");
            if ((m & KeyModifier.Control) != KeyModifier.None)
-             str = "C-" + str;
+             mt.Ins (0, "C-");
            if ((m & KeyModifier.Alt) != KeyModifier.None)
-             str = "A-" + str;
+             mt.Ins (0, "A-");
            if ((m & KeyModifier.AltGr) != KeyModifier.None)
-             str = "G-" + str;
+             mt.Ins (0, "G-");
            if ((m & KeyModifier.Super) != KeyModifier.None)
-             str = "s-" + str;
+             mt.Ins (0, "s-");
            if ((m & KeyModifier.Hyper) != KeyModifier.None)
-             str = "H-" + str;
+             mt.Ins (0, "H-");
          }
-       return str;
+       return (string) mt;
       }
     }
 
-    public class KeySeq : Xex.TermValue
+    internal class KeySeq : Xex.TermValue
     {
       public List<Key> keyseq = new List<Key> ();
 
@@ -361,20 +411,29 @@ namespace M17N.Input
 
       public override string ToString ()
       {
-       string str;
-
+       MText mt;
        foreach (Key key in keyseq)
-         if (key.HasModifier)
+         if (key.HasModifier || key.ToChar () < 0x20)
            {
-             str = "(keyseq";
+             mt = "(";
              foreach (Key k in keyseq)
-               str += " " + k.ToString ();
-             return str + ")";
+               {
+                 if (mt.Length > 1)
+                   mt.Cat (' ');
+                 mt.Cat (k.ToString ());
+               }
+             return (string) mt.Cat (")");
            }
-       str = "\"";
-       foreach (Key key in keyseq)             
-         str += key.ToString ();
-       return str + "\"";
+       mt = "\"";
+       foreach (Key k in keyseq)               
+         {
+           int c = k.ToChar ();
+
+           if (c == '\\' || c == '"')
+             mt.Cat ('\\');
+           mt.Cat (c);
+         }         
+       return (string) mt.Cat ("\"");
       }
     }
 
@@ -382,7 +441,7 @@ namespace M17N.Input
     {
       public MSymbol name;
       public MText description;
-      public List<KeySeq> keys;
+      internal List<KeySeq> keys;
 
       public Command (MPlist p)
       {
@@ -392,7 +451,6 @@ namespace M17N.Input
        if (description == null)
          description = "No description";
        keys = new List<KeySeq> ();
-       Console.WriteLine ("cmd:" + p);
        for (p = p.next; ! p.IsEmpty; p = p.next)
          {
            if (p.IsMText)
@@ -487,48 +545,59 @@ namespace M17N.Input
 
     internal abstract class Marker : Xex.TermValue
     {
-      private MSymbol name;
+      private Xex.Symbol name;
 
-      public Marker (MSymbol name)
+      private Marker (Xex.Symbol name)
        {
          this.name = name;
        }
 
       public abstract int Position (Context ic);
-      public abstract void Mark (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 Xex.TermValue Clone () { return this; }
 
       public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
       {
-       MSymbol name = node.InnerText;
-       
-       return Get ((Context) domain.context, name);
+       return Get ((Xex.Symbol) node.InnerText);
       }
 
       public class Named : Marker
       {
-       int pos;
-
-       public Named (MSymbol name) : this (name, 0) { }
-
-       private Named (MSymbol name, int p) : base (name) { pos = p; }
+       public Named (Xex.Symbol name) : base (name) { }
 
-       public override int Position (Context ic) { return pos; }
+       public override int Position (Context ic)
+       {
+         int pos;
 
-       public override void Mark (Context ic) { pos = ic.cursor_pos; } 
+         if (ic.marker_positions.TryGetValue (this, out pos))
+           return pos;
+         return 0;
+       }
 
-       public override Xex.TermValue Clone ()
+       public override void Mark (Context ic)
        {
-         return new Named (name, pos);
+         ic.marker_positions[this] = ic.cursor_pos;
        }
       }
      
       public class Predefined : Marker
       {
-       public Predefined (MSymbol name) : base (name) { }
+       char tag;
+       public Predefined (Xex.Symbol name) : base (name)
+         {
+           tag = ((string) name)[1];
+         }
        
        public override int Position (Context ic)
        {
-         switch (name.Name[1]) {
+         switch (tag) {
          case '<': return 0;
          case '>': return ic.preedit.Length;
          case '-': return ic.cursor_pos - 1;
@@ -552,49 +621,84 @@ namespace M17N.Input
              }
            return ic.preedit.Length;
          default:
-           return name.Name[1] - '0';
+           return tag - '0';
          }
        }
-      
-       public override void Mark (Context ic)
+      }
+
+      public class PredefinedAbsolute : Marker
+      {
+       private int pos;
+
+       public PredefinedAbsolute (Xex.Symbol 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)
        {
-         throw new Exception ("Can't set predefined marker: " + name);
+         return (pos < ic.preedit.Length ? pos : ic.preedit.Length);
        }
+      }
+
+      public class PredefinedSurround : Marker
+      {
+       private int distance;
+
+       public PredefinedSurround (Xex.Symbol 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 Xex.TermValue Clone ()
+       public override int Position (Context ic)
        {
-         return new Predefined (name);
+         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 internal Dictionary<Xex.Symbol,Predefined> predefined_markers;
 
       static Marker ()
       {
-       predefined_markers = new Dictionary<MSymbol,Predefined> ();
-       MSymbol[] symlist = new MSymbol[] {"@<", "@>", "@-", "@+", "@[", "@]",
-                                          "@0", "@1", "@2", "@3", "@4",
-                                          "@5", "@6", "@7", "@8", "@9" };
-       foreach (MSymbol s in symlist)
+       predefined_markers = new Dictionary<Xex.Symbol, Predefined> ();
+       Xex.Symbol[] symlist
+         = new Xex.Symbol[] {"@<", "@>", "@-", "@+", "@[", "@]" };
+       foreach (Xex.Symbol s in symlist)
          predefined_markers[s] = new Predefined (s);
       }
 
-      public static Marker Get (Context ic, MSymbol name)
+      public static Marker Get (Xex.Symbol name)
       {
-       Predefined pred;
-       Marker m;
-
-       if (predefined_markers.TryGetValue (name, out pred))
-         return pred;
-       if (name.Name[0] == '@')
-         throw new Exception ("Invalid marker name: " + name);
-       m = (Marker) ic.markers.Get (name);
-       if (m == null)
+       string str = name;
+       if (str[0] == '@')
          {
-           m = new Named (name);
-           ic.markers.Put (name, m);
+           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 == Qat_minus_zero
+               || ! (str[1] == '-' || str[1] == '+'))
+             throw new Exception ("Invalid marker name: " + name);
+           return new PredefinedSurround (name);
          }
-       return m;
+       return new Named (name);
       }
     }
       
@@ -852,117 +956,157 @@ namespace M17N.Input
       }
     }
 
-    internal abstract class Selector : Xex.TermValue
+    internal class Selector : Xex.TermValue
     {
-      private Selector () { }
+      static new Dictionary<MSymbol, Selector> selectors;
+
+      static Selector ()
+       {
+         selectors = new Dictionary<MSymbol, Selector> ();
+         MSymbol[] symlist = new MSymbol[] { "@<", "@=", "@>", "@-", "@+",
+                                             "@[", "@]" };
+         foreach (MSymbol s in symlist)
+           selectors[s] = new Selector (s);
+         selectors["@first"] = new Selector ('<');
+         selectors["@current"] = new Selector ('=');
+         selectors["@last"] = new Selector ('>');
+         selectors["@previous"] = new Selector ('-');
+         selectors["@next"] = new Selector ('+');
+         selectors["@previous-candidate-change"] = new Selector ('[');
+         selectors["@next-candidate-change"] = new Selector (']');
+       }
+
+      private char tag;
 
-      public abstract void Select (Candidates candidates);
+      private Selector (MSymbol sym) { tag = sym.Name[1]; }
+
+      private Selector (char tag) { this.tag = tag; }
 
       public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
       {
-       MSymbol name = node.InnerText;
-       Predefined pred;
+       return Get ((MSymbol) node.InnerText);
+      }
 
-       if (predefined_selectors.TryGetValue (name, out pred))
-         return pred;
-       if (name.Name[0] == '@')
-         throw new Exception ("Invalid selector name: " + name);
-       int index;
-       if (! Int32.TryParse (node.InnerText, out index))
+      public static Xex.TermValue Get (MSymbol name)
+      {
+       Selector selector;
+       if (! selectors.TryGetValue (name, out selector))
          throw new Exception ("Invalid selector name: " + name);
-       return new Numbered (index);
+       return selector;
       }
 
       public override Xex.TermValue Clone () { return this; }
 
-      public class Numbered : Selector
+      public void Select (Candidates candidates)
       {
-       int index;
+       switch (tag)
+         {
+         case '<': candidates.First (); break;
+         case '>': candidates.Last (); break;
+         case '-': candidates.Prev (); break;
+         case '+': candidates.Next (); break;
+         case '[': candidates.PrevGroup (); break;
+         case ']': candidates.NextGroup (); break;
+         default: break;
+         }
+      }
+    }
 
-       public Numbered (int index) { this.index = index; }
+    internal class Map
+    {
+      public MSymbol name;
+      public List<Entry> entries = new List<Entry> ();
 
-       public override void Select (Candidates can) { can.Select (index); }
-      }
+      public Map (MSymbol name) { this.name = name; }
 
-      public class Predefined : Selector
+      public class Entry
       {
-       private char tag;
+       public KeySeq keyseq;
+       public Xex.Term[] actions;
 
-       internal Predefined (MSymbol sym) { this.tag = sym.Name[1]; }
-
-       public override void Select (Candidates candidates)
+       public Entry (Xex.Domain domain, KeySeq keyseq, Xex.Term[] actions)
        {
-         switch (tag)
-           {
-           case '<': candidates.First (); break;
-           case '>': candidates.Last (); break;
-           case '-': candidates.Prev (); break;
-           case '+': candidates.Next (); break;
-           case '[': candidates.PrevGroup (); break;
-           case ']': candidates.NextGroup (); break;
-           default: break;
-           }
+         this.keyseq = keyseq;
+         this.actions = actions;
        }
       }
 
-      static new Dictionary<MSymbol, Predefined> predefined_selectors;
-
-      static Selector ()
-       {
-         predefined_selectors = new Dictionary<MSymbol, Predefined> ();
-         MSymbol[] symlist = new MSymbol[] { "@<", "@=", "@>", "@-", "@+",
-                                             "@[", "@]" };
-         foreach (MSymbol s in symlist)
-           predefined_selectors[s] = new Predefined (s);
-       }
+      public override string ToString ()
+      {
+       string str = "(" + name;
+       foreach (Entry e in entries)
+         str += " " + e.keyseq.ToString ();
+       return str + ")";
+      }
     }
 
-    internal class Map
+    internal class Keymap
     {
-      public MSymbol name;
-      public Dictionary<Key, Map> submaps;
-      public Xex.Term actions;
+      public Dictionary<Key, Keymap> submaps;
+      public Xex.Term[] map_actions, branch_actions;
 
-      public void Add (KeySeq keys, int index, Xex.Term actions)
-      {
-       Map sub = null;
+      public Keymap () { }
 
-       if (submaps == null)
-         submaps = new Dictionary<Key, Map> ();
+      public void Add (KeySeq keys, int index, 
+                      Xex.Term[] map_actions, Xex.Term[] branch_actions)
+      {
+       if (index == keys.keyseq.Count)
+         {
+           this.map_actions = map_actions;
+           this.branch_actions = branch_actions;
+         }
        else
-         submaps.TryGetValue (keys.keyseq[index], out sub);
-       if (sub == null)
          {
            Key key = keys.keyseq[index];
-           submaps[key] = sub = new Map ();
+           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);
          }
-       if (index + 1 < keys.keyseq.Count)
-         sub.Add (keys, index + 1, actions);
-       else
-         this.actions = actions;
       }
 
-      public Xex.Term Lookup (KeySeq keys, int index)
+      public void AddMap (Map map, Xex.Term[] branch_actions)
+      {
+       foreach (Map.Entry entry in map.entries)
+         Add (entry.keyseq, 0, entry.actions, branch_actions);
+      }
+
+      public Keymap Lookup (KeySeq keys, ref int index)
       {
-       Map sub;
+       Keymap sub;
 
-       if (index + 1 == keys.keyseq.Count)
-         return actions;
-       if (submaps.TryGetValue (keys.keyseq[index], out sub))
-         return sub.Lookup (keys, index + 1);
-       return Tnil;
+       if (index < keys.keyseq.Count
+           && submaps != null
+           && submaps.TryGetValue (keys.keyseq[index], out sub))
+         {
+           index++;
+           return sub.Lookup (keys, ref index);
+         }
+       return this;
       }
 
       private void describe (MText mt, KeySeq keyseq)
       {
-       if (keyseq.keyseq.Count > 0)
+       if (map_actions != null || branch_actions != null)
          {
-           mt.Cat (" (").Cat (keyseq.ToString ());
-           mt.Cat (' ').Cat (actions.ToString ());
+           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, Map> kv in submaps)
+         foreach (KeyValuePair<Key, Keymap> kv in submaps)
            {
              keyseq.keyseq.Add (kv.Key);
              kv.Value.describe (mt, keyseq);
@@ -972,7 +1116,7 @@ namespace M17N.Input
 
       public override string ToString ()
       {
-       MText mt = "(" + name.Name;
+       MText mt = "";
        KeySeq keyseq = new KeySeq ();
 
        describe (mt, keyseq);
@@ -983,23 +1127,85 @@ namespace M17N.Input
 
     internal class State
     {
-      public MSymbol name;
+      public Xex.Symbol name;
       public MText title;
-      public MPlist branches = new MPlist ();
+      public Xex.Term[] enter_actions, fallback_actions;
+      public Keymap keymap = new Keymap ();
 
-      public State (MSymbol name)
+      public State (Xex.Symbol name, MText title)
       {
        this.name = name;
+       this.title = title;
+      }
+
+      public State (MInputMethod im, XmlNode node)
+      {
+       this.name = node.Attributes[Qsname].Value;
+       XmlAttribute attr = node.Attributes[Qtitle];
+       if (attr != null)
+         title = (MText) attr.Value;
+       keymap = new Keymap ();
+       for (node = node.FirstChild; node != null; node = node.NextSibling)
+         {
+           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);
+             }
+         }
+      }
+
+      public State (MInputMethod im, MPlist plist)
+      {
+       if (! plist.IsSymbol)
+         throw new Exception ("Invalid state: " + plist);
+       this.name = plist.Symbol.Name;
+       plist = plist.next;
+       if (plist.IsMText)
+         {
+           this.title = plist.Text;
+           plist = plist.next;
+         }
+       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);
+             }
+           }
       }
 
       public override string ToString ()
       {
-       MText mt = "(" + name.Name;
+       MText mt = "(" + name;
 
        if (title != null)
          mt.Cat (" \"" + title + "\"");
-       for (MPlist p = branches; ! p.IsEmpty; p = p.next)
-         mt.Cat (" (" + p.Key + " " + (Xex) p.Val + ")");
+       mt.Cat (keymap.ToString ());
        return (string) mt + ")";
       }
     }
@@ -1017,7 +1223,8 @@ namespace M17N.Input
     internal Xex.Symbol[] var_names;
     internal Dictionary<MSymbol, Plugin> plugins;
     internal Dictionary<MSymbol, Map> maps;
-    internal MPlist states;
+    internal Dictionary<Xex.Symbol, State> states;
+    internal State initial_state;
 
     static MInputMethod ()
     {
@@ -1026,7 +1233,7 @@ namespace M17N.Input
       im_domain.DefTerm ("selector", Selector.parser);
 
       im_domain.DefSubr (Finsert, "insert", false, 1, 1);
-      im_domain.DefSubr (Finsert_candidates, "candidates", 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);
@@ -1039,7 +1246,7 @@ namespace M17N.Input
       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 (Fshift_back, "shiftback", false, 0, 0);
+      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",
@@ -1168,10 +1375,19 @@ namespace M17N.Input
       return true;
     }
 
+    private void add_default_state ()
+    {
+      Xex.Symbol Qinit = "init";
+      State state = new State (Qinit, null);
+      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)
@@ -1216,14 +1432,8 @@ namespace M17N.Input
        commands = new Command[0];
       if (! full)
        return;
-      if (states.IsEmpty)
-       {
-         State state = new State ((MSymbol) "init");
-         plist = new MPlist ();
-         foreach (KeyValuePair<MSymbol, Map>kv in maps)
-           state.branches.Add (kv.Key, null);
-         states.Add (state.name, state);
-       }
+      if (states.Count == 0)
+       add_default_state ();
     }
 
     private void load (XmlNode node, bool full)
@@ -1231,7 +1441,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;
@@ -1272,133 +1482,8 @@ namespace M17N.Input
        commands = new Command[0];
       if (! full)
        return;
-      if (states.IsEmpty)
-       {
-         State state = new State ((MSymbol) "init");
-         foreach (KeyValuePair<MSymbol, Map>kv in maps)
-           state.branches.Add (kv.Key, null);
-         states.Add (state.name, state);
-       }
-    }
-
-    private static void transform (MPlist plist)
-    {
-      for (; ! plist.IsEmpty; plist = plist.next)
-       {
-         if (plist.IsMText)
-           {
-             MPlist p = new MPlist ();
-             p.Add (MSymbol.symbol, Minsert);
-             p.Add (MSymbol.mtext, plist.Text);
-             plist.Set (MSymbol.plist, p);
-           }
-         else if (plist.IsInteger)
-           {
-             MPlist p = new MPlist ();
-             p.Add (MSymbol.symbol, Minsert);
-             p.Add (MSymbol.integer, plist.Integer);
-             plist.Set (MSymbol.plist, p);
-           }
-         else if (plist.IsPlist)
-           {
-             MPlist pl = plist.Plist;
-
-             if (pl.IsSymbol)
-               {
-                 if (pl.Symbol == Madd)
-                   pl.Set (MSymbol.symbol, (MSymbol) "+=");
-                 else if (pl.Symbol == Msub)
-                   pl.Set (MSymbol.symbol, (MSymbol) "-=");
-                 else if (pl.Symbol == Mmul)
-                   pl.Set (MSymbol.symbol, (MSymbol) "*=");
-                 else if (pl.Symbol == Mdiv)
-                   pl.Set (MSymbol.symbol, (MSymbol) "/=");
-                 else if (pl.Symbol == Minsert)
-                   {
-                     // (insert (CANDIDATES ...))
-                     //   => (candidates CANDIDATES ...)
-                     if (pl.next.IsPlist)
-                       {
-                         pl.Set (MSymbol.symbol, Mcandidates);
-                         pl = pl.next;
-                         MPlist p = pl.Plist;
-                         pl.Set (p.key, p.val);
-                         for (p = p.next; ! p.IsEmpty; p = p.next);
-                         pl.Add (p.key, p.val);
-                       }
-                   }
-                 else if (pl.Symbol == Mif)
-                   {
-                     pl = pl.next;
-                     if (! pl.IsEmpty)
-                       transform (pl.next);
-                   }
-                 else if (pl.Symbol == Mcond)
-                   {
-                     for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
-                       if (pl.IsPlist)
-                         {
-                           MPlist p = pl.Plist;
-
-                           if (p.IsPlist)
-                             transform (p);
-                           else
-                             transform (p.next);
-                         }
-                   }
-                 else if (pl.Symbol == Mdelete
-                          || pl.Symbol == Mmove
-                          || pl.Symbol == Mmark)
-                   {
-                     pl = pl.next;
-                     if (pl.IsSymbol)
-                       {
-                         MSymbol sym = pl.Symbol;
-                         MPlist p = new MPlist ();
-                         p.Add (MSymbol.symbol, Mmarker);
-                         p.Add (MSymbol.symbol, sym);
-                         pl.Set (MSymbol.plist, p);
-                       }
-                   }
-                 else if (pl.Symbol == Mpushback)
-                   {
-                     pl = pl.next;
-                     if (pl.IsPlist)
-                       pl.Plist.Push (MSymbol.symbol, Mkeyseq);
-                   }
-               }
-             else if (pl.IsMText)
-               {
-                 // (CANDIDATES ...) => (candidates CANDIDATES ...)
-                 pl.Push (MSymbol.symbol, Mcandidates);
-               }
-           }
-         else if (plist.IsSymbol)
-           {
-             MSymbol sym = plist.Symbol;
-
-             if (sym.Name.Length >= 3
-                 && sym.Name[0] == '@'
-                 && (sym.Name[1] == '-' || sym.Name[1] == '+'))
-               {
-                 int pos = int.Parse (sym.Name.Substring (1));
-                 MPlist p = new MPlist ();
-
-                 if (pos == 0)
-                   {
-                     p.Add (MSymbol.symbol, Msurrounding_flag);
-                   }
-                 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)
@@ -1436,8 +1521,9 @@ namespace M17N.Input
        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)
                {
@@ -1653,10 +1739,43 @@ namespace M17N.Input
 
     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;     
+         }
     }
 
     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;
+         }
     }
 
     private void parse_include (MPlist plist)
@@ -1717,14 +1836,15 @@ 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
            {
-             object state = im.states.Get (target_name);
-             if (state != null)
-               states.Add (target_name, state);
+             Xex.Symbol state_name = target_name.Name;
+             State state;
+             if (im.states.TryGetValue (state_name, out state))
+               states[state_name] = state;
            }
        }
     }
@@ -1738,52 +1858,161 @@ namespace M17N.Input
          if (! plist.IsPlist)
            throw new Exception ("Invalid cond args: " + plist);
          MPlist p = plist.Plist;
-         List<Xex.Term> arg = new List<Xex.Term> (parse_action (p));
+         List<Xex.Term> arg = new List<Xex.Term> (parse_actions (p, false));
          args[i] = new Xex.Term (arg);
        }
-      return new Xex.Term (domain, (Xex.Symbol) Mcond.Name, args);
+      return new Xex.Term (domain, Qcond, args);
     }
 
-    private Xex.Term[] parse_action (MPlist plist)
+    private Xex.Term parse_insert (MPlist plist)
     {
-      Xex.Term[] terms = new Xex.Term[plist.Count];
-
-      for (int i = 0; ! plist.IsEmpty; i++, plist = plist.next)
+      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)
        {
-         if (plist.IsSymbol)
-           terms[i] = new Xex.Term ((Xex.Symbol) plist.Symbol.Name);
-         else if (plist.IsMText)
-           terms[i] = new Xex.Term ((string) plist.Text);
-         else if (plist.IsInteger)
-           terms[i] = new Xex.Term (plist.Integer);
-         else if (plist.IsPlist)
+         MPlist pl = plist.Plist;
+
+         args = new Xex.Term[pl.Count];
+         int i;
+         for (i = 0; ! pl.IsEmpty; i++, pl = pl.next)
            {
-             MPlist p = plist.Plist;
-             if (! p.IsSymbol)
-               throw new Exception ("Invalid action: " + p);
-             MSymbol name = p.Symbol;
-             p = p.next;
-             if (name == Mcond)
-               terms[i] = parse_cond (p);
-             else if (name == Mset || name == Madd || name == Msub
-                      || name == Mmul || name == Mdiv)
+             if (pl.IsMText)
+               args[i] = new Xex.Term ((string) pl.Text);
+             else if (pl.IsPlist)
                {
-                 if (! p.IsSymbol)
-                   throw new Exception ("Invalid action: " + p);
-                 Xex.Symbol varname = p.Symbol.Name;
-                 terms[i] = new Xex.Term (domain, (Xex.Symbol) name.Name,
-                                          varname, parse_action (p.next));
+                 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
-               terms[i] = new Xex.Term (domain, (Xex.Symbol) name.Name,
-                                        parse_action (p));
+               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 ((Xex.Symbol) plist.Symbol.Name));
+      else
+       throw new Exception ("Invalid arg to " + func + ": " + plist);
+      return new Xex.Term (domain, func, args);
+    }
+
+    private Xex.Term parse_char_at (Xex.Symbol 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);
+         Xex.Symbol name = p.Symbol.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 (name);
+         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
-           throw new Exception ("Invalid action: " + plist);
+           {
+             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));
+           }
        }
-      return terms;
+      else if (plist.IsSymbol)
+       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)
     {
@@ -1803,9 +2032,8 @@ namespace M17N.Input
 
            if (! p.IsSymbol)
              continue;
-           transform (p.next);
            domain.Defun ((Xex.Symbol) p.Symbol.Name, false, null,
-                         parse_action (p.next), false);
+                         parse_actions (p.next, false), false);
          }
     }
 
@@ -1818,9 +2046,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)
@@ -1834,46 +2061,9 @@ namespace M17N.Input
                else
                  continue;
                p = p.next;
-               if (p.IsEmpty)
-                 continue;
-               map.Add (keys, 0,
-                        new Xex.Term (domain, Nprogn, parse_action (p)));
-             }
-         }
-    }
-
-    private void parse_states (MPlist plist)
-    {
-      for (; ! plist.IsEmpty; plist = plist.next)
-       if (plist.IsPlist)
-         {
-           MPlist pl = plist.Plist;
-           MText title = null;
-         
-           if (pl.IsMText)
-             {
-               title = pl.Text;
-               pl = pl.next;
-             }
-           if (! pl.IsSymbol)
-             continue;
-
-           State state = new State (pl.Symbol);
-           state.title = title;
-           if (states == null)
-             states = new MPlist ();
-           states.Add (state.name, state);
-           for (pl = pl.next; ! pl.IsEmpty; pl = pl.next)
-             {
-               if (! pl.IsPlist)
-                 continue;
-               MPlist p = pl.Plist;
-               if (! p.IsSymbol)
-                 continue;
-               MSymbol map_name = p.Symbol;
-               p = p.next;
-               Xex.Term term = new Xex.Term (domain, Nprogn, parse_action (p));
-               state.branches.Add (map_name, term);
+               Xex.Term[] actions
+                 = p.IsEmpty ? null : parse_actions (p, false);
+               map.entries.Add (new Map.Entry (domain, keys, actions));
              }
          }
     }
@@ -2003,14 +2193,19 @@ namespace M17N.Input
     private static Xex.Term Fshift (Xex.Domain domain, Xex.Variable vari,
                                    Xex.Term[] args)
     {
-      ((Context) 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 Fshift_back (Xex.Domain domain, Xex.Variable vari,
-                                        Xex.Term[] args)
+    private static Xex.Term Fshiftback (Xex.Domain domain, Xex.Variable vari,
+                                       Xex.Term[] args)
     {
-      ((Context) domain.context).shift_back ();
+      ((Context) domain.context).shift (null);
       return Tnil;
     }
 
@@ -2024,7 +2219,7 @@ namespace M17N.Input
                                               Xex.Variable vari,
                                               Xex.Term[] args)
     {
-      return new Xex.Term (((Context) domain.context).surrounding_flag);
+      return new Xex.Term (((Context) domain.context).SurroundingFlag);
     }
 
     public override string ToString ()
@@ -2055,53 +2250,117 @@ 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 Context
     {
-      internal static Xex.Symbol Ncandidates_group_size
+      internal static Xex.Symbol Qcandidates_group_size
         = "candidates-group-size";
-      public MInputMethod im;
+      internal MInputMethod im;
+      private Dictionary<MSymbol, Callback> callbacks
+       = new Dictionary<MSymbol, Callback> ();
+
       private MText produced;
       private bool active;
       private MText status;
       internal MText preedit;
       internal int cursor_pos;
+      internal Dictionary<Marker, int> marker_positions
+       = new Dictionary<Marker, int> ();
       internal Candidates candidates;
       private int candidate_from, candidate_to;
       private bool candidate_show;
+      public bool CandidateShow { get { return candidate_show; } }
+
+      private List<State> state_list = new List<State> ();
+      private Keymap keymap;
 
-      private Stack<State> state_stack;
-      internal KeySeq keys;
+      // Sequence of input keys.
+      internal KeySeq keys = new KeySeq ();
+
+      // Index into KEYS specifying the next key to handle.
       internal int key_head;
+
+
       private int state_key_head;
       private object state_var_values;
       private int commit_key_head;
       private MText state_preedit;
       private int state_pos;
-      internal MPlist markers = new MPlist ();
       internal MText preceding_text = new MText ();
       internal MText following_text = new MText ();
+
+      // Set to false before calling the method 'handle_key', and set
+      // to true when some key is unhandled.
       private bool key_unhandled;
 
+      // The unhandled key.  It has the meaning only when
+      // 'key_unhandled' is true.
+      private Key unhandled_key;
+
       internal Xex.Domain domain;
 
       internal ChangedStatus changed;
 
-      public ChangedStatus Changed { get { return changed; } }
+      static MPlist callback_arg = new MPlist ();
 
-      public Context (MInputMethod im)
+      static Xex.Term[] catch_args = new Xex.Term[2];
+
+      private bool take_action (Xex.Term[] actions)
       {
-       this.im = im;
-       domain = new Xex.Domain (im.domain, this);
-       state_stack = new Stack<State> ();
-       state_stack.Push ((State) im.states.val);
-       keys = new KeySeq ();
+       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);
+      }
+
+
+      private bool call_callback (MSymbol name, MPlist arg)
+      {
+       Callback callback;
+       if (! callbacks.TryGetValue (name, out callback))
+         return false;
+       return callback (this, arg);
       }
 
+      private bool get_surrounding_text (int len)
+      {
+       if (len < 0 ? -len <= preceding_text.Length
+           : len <= following_text.Length)
+         return true;
+       callback_arg.Set (MSymbol.integer, len);
+       if (! call_callback (Mget_surrounding_text, 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);
+      }
+
+      internal int SurroundingFlag
+      {
+       get { return (callbacks.ContainsKey (Mget_surrounding_text) ? 1 : 0); }
+      }
+
+      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, object inserted)
       {
        int ins = (inserted == null ? 0
@@ -2109,15 +2368,15 @@ namespace M17N.Input
                   : ((MText) inserted).Length);
        int diff = ins - (to - from);
 
-       for (MPlist plist = markers; ! plist.IsEmpty; plist = plist.next)
+       foreach (Marker m in marker_positions.Keys)
          {
-           int pos = plist.Integer;
+           int pos = marker_positions[m];
            if (pos > from)
              {
                if (pos >= to)
-                 plist.val = pos + diff;
+                 marker_positions[m] = pos + diff;
                else
-                 plist.val = from;
+                 marker_positions[m] = from;
              }
          }
        if (cursor_pos >= to)
@@ -2172,7 +2431,7 @@ namespace M17N.Input
       internal void insert_candidates (Xex.Term arg)
       {
        int column = 0;
-       Xex.Variable v = domain.GetVar (Ncandidates_group_size, false);
+       Xex.Variable v = domain.GetVar (Qcandidates_group_size, false);
 
        if (v != null)
          column = v.Value.Intval;
@@ -2190,58 +2449,6 @@ namespace M17N.Input
          }
       }
 
-      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 (Mcandidates, pos - 1,
-                                     out pos, out to);
-                 }
-               else
-                 pos = 0;
-               break;
-             case ']':
-               if (cursor_pos < preedit.Length - 1)
-                 {
-                   int from;
-                   preedit.FindProp (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;
@@ -2316,16 +2523,6 @@ namespace M17N.Input
          }
       }
 
-      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)
@@ -2376,26 +2573,24 @@ namespace M17N.Input
        changed |= ChangedStatus.Preedit;
       }
 
-      internal void shift (MSymbol sym)
+      internal void shift (State state)
       {
-       State state;
+       bool changed;
 
-       if (sym == MSymbol.t)
+       if (state == null)
          {
-           if (state_stack.Count > 1)
-             state = state_stack.Pop ();
-           else
-             state = state_stack.Peek ();
+           if (state_list.Count > 1)
+             state_list.RemoveAt (state_list.Count - 1);
+           state = state_list[state_list.Count - 1];
+           changed = true;
          }
        else
          {
-           state = (State) im.states.Get (sym);
-           if (state == null)
-             throw new Exception ("Unknown state: " + state.name);
+           changed = state != state_list[state_list.Count - 1];
+           if (changed)
+             state_list.Add (state);
          }
-       if (state == null)
-         state = state_stack.Pop ();
-       if (state == (State) im.states.val)
+       if (state_list.Count == 1)
          {
            commit ();
            reset ();
@@ -2405,18 +2600,15 @@ namespace M17N.Input
            state_key_head = key_head;
            state_pos = cursor_pos;
            state_preedit = preedit.Dup ();
-           if (state != state_stack.Peek ())
+           state_var_values = domain.SaveValues ();
+           if (changed)
              {
-               state_stack.Push (state);
-               state_var_values = domain.SaveValues ();
                status = state.title;
                if (status == null)
                  status = im.title;
-               changed |= ChangedStatus.StateTitle;
-               Xex on_entry
-                 = (Xex) state.branches.Get (MSymbol.t);
-               if (on_entry != null)
-                 on_entry.Eval (domain);
+               this.changed |= ChangedStatus.StateTitle;
+               if (state.enter_actions != null)
+                 take_action (state.enter_actions);
              }
          }
       }
@@ -2426,15 +2618,31 @@ namespace M17N.Input
        preedit.Del ();
        state_preedit.Del ();
        produced.Del ();
-       markers.Clear ();
+       marker_positions.Clear ();
        cursor_pos = 0;
+       keys.keyseq.Clear ();
        key_head = commit_key_head = 0;
-       state_stack.Clear ();
-       state_stack.Push ((State) im.states.Val);
-       state_key_head = 0;
+       state_list.Clear ();
+       state_list.Add (im.initial_state);
+       keymap = im.initial_state.keymap;
+       key_head = state_key_head = 0;
        state_pos = 0;
       }
 
+      public Context (MInputMethod im)
+      {
+       this.im = im;
+       domain = new Xex.Domain (im.domain, this);
+       reset ();
+      }
+
+      public ChangedStatus Changed { get { return changed; } }
+
+      public void AddCallback (MSymbol name, Callback callback)
+      {
+       callbacks[name] = callback;
+      }
+
       internal object GetCandidates (out int column)
       {
        column = 0;
@@ -2448,15 +2656,111 @@ namespace M17N.Input
        return candidates.Current;
       }
 
-      internal void HandleKey ()
+      private void restore_state ()
       {
       }
 
+      private bool handle_key ()
+      {
+       State state = state_list[state_list.Count - 1];
+       Keymap sub = keymap.Lookup (keys, ref key_head);
+
+       if (sub != keymap)
+         {
+           keymap = sub;
+           if (keymap.map_actions != null)
+             {
+               restore_state ();
+               if (! take_action (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 ());
+             }
+           if (keymap.submaps == null)
+             {
+               if (keymap.branch_actions != null)
+                 {
+                   if (! take_action (keymap.branch_actions))
+                     return false;
+                 }
+               if (keymap != state.keymap)
+                 shift (state);
+             }
+         }
+       else
+         {
+           if (keymap.branch_actions != null)
+             {
+               if (! take_action (keymap.branch_actions))
+                 return false;
+             }
+           if (state == state_list[state_list.Count - 1])
+             {
+               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;
+      }
+
       public bool Toggle ()
       {
        active = ! active;
        return active;
       }
+
+      public bool UnhandledKey (out Key key)
+      {
+       key = unhandled_key;
+       return key_unhandled;
+      }
+
+      public bool Produced (out MText mt)
+      {
+       mt = produced;
+       return (produced.Length > 0);
+      }
+
+      // Return value:
+      //   true: All keys are handled and there's no text to commit.
+      //   false: Some key is unhandled or there's a text to commit.
+      //      The caller should use methods UnhandledKey and Produced.
+
+      public bool Filter (Key key)
+      {
+       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 == 100)
+             break;
+         }
+       keys.keyseq.RemoveRange (0, key_head);
+       return (! key_unhandled && produced.Length == 0);
+      }
     }
   }
 }