*** empty log message ***
[m17n/m17n-lib-cs.git] / MInputMethod.cs
index 3f80c2f..ebb8277 100644 (file)
@@ -9,7 +9,7 @@ using M17N;
 using M17N.Core;
 using M17N.Input;
 
-using Xex = System.Xml.Expression.Xexpression;
+using Xex = System.Xml.Xexpression;
 
 namespace M17N.Input
 {
@@ -18,14 +18,8 @@ namespace M17N.Input
     // Delegaes
     public delegate bool Callback (Context ic, MPlist args);
 
-    // Class members
-    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";
@@ -38,7 +32,10 @@ namespace M17N.Input
     private static MSymbol Mmap_list = "map-list";
     private static MSymbol Mstate = "state";
     internal static MSymbol Mcandidates = "candidates";
+    private static MSymbol Mat_minus_zero = "@-0";
+    private static MSymbol Matat = "@@";
 
+    private static Xex.Symbol Qxi_include = "xi:include";
     private static Xex.Symbol Qmap = "map";
     private static Xex.Symbol Qrule = "rule";
     private static Xex.Symbol Qkeyseq = "keyseq";
@@ -47,7 +44,6 @@ namespace M17N.Input
     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";
@@ -68,6 +64,12 @@ namespace M17N.Input
     private static Xex.Symbol Qtitle = "title";
     private static Xex.Symbol Qeq = "=";
     private static Xex.Symbol Qeqeq = "==";
+    private static Xex.Symbol Qhide = "hide";
+    private static Xex.Symbol Qhide_candidates = "hide-candidates";
+    private static Xex.Symbol Qshow = "show";
+    private static Xex.Symbol Qshow_candidates = "show-candidates";
+    private static Xex.Symbol Qkey_count = "key-count";
+    private static Xex.Symbol Qsurrounding_text_flag = "surrounding-text-flag";
     private static Xex.Symbol Qcandidates_group_size = "candidates-group-size";
 
     private static Xex.Term Tnil = new Xex.Term ((Xex.Symbol) "nil");
@@ -105,880 +107,951 @@ namespace M17N.Input
     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 Xex.Symbol name;
-
-      private Marker (Xex.Symbol 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 Xex.TermValue Clone () { return this; }
-
-      public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
-      {
-       return Get ((Xex.Symbol) node.InnerText);
-      }
-
-      public class Named : Marker
-      {
-       public Named (Xex.Symbol name) : base (name) { }
-
-       public override int Position (Context ic)
-       {
-         int pos;
-
-         if (ic.marker_positions.TryGetValue (this, out pos))
-           return pos;
-         return 0;
-       }
-
-       public override void Mark (Context ic)
-       {
-         ic.marker_positions[this] = ic.cursor_pos;
-       }
-      }
-     
-      public class Predefined : Marker
-      {
-       char tag;
-       public Predefined (Xex.Symbol 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 (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)
-       {
-         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 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<Xex.Symbol,Predefined> predefined_markers;
-
-      static Marker ()
-      {
-       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 (Xex.Symbol name)
-      {
-       string str = 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 == Qat_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);
-      }
-
-      // Fill the array "group" by candidates stating from INDEX.
-      // INDEX must be a multiple of "column".  Set NTIMES to the
-      // number of valid candidates in "group".  Update "block" if
-      // necessary.  Return "group".
-
-      private int fill_group (int start)
-      {
-       int nitems = group.Length;
-       int r = row;
-       Block b = blocks[r];
-
-       if (start < b.Index)
-         while (start < b.Index)
-            b = blocks[--r];
-       else
-         while (start >= b.Index + b.Count)
-           b = blocks[++r];
-       row = r;
-
-       int count = b.Count;
-       start -= b.Index;
-       for (int i = 0; i < nitems; i++, start++)
-         {
-           if (start >= count)
-             {
-               r++;
-               if (r == blocks.Length)
-                 return i;
-               b = blocks[r];
-               count = b.Count;
-               start = 0;
-             }
-           group[i] = b[start];
-         }
-       return nitems;
-      }
-
-      // Update "row" to what contains the first candidate of
-      // the previous candidate-group, update "current_index", and
-      // update "group" if necessary.  Return the previous
-      // candidate-group.  Set NITEMS to the number of valid
-      // candidates contained in that group.
-
-      public int PrevGroup ()
-      {
-       int nitems;
-       int col = Column;
-
-       if (IsFixed)
-         {
-           nitems = group.Length;
-           if ((index -= col + nitems) < 0)
-             index = (Total / nitems) * nitems;
-           nitems = fill_group (index);
-         }
-       else
-         {
-           row = row > 0 ? row-- : blocks.Length - 1;
-           nitems = blocks[row].Count;
-           index = blocks[row].Index;
-         }
-       index += col < nitems ? col : nitems - 1;
-       return nitems;
-      }
-
-      public int NextGroup ()
-      {
-       int nitems;
-       int col = Column;
-
-       if (IsFixed)
-         {
-           nitems = group.Length;
-           if ((index += nitems - col) >= Total)
-             index = 0;
-           nitems = fill_group (index);
-         }
-       else
-         {
-           row = row < blocks.Length - 1 ? row + 1 : 0;
-           nitems = blocks[row].Count;
-           index = blocks[row].Count;
-         }
-       index += col < nitems ? col : nitems - 1;
-       return nitems;
-      }
-
-      public void Prev ()
-      {
-       int col = Column;
-
-       if (col == 0)
-         {
-           int nitems = PrevGroup ();
-           index += col < nitems - 1 ? col : nitems - 1;
-         }
-       else
-         index--;
-      }
-
-      public void Next ()
-      {
-       int col = Column;
-       int nitems = GroupLength;
-
-       if (col == nitems - 1)
-         {
-           nitems = NextGroup ();
-           index -= Column;
-         }
-       else
-         index++;
-      }
-
-      public void First ()
-      {
-       index -= Column;
-      }
-
-      public void Last ()
-      {
-       index += GroupLength - (Column + 1);
-      }
-
-      public void Select (int col)
-      {
-       int maxcol = GroupLength - 1;
-       if (col > maxcol)
-         col = maxcol;
-       index = index - Column + col;
-      }
-    }
+     [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 readonly Key Reload;
+
+       static Key ()
+       {
+        keysyms["null"] = 0x00;
+        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 ((MSymbol) "-reload");
+       }
+
+       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 (key & 0x3FFFFF) <= 0x1FFFFF ? (int) (key & 0x1FFFFF) : -1;
+       }
+
+       public override string ToString ()
+       {
+        int c = ToChar ();
+        MText mt = null;
+        if (c < 0x20)
+          foreach (KeyValuePair<string, uint> kv in keysyms)
+            if ((key & 0x3FFFFF) == 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
+     {
+       private static Xex.Symbol name = "keyseq";
+       public static Xex.Symbol Name { get { return name; } }
+
+       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 bool Equals (object obj)
+       {
+        KeySeq ks = obj as KeySeq;
+        if (ks == null || ks.keyseq.Count != keyseq.Count)
+          return false;
+        for (int i = 0; i < keyseq.Count; i++)
+          if (keyseq[i] != ks.keyseq[i])
+            return false;
+        return true;
+       }
+
+       public override int GetHashCode ()
+       {
+        int code = 0;
+        for (int i = 0; i < keyseq.Count; i++)
+          code ^= keyseq[i].GetHashCode ();
+        return code;
+       }
+
+       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 static Xex.Symbol name = "marker";
+       public static Xex.Symbol Name { get { return name; } }
+
+       private MSymbol mname;
+
+       private Marker (MSymbol mname)
+        {
+          this.mname = mname;
+        }
+
+       public abstract int Position (Context ic);
+
+       public virtual void Mark (Context ic)
+        {
+          throw new Exception ("Can't set predefined marker: " + mname);
+        }
+       public virtual int CharAt (Context ic)
+        {
+          int pos = Position (ic);
+
+          return ((pos >= 0 && pos < ic.preedit.Length) ? ic.preedit[pos]
+                  : -1);
+        }
+
+       public override string ToString ()
+       {
+        return "<marker>" + mname + "</marker>";
+       }
+
+       public static Xex.TermValue Parser (Xex.Domain domain, XmlNode node)
+       {
+        return Get ((MSymbol) node.InnerText);
+       }
+
+       
+       public override bool Equals (object obj)
+       {
+        Marker m = obj as Marker;
+        return (m != null && m.mname == mname);
+       }
+
+       public override int GetHashCode () { return mname.GetHashCode (); }
+
+       public class Named : Marker
+       {
+        public Named (MSymbol mname) : base (mname) { }
+
+        public override int Position (Context ic)
+        {
+          MPlist p =  ic.marker_positions.Find (mname);
+          return (p == null ? 0 : p.Integer);
+        }
+
+        public override void Mark (Context ic)
+        {
+          ic.marker_positions.Put (mname, ic.cursor_pos);
+        }
+       }
+
+       public class Predefined : Marker
+       {
+        char tag;
+
+        public Predefined (char tag) : base ("@" + tag) { this.tag = tag; }
+
+        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 mname) : base (mname)
+          {
+            if (! int.TryParse (((string) mname).Substring (1), out pos))
+              throw new Exception ("Invalid marker name: " + mname);
+          }
+
+        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 mname) : base (mname)
+          {
+            if (! int.TryParse (((string) name).Substring (1), out distance))
+              throw new Exception ("Invalid marker name: " + mname);
+            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> predefineds;
+
+       static Marker ()
+        {
+          predefineds = new Dictionary<MSymbol, Predefined> ();
+          predefineds ["@<"] = predefineds["@first"] = new Predefined ('<');
+          predefineds ["@>"] = predefineds["@last"] = new Predefined ('>');
+          predefineds ["@-"] = predefineds["@previous"] = new Predefined ('-');
+          predefineds ["@+"] = predefineds["@next"] = new Predefined ('+');
+          predefineds ["@["] = predefineds["@previous-candidate-change"]
+            = new Predefined ('[');
+          predefineds ["@]"] = predefineds["@next-candidate-change"]
+            = new Predefined (']');
+        }
+
+       public static Marker Get (MSymbol mname)
+       {
+        string str = mname.Name;
+        if (str[0] == '@')
+          {
+            Predefined pred;
+            if (predefineds.TryGetValue (mname, out pred))
+              return pred;
+            if (str.Length == 1)
+              throw new Exception ("Invalid marker name: " + mname);
+            if (Char.IsDigit (str[1]))
+              return new PredefinedAbsolute (mname);
+            if (str.Length == 2 || mname == Mat_minus_zero
+                || ! (str[1] == '-' || str[1] == '+'))
+              throw new Exception ("Invalid marker name: " + mname);
+            return new PredefinedSurround (mname);
+          }
+        return new Named (mname);
+       }
+     }
+
+     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];
+            fill_group (0);
+          }
+       }
+
+       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];
+            fill_group (0);
+          }
+       }
+
+       public static void Detach (Context ic)
+       {
+        ic.preedit.PopProp (0, ic.preedit.Length, Mcandidates);
+        ic.candidates = null;
+        ic.changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                       | CandidateAll);
+       }
+
+       // Fill the array "group" by candidates stating from START.
+       // START must be a multiple of "column".  Return the number of
+       // valid candidates in "group".
+
+       private int fill_group (int start)
+       {
+        int nitems = group.Length;
+        int r = row;
+        Block b = blocks[r];
+
+        if (start < b.Index)
+          while        (start < b.Index)
+            b = blocks[--r];
+        else
+          while        (start >= b.Index + b.Count)
+            b = blocks[++r];
+        row = r;
+
+        int count = b.Count;
+        start -= b.Index;
+        for (int i = 0; i < nitems; i++, start++)
+          {
+            if (start >= count)
+              {
+                r++;
+                if (r == blocks.Length)
+                  return i;
+                b = blocks[r];
+                count = b.Count;
+                start = 0;
+              }
+            group[i] = b[start];
+          }
+        return nitems;
+       }
+
+       // Update "row" to what contains the first candidate of
+       // the previous candidate-group, update "current_index", and
+       // update "group" if necessary.  Return the previous
+       // candidate-group.  Set NITEMS to the number of valid
+       // candidates contained in that group.
+
+       public int PrevGroup ()
+       {
+        int nitems;
+        int col = Column;
+
+        if (IsFixed)
+          {
+            nitems = group.Length;
+            if ((index -= col + nitems) < 0)
+              index = (Total / nitems) * nitems;
+            nitems = fill_group (index);
+          }
+        else
+          {
+            row = row > 0 ? row-- : blocks.Length - 1;
+            nitems = blocks[row].Count;
+            index = blocks[row].Index;
+          }
+        index += col < nitems ? col : nitems - 1;
+        return nitems;
+       }
+
+       public int NextGroup ()
+       {
+        int nitems;
+        int col = Column;
+
+        if (IsFixed)
+          {
+            nitems = group.Length;
+            if ((index += nitems - col) >= Total)
+              index = 0;
+            nitems = fill_group (index);
+          }
+        else
+          {
+            row = row < blocks.Length - 1 ? row + 1 : 0;
+            nitems = blocks[row].Count;
+            index = blocks[row].Count;
+          }
+        index += col < nitems ? col : nitems - 1;
+        return nitems;
+       }
+
+       public void Prev ()
+       {
+        int col = Column;
+
+        if (col == 0)
+          {
+            int nitems = PrevGroup ();
+            index += col < nitems - 1 ? col : nitems - 1;
+          }
+        else
+          index--;
+       }
+
+       public void Next ()
+       {
+        int col = Column;
+        int nitems = GroupLength;
+
+        if (col == nitems - 1)
+          {
+            nitems = NextGroup ();
+            index -= Column;
+          }
+        else
+          index++;
+       }
+
+       public void First ()
+       {
+        index -= Column;
+       }
+
+       public void Last ()
+       {
+        index += GroupLength - (Column + 1);
+       }
+
+       public object Select (int col)
+       {
+        int maxcol = GroupLength - 1;
+        if (col > maxcol)
+          col = maxcol;
+        index = index - Column + col;
+        return Current;
+       }
+
+       public object Select (Selector selector)
+       {
+        switch (selector.Tag)
+          {
+          case '<': First (); break;
+          case '>': Last (); break;
+          case '-': Prev (); break;
+          case '+': Next (); break;
+          case '[': PrevGroup (); break;
+          case ']': NextGroup (); break;
+          default: break;
+          }
+        return Current;
+       }
+
+       public override string ToString ()
+       {
+        return (String.Format ("<candidates row={0} col={1}>", row, index)
+                + Group
+                + "</candidates>");
+       }
+     }
 
     internal class Selector : Xex.TermValue
     {
+      private static Xex.Symbol name = "selector";
+      public static Xex.Symbol Name { get { return name; } }
+
       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 (']');
+         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 (']');
        }
 
-      private char tag;
+      private readonly char tag;
 
-      private Selector (MSymbol sym) { tag = sym.Name[1]; }
+      public char Tag { get { return tag; } }
 
       private Selector (char tag) { this.tag = tag; }
 
-      public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+      public static Xex.TermValue Parser (Xex.Domain domain, XmlNode node)
       {
        return Get ((MSymbol) node.InnerText);
       }
@@ -991,20 +1064,17 @@ namespace M17N.Input
        return selector;
       }
 
-      public override Xex.TermValue Clone () { return this; }
+      public override bool Equals (object obj)
+      {
+       Selector s = obj as Selector;
+       return (s != null && s.tag == tag);
+      }
+
+      public override int GetHashCode () { return (int) tag; }
 
-      public void Select (Candidates candidates)
+      public override string ToString ()
       {
-       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;
-         }
+       return "<selector>@" + tag + "</selector>";
       }
     }
 
@@ -1036,6 +1106,31 @@ namespace M17N.Input
       }
     }
 
+    protected class Action
+    {
+      private Xex.Term action;
+
+      public Action (Xex.Domain domain, Xex.Term[] terms)
+      {
+       Xex.Term[] args = new Xex.Term[terms.Length];
+       args[0] = Tcatch_tag;
+       for (int i = 0; i < terms.Length; i++)
+         args[i + 1] = terms[i];
+       action = new Xex.Term (domain, Qcatch, args);
+      }
+
+      public bool Run (Xex.Domain domain)
+      {
+       Xex.Term result = action.Eval (domain);
+       if (result.IsError)
+         {
+           ((Context) domain.context).Error = result.ToString ();
+           return false;
+         }
+       return (result != Tcatch_tag);
+      }
+    }
+
     internal class Keymap
     {
       public Dictionary<Key, Keymap> submaps;
@@ -1122,12 +1217,12 @@ namespace M17N.Input
 
     internal class State
     {
-      public Xex.Symbol name;
+      public MSymbol name;
       public MText title;
       public Xex.Term[] enter_actions, fallback_actions;
       public Keymap keymap = new Keymap ();
 
-      public State (Xex.Symbol name, MText title)
+      public State (MSymbol name, MText title)
       {
        this.name = name;
        this.title = title;
@@ -1165,7 +1260,7 @@ namespace M17N.Input
       {
        if (! plist.IsSymbol)
          throw new Exception ("Invalid state: " + plist);
-       this.name = plist.Symbol.Name;
+       this.name = plist.Symbol;
        plist = plist.next;
        if (plist.IsMText)
          {
@@ -1210,7 +1305,7 @@ namespace M17N.Input
     }
 
     // Instance members
-    internal Xex.Domain domain = new Xex.Domain (im_domain, null);
+    internal Xex.Domain domain;
 
     protected LoadStatus load_status = LoadStatus.None;
     protected MDatabase.Tag tag;
@@ -1222,21 +1317,20 @@ namespace M17N.Input
     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;
+    internal MPlist states;
 
     static MInputMethod ()
     {
-      im_domain.DefTerm ("keyseq", KeySeq.parser);
-      im_domain.DefTerm ("marker", Marker.parser);
-      im_domain.DefTerm ("selector", Selector.parser);
+      im_domain.DefType (typeof (KeySeq));
+      im_domain.DefType (typeof (Marker));
+      im_domain.DefType (typeof (Selector));
 
       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 (Fshow, "show-candidates", false, 0, 0);
+      im_domain.DefSubr (Fhide, "hide-candidates", 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);
@@ -1247,7 +1341,7 @@ namespace M17N.Input
       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 (Fkey_count, "key-count", false, 0, 0);
       im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag",
                         false, 0, 0);
 
@@ -1262,6 +1356,7 @@ namespace M17N.Input
     private MInputMethod (MDatabase.Tag tag)
     {
       this.tag = tag;
+      domain = new Xex.Domain (tag[1].Name, im_domain, null);
     }
 
     // Instance Properties
@@ -1333,15 +1428,14 @@ namespace M17N.Input
       mdb = MDatabase.Find (tag);
       if (mdb == null)
        return false;
-      mdb.name_table = Xex.Symbol.Table;
       try {
-       MSymbol format = mdb.Format;
-
-       if (format == MSymbol.plist)
-         load ((MPlist) mdb.Load (Mmap), false);
+       if (mdb.Format == MSymbol.plist)
+         load (mdb.Load (Mmap), false);
        else
          {
-           XmlDocument doc = (XmlDocument) mdb.Load (Mmap_list);
+           XmlDocument doc = new XmlDocument (Xex.Symbol.NameTable);
+           if (! mdb.Load (doc, Mmap_list))
+             throw new Exception ("Load error" + mdb.tag);
            load (doc.DocumentElement, false);
          }
       } catch (Exception e) {
@@ -1358,13 +1452,16 @@ namespace M17N.Input
       mdb = MDatabase.Find (tag);
       if (mdb == null)
        return false;
-      mdb.name_table = Xex.Symbol.Table;
       try {
-       object obj = mdb.Load ();
-       if (obj is MPlist)
-         load ((MPlist) obj, true);
+       if (mdb.Format == MSymbol.plist)
+         load (mdb.Load (), true);
        else
-         load ((XmlDocument) obj, true);
+         {
+           XmlDocument doc = new XmlDocument (Xex.Symbol.NameTable);
+           if (! mdb.Load (doc))
+             throw new Exception ("Load error" + mdb.tag);
+           load (doc.DocumentElement, true);
+         }
       } catch (Exception e) {
        Console.WriteLine (e);
        load_status = LoadStatus.Error;
@@ -1376,17 +1473,17 @@ namespace M17N.Input
 
     private void add_default_state ()
     {
-      Xex.Symbol Qinit = "init";
+      MSymbol 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;
+      states.Add (Qinit, state);
     }
 
     private void load (MPlist plist, bool full)
     {
       maps = new Dictionary<MSymbol, Map> ();
-      states = new Dictionary<Xex.Symbol, State> ();
+      states = new MPlist ();
 
       for (; ! plist.IsEmpty; plist = plist.next)
        if (plist.IsPlist)
@@ -1431,7 +1528,7 @@ namespace M17N.Input
        commands = new Command[0];
       if (! full)
        return;
-      if (states.Count == 0)
+      if (states.IsEmpty)
        add_default_state ();
     }
 
@@ -1440,7 +1537,7 @@ namespace M17N.Input
       bool skip_header = load_status == LoadStatus.Header;
 
       maps = new Dictionary<MSymbol, Map> ();
-      states = new Dictionary<Xex.Symbol, State> ();
+      states = new MPlist ();
 
       if (node.NodeType == XmlNodeType.Document)
        node = node.FirstChild;
@@ -1461,7 +1558,7 @@ namespace M17N.Input
              else if (node.Name == "command-list")
                parse_commands (node);
            }
-         else if (full)
+         if (full)
            {
              if (node.Name == "module-list")
                parse_plugins (node);
@@ -1481,140 +1578,42 @@ namespace M17N.Input
        commands = new Command[0];
       if (! full)
        return;
-      if (states.Count == 0)
+      if (states.IsEmpty)
        add_default_state ();
     }
 
     private static MText parse_description (MPlist plist)
     {
       if (plist.IsMText)
-       return plist.Text;
-      if (plist.IsPlist)
-       {
-         plist = plist.Plist;
-         if (plist.IsSymbol && plist.Symbol == (MSymbol) "_"
-             && plist.next.IsMText)
-           return plist.next.Text;
-       }
-      return null;
-    }
-
-    private static MText parse_description (XmlNode node)
-    {
-      if (node.HasChildNodes)
-       node = node.FirstChild;
-      return node.InnerText;
-    }
-
-    private static MText parse_title (XmlNode node)
-    {
-      return node.InnerText;
-    }
-
-    private void new_variable (Xex.Symbol name, string desc, int val,
-                              MPlist pl, Xex.Variable vari)
-    {
-      int[] range;
-
-      if (pl.IsEmpty)
-       range = null;
-      else
-       {
-         int nrange = pl.Count;
-         range = new int[nrange * 2];
-         for (int i = 0; i < nrange; i++)
-           {
-             if (pl.IsPlist)
-               {
-                 MPlist p = pl.Plist;
-
-                 if (! p.IsInteger || ! p.next.IsInteger)
-                   throw new Exception ("Invalid range: " + p);
-                 range[i * 2] = p.Integer;
-                 range[i * 2 + 1] = p.next.Integer;
-               }
-             else if (pl.IsInteger)
-               range[i * 2] = range[i * 2 + 1] = pl.Integer;
-             else
-               throw new Exception ("Invalid range: " + pl);
-           }
-       }
-      if (vari == null)
-       domain.Defvar (new Xex.Variable.Int (name, desc, val, range));
-      else
-       {
-         Xex.Term term = new Xex.Term (val);
-         vari.Value = term;
-         vari.DefaultValue = term;
-         vari.Range = range;
-       }
-    }
-
-    private void new_variable (Xex.Symbol name, string desc, MText val,
-                              MPlist pl, Xex.Variable vari)
-    {
-      string[] range;
-
-      if (pl.IsEmpty)
-       range = null;
-      else
-       {
-         range = new string[pl.Count * 2];
-         for (int i = 0; i < range.Length; i++)
-           {
-             if (pl.IsMText)
-               range[i] = (string) pl.Text;
-             else
-               throw new Exception ("Invalid range: " + pl);
-           }
-       }
-      if (vari == null)
-       domain.Defvar (new Xex.Variable.Str (name, desc, (string) val, range));
-      else
+       return plist.Text;
+      if (plist.IsPlist)
        {
-         Xex.Term term = new Xex.Term ((string) val);
-         vari.Value = term;
-         vari.DefaultValue = term;
-         vari.Range = range;
+         plist = plist.Plist;
+         if (plist.IsSymbol && plist.Symbol == (MSymbol) "_"
+             && plist.next.IsMText)
+           return plist.next.Text;
        }
+      return null;
     }
 
-    private void new_variable (Xex.Symbol name, string desc, MSymbol val,
-                              MPlist pl, Xex.Variable vari)
+    private static MText parse_description (XmlNode node)
     {
-      Xex.Symbol[] range;
-      Xex.Symbol sym = val.Name;
+      if (node.HasChildNodes)
+       node = node.FirstChild;
+      return node.InnerText;
+    }
 
-      if (pl.IsEmpty)
-       range = null;
-      else
-       {
-         range = new Xex.Symbol[pl.Count * 2];
-         for (int i = 0; i < range.Length; i++)
-           {
-             if (pl.IsSymbol)
-               range[i] = pl.Symbol.Name;
-             else
-               throw new Exception ("Invalid range: " + pl);
-           }
-       }
-      if (vari == null)
-       domain.Defvar (new Xex.Variable.Sym (name, desc, sym, range));
-      else
-       {
-         Xex.Term term = new Xex.Term (sym);
-         vari.Value = term;
-         vari.DefaultValue = term;
-         vari.Range = range;
-       }
+    private static MText parse_title (XmlNode node)
+    {
+      return node.InnerText;
     }
 
     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");
+         MDatabase.Tag 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"); 
@@ -1635,23 +1634,84 @@ namespace M17N.Input
          Xex.Symbol name = (Xex.Symbol) p.Symbol.Name;
          var_names[i] = name;
          p = p.next;
-         string desc = (string) parse_description (p);
+         MText mt = parse_description (p);
+         string desc = mt == null ? null : (string) mt;
+         if (! p.IsEmpty)
+           p = p.next;
          Xex.Variable vari = get_global_var (name);
          if (vari != null)
            domain.Defvar (vari);
-         if (desc != null)
-           p = p.next;
-         if (! p.IsEmpty)
+         if (p.IsInteger)
+           {
+             int n = p.Integer;
+             int[] range = null;
+
+             p = p.Next;
+             if (! p.IsEmpty)
+               {
+                 int nrange = p.Count;
+                 range = new int[nrange * 2];
+                 for (int j = 0; j < nrange; j++)
+                   {
+                     if (p.IsPlist)
+                       {
+                         MPlist p0 = p.Plist;
+
+                         if (! p0.IsInteger || ! p0.next.IsInteger)
+                           throw new Exception ("Invalid range: " + p0);
+                         range[j * 2] = p0.Integer;
+                         range[j * 2 + 1] = p0.next.Integer;
+                       }
+                     else if (p.IsInteger)
+                       range[j * 2] = range[j * 2 + 1] = p.Integer;
+                     else
+                       throw new Exception ("Invalid range: " + p);
+                   }
+               }
+             domain.DefvarInt (name, n, desc, range);
+           }
+         else if (p.IsMText)
+           {
+             string str = (string) p.Text;
+             string[] range = null;
+
+             p = p.next;
+             if (! p.IsEmpty)
+               {
+                 range = new string[p.Count];
+                 for (int j = 0; j < range.Length; j++)
+                   {
+                     if (p.IsMText)
+                       range[j] = (string) p.Text;
+                     else
+                       throw new Exception ("Invalid range: " + p);
+                   }
+               }
+             domain.DefvarStr (name, str, desc, range);
+           }
+         else if (p.IsSymbol)
            {
-             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);
+             Xex.Symbol sym = p.Symbol.Name;
+             Xex.Symbol[] range;
+
+             p = p.next;
+             if (p.IsEmpty)
+               range = null;
              else
-               throw new Exception ("Invalid variable type: " + p.val);
+               {
+                 range = new Xex.Symbol[p.Count];
+                 for (int j = 0; j < range.Length; j++)
+                   {
+                     if (p.IsSymbol)
+                       range[j] = p.Symbol.Name;
+                     else
+                       throw new Exception ("Invalid range: " + p);
+                   }
+               }
+             domain.DefvarSym (name, sym, desc, range);
            }
+         else if (! p.IsEmpty)
+           throw new Exception ("Invalid variable type: " + p.val);
        }
     }
 
@@ -1726,35 +1786,71 @@ namespace M17N.Input
        }
     }
 
+    private void parse_include (XmlNode node)
+    {
+      XmlNode n;
+      MSymbol language, name, subname;
+      MSymbol part, section;
+      node = node.FirstChild;
+      n = node.FirstChild;
+      language = n.InnerText;
+      n = n.NextSibling;
+      name = n.InnerText;
+      n = n.NextSibling;      
+      if (n != null)
+       subname = n.InnerText;
+      else
+       subname = MSymbol.nil;
+      node = node.NextSibling;
+      part = node.InnerText;
+      node = node.NextSibling;
+      if (node != null)
+       section = node.InnerText;
+      else
+       section = MSymbol.nil;
+      include_part (language, name, subname, part, section);
+    }
+
     private void parse_macros (XmlNode node)
     {
       for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
        if (nn.NodeType == XmlNodeType.Element)
-         domain.Defun (nn, true);
+       {
+         if (nn.Name == Xex.Qdefun)
+           domain.Defun (nn, true);
+         else if (nn.Name == Qxi_include)
+           parse_include (nn);
+       }
       for (XmlNode nn = node.FirstChild; nn != null; nn = nn.NextSibling)
-       if (nn.NodeType == XmlNodeType.Element)
+       if (nn.NodeType == XmlNodeType.Element
+           && nn.Name == Xex.Qdefun)
          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));
-               }
-         }
+       {
+         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));
+                 }
+           }
+         else if (node.Name == Qxi_include)
+           parse_include (node);
+       }
     }
 
     private void parse_states (MPlist plist)
@@ -1763,67 +1859,42 @@ namespace M17N.Input
        if (plist.IsPlist)
          {
            State state = new State (this, plist.Plist);
-           states[state.name] = state;     
-           if (initial_state == null)
-             initial_state = state;
+           states.Add (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;
-           if (initial_state == null)
-             initial_state = state;
-         }
-    }
-
-    private void parse_include (MPlist plist)
-    {
-      if (! plist.IsPlist)
-       return;
-      MPlist p = plist.Plist;
-      MSymbol language, name, subname;
-      language = p.Symbol;
-      p = p.next;
-      if (! p.IsSymbol)
-       name = subname = MSymbol.nil;
-      else
        {
-         name = p.Symbol;
-         p = p.next;
-         if (! p.IsSymbol)
-           subname = MSymbol.nil;
-         else
-           subname = p.Symbol;
+         if (node.Name == Qstate)
+           {
+             State state = new State (this, node);
+             states.Add (state.name, state);
+           }
+         else if (node.Name == Qxi_include)
+           parse_include (node);
        }
+    }
 
+    private void include_part (MSymbol language, MSymbol name, MSymbol subname,
+                              MSymbol part, MSymbol section)
+    {
       MInputMethod im = MInputMethod.Find (language, name, subname);
       if (im == null)
        return;
       if (! im.Open ())
        return;
-      plist = plist.next;
-      if (! plist.IsSymbol)
-       return;
-      MSymbol target_type = plist.Symbol;
-      plist = plist.next;
-      MSymbol target_name = MSymbol.nil;
-      if (plist.IsSymbol)
-       target_name = plist.Symbol;
-      if (target_type == Mmacro)
+      if (part == Mmacro)
        {
-         if (target_name == MSymbol.nil)
+         if (section == MSymbol.nil)
            im.domain.CopyFunc (domain);
          else
-           im.domain.CopyFunc (domain, (Xex.Symbol) target_name.Name);
+           im.domain.CopyFunc (domain, (Xex.Symbol) section.Name);
        }
-      else if (target_type == Mmap)
+      else if (part == Mmap)
        {
-         if (target_name == MSymbol.nil)
+         if (section == MSymbol.nil)
            {
              foreach (KeyValuePair<MSymbol, Map> kv in im.maps)
                maps[kv.Key] = kv.Value;
@@ -1831,27 +1902,57 @@ namespace M17N.Input
          else
            {
              Map map;
-             if (im.maps.TryGetValue (target_name, out map))
-               maps[target_name] = map;
+             if (im.maps.TryGetValue (section, out map))
+               maps[section] = map;
            }
        }
-      else if (target_type == Mstate)
+      else if (part == Mstate)
        {
-         if (target_name == MSymbol.nil)
+         if (section == MSymbol.nil)
            {
-             foreach (KeyValuePair<Xex.Symbol, State> kv in im.states)
-               states[kv.Key] = kv.Value;
+             for (MPlist p = im.states; ! p.IsEmpty; p = p.next)
+               states.Add (p.Key, p.Val);
            }
          else
            {
-             Xex.Symbol state_name = target_name.Name;
-             State state;
-             if (im.states.TryGetValue (state_name, out state))
-               states[state_name] = state;
+             MSymbol state_name = (string) section.Name;
+             State state = (State) im.states.Get (state_name);
+             if (state != null)
+               states.Add (state.name, state);
            }
        }
     }
 
+    private void parse_include (MPlist plist)
+    {
+      if (! plist.IsPlist)
+       return;
+      MPlist p = plist.Plist;
+      MSymbol language, name, subname;
+      language = p.Symbol;
+      p = p.next;
+      if (! p.IsSymbol)
+       name = subname = MSymbol.nil;
+      else
+       {
+         name = p.Symbol;
+         p = p.next;
+         if (! p.IsSymbol)
+           subname = MSymbol.nil;
+         else
+           subname = p.Symbol;
+       }
+      plist = plist.next;
+      if (! plist.IsSymbol)
+       return;
+      MSymbol part = plist.Symbol;
+      plist = plist.next;
+      MSymbol section = MSymbol.nil;
+      if (plist.IsSymbol)
+       section = plist.Symbol;
+      include_part (language, name, subname, part, section);
+    }
+
     private Xex.Term parse_cond (MPlist plist)
     {
       Xex.Term[] args = new Xex.Term[plist.Count];
@@ -1861,7 +1962,10 @@ 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_actions (p, false));
+         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);
@@ -1930,13 +2034,13 @@ namespace M17N.Input
       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));
+       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 (Xex.Symbol name)
+    private Xex.Term parse_char_at (MSymbol name)
     {
       Xex.Term[] args = new Xex.Term[1];
       args[0] = new Xex.Term (Marker.Get (name));
@@ -1962,7 +2066,8 @@ namespace M17N.Input
            return parse_insert (plist);
          if (! p.IsSymbol)
            throw new Exception ("Invalid action: " + p);
-         Xex.Symbol name = p.Symbol.Name;
+         MSymbol sym = p.Symbol;
+         Xex.Symbol name = sym.Name;
          p = p.next;
          if (name == Qcond)
            return parse_cond (p);
@@ -1974,8 +2079,6 @@ namespace M17N.Input
            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)
            {
@@ -1990,6 +2093,10 @@ namespace M17N.Input
            {
              if (name == Qeq)
                name = Qeqeq;
+             else if (name == Qhide)
+               name = Qhide_candidates;
+             else if (name == Qshow)
+               name = Qshow_candidates;
              if (p.IsEmpty)
                return new Xex.Term (domain, name, null);
              else
@@ -1997,7 +2104,15 @@ namespace M17N.Input
            }
        }
       else if (plist.IsSymbol)
-       return new Xex.Term (domain, (Xex.Symbol) plist.Symbol.Name);
+       {
+         if (plist.Symbol == Matat)
+           return new Xex.Term (domain, Qkey_count, null);
+         if (plist.Symbol == Mat_minus_zero)
+           return new Xex.Term (domain, Qsurrounding_text_flag, null);
+         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));
@@ -2017,16 +2132,53 @@ namespace M17N.Input
       return terms;
     }
 
+    private Xex.Variable[] parse_args (Xex.Domain domain, Xex.Symbol[] args)
+    {
+      int nfixed = 0;
+      int noptional = 0;
+      int nrest = 0;
+
+      if (args.Length > 0)
+       {
+         int i = 0;
+         for (i = 0; i < args.Length; i++, nfixed++)
+           if (args[i] == Qoptional || args[i] == Qrest)
+             break;
+         if (i < args.Length)
+           {
+             if (args[i] == Qoptional)
+               {
+                 for (i++; i < args.Length; i++, noptional++)
+                   if (args[i] == Qrest)
+                     break;
+                 if (i < args.Length)
+                   nrest = 1;
+               }
+           }
+         min_args = nfixed;
+         max_args = nfixed + noptional + nrest;
+         this.args = new Variable[max_args];
+         int j;
+         for (i = j = 0; j < this.args.Length; i++)
+           if (args[i] != Qoptional || args[i] != Qrest)
+             this.args[j++] = domain.Defvar (args[i]);
+       }
+      else
+       {
+         min_args = max_args = 0;
+       }
+      with_var = false;
+    }
+
     private void parse_macros (MPlist plist)
     {
       for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
        if (pl.IsPlist)
          {
            MPlist p = pl.Plist;
-
            if (! p.IsSymbol)
              continue;
-           domain.Defun ((Xex.Symbol) p.Symbol.Name, false, null, null, true);
+           domain.Defun ((Xex.Symbol) p.Symbol.Name, null, null, true);
          }
       for (MPlist pl = plist; ! pl.IsEmpty; pl = pl.next)
        if (pl.IsPlist)
@@ -2035,7 +2187,7 @@ namespace M17N.Input
 
            if (! p.IsSymbol)
              continue;
-           domain.Defun ((Xex.Symbol) p.Symbol.Name, false, null,
+           domain.Defun ((Xex.Symbol) p.Symbol.Name, null,
                          parse_actions (p.next, false), false);
          }
     }
@@ -2075,9 +2227,9 @@ namespace M17N.Input
                                     Xex.Term[] args)
     {
       if (args[0].IsInt)
-       ((Context) domain.context).insert (args[0].Intval);
+       ((Context) domain.context).insert (args[0].Intval, null);
       else
-       ((Context) domain.context).insert (args[0].Strval);
+       ((Context) domain.context).insert ((MText) args[0].Strval, null);
       return args[0];
     }
 
@@ -2088,8 +2240,13 @@ namespace M17N.Input
       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;
 
-      ic.insert_candidates (new Candidates (args, column));
+      if (candidate is MText)
+       ic.insert ((MText) candidate, candidates);
+      else
+       ic.insert ((int) candidate, candidates);
       return args[0];
     }
 
@@ -2121,10 +2278,26 @@ namespace M17N.Input
     private static Xex.Term Fselect (Xex.Domain domain, Xex.Variable vari,
                                     Xex.Term[] args)
     {
-      Candidates can = ((Context) domain.context).candidates;
+      Context ic = (Context) domain.context;
+      Candidates can = ic.candidates;
 
       if (can != null)
-       ((Selector) args[0].Objval).Select (can);
+       {
+         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];
     }
 
@@ -2200,20 +2373,18 @@ namespace M17N.Input
                                       Xex.Term[] args)
     {
       ((Context) domain.context).commit ();
-      args = new Xex.Term[2];
-      args[0] = args[1] = Tcatch_tag;
-      return Xex.Fthrow (domain, vari, args);
+      return Xex.Fthrow (domain, vari, new Xex.Term[1] { Tcatch_tag });
     }
 
     private static Xex.Term Fshift (Xex.Domain domain, Xex.Variable vari,
                                    Xex.Term[] args)
     {
       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);
+      MSymbol state_name = (string) args[0].Symval;
+      State state = (State) ic.im.states.Get (state_name);
+      if (state == null)
+       throw new Exception ("Unknown state: " + state_name);
+      ((Context) domain.context).shift (state);
       return args[0];
     }
 
@@ -2234,7 +2405,8 @@ namespace M17N.Input
                                               Xex.Variable vari,
                                               Xex.Term[] args)
     {
-      return new Xex.Term (GetSurroundingText == null ? 0 : 1);
+      return new Xex.Term (((Context) domain.context).GetSurroundingText == null
+                          ? -2 : -1);
     }
 
     public override string ToString ()
@@ -2266,10 +2438,8 @@ namespace M17N.Input
       foreach (KeyValuePair<MSymbol, Map> kv in maps)
        str += " " + kv.Value;
       str += ") (states";
-      foreach (KeyValuePair<Xex.Symbol, State> kv in states)
-       {
-         str += " (" + kv.Key + " " + kv.Value.keymap + ")";
-       }
+      for (MPlist p = states; ! p.IsEmpty; p = p.next)
+       str += " (" + p.Key + " " + ((State) p.Val).keymap + ")";
       return str + "))";
     }
 
@@ -2279,19 +2449,23 @@ namespace M17N.Input
       internal Xex.Domain domain;
       private bool active;
 
+      public Callback PreeditChanged;
+      public Callback StatusChanged;
+      public Callback CandidateChanged;
+      public Callback GetSurroundingText;
+      public Callback DelSurroundingText;
+
       private MText status;
       private MText produced = new MText ();
       internal MText preedit = new MText ();
       internal int cursor_pos;
-      internal Dictionary<Marker, int> marker_positions
-       = new Dictionary<Marker, int> ();
+      internal MPlist marker_positions = new MPlist ();
 
       internal Candidates candidates;
-      private int candidate_from, candidate_to;
       private bool candidate_show;
       public bool CandidateShow { get { return candidate_show; } }
 
-      private State state, prev_state;
+      private State initial_state, state, prev_state;
       private MText state_preedit = new MText ();
       private int state_key_head;
       private object state_var_values, state_initial_var_values;
@@ -2303,7 +2477,6 @@ namespace M17N.Input
       // Index into KEYS specifying the next key to handle.
       internal int key_head;
 
-      private int commit_key_head;
       internal MText preceding_text = new MText ();
       internal MText following_text = new MText ();
 
@@ -2317,27 +2490,38 @@ namespace M17N.Input
 
       internal ChangedStatus changed;
 
+      private string error_message;
+      public string Error {
+       get { return error_message; }
+       set { error_message = value; }
+      }
+
+      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;
+      }
+
       internal void reset ()
       {
-       status = im.initial_state.title;
+       status = initial_state.title;
        produced.Del ();
        preedit.Del ();
 
-       cursor_pos = 0;
+       set_cursor ("reset", 0);
        marker_positions.Clear ();
        candidates = null;
        candidate_show = false;
 
-       state = im.initial_state;
-       prev_state = null;
+       state = prev_state = null;
        state_preedit.Del ();
-       state_key_head = 0;
        state_var_values = state_initial_var_values;
        state_pos = 0;
-
-       keymap = im.initial_state.keymap;
-       keys.keyseq.Clear ();
-       key_head = commit_key_head = 0;
+       shift (initial_state);
 
        preceding_text.Del ();
        following_text.Del ();
@@ -2387,90 +2571,59 @@ namespace M17N.Input
        return following_text[pos];
       }        
 
-      private void adjust_markers (int from, int to, object inserted)
+      private void adjust_markers (int from, int to, int inserted)
       {
-       int ins = (inserted == null ? 0
-                  : inserted is int ? 1
-                  : ((MText) inserted).Length);
-       int diff = ins - (to - from);
+       int diff = inserted - (to - from);
 
-       foreach (Marker m in marker_positions.Keys)
+       for (MPlist p = marker_positions; ! p.IsEmpty; p = p.next)
          {
-           int pos = marker_positions[m];
+           int pos = p.Integer;
            if (pos > from)
-             {
-               if (pos >= to)
-                 marker_positions[m] = pos + diff;
-               else
-                 marker_positions[m] = from;
-             }
+             p.Set (p.Key, pos >= to ? pos + diff : from);
          }
        if (cursor_pos >= to)
-         cursor_pos += diff;
+         set_cursor ("adjust", cursor_pos + diff);
        else if (cursor_pos > from)
-         cursor_pos = from;
+         set_cursor ("adjust", from);
       }
 
-      private void preedit_replace (int from, int to, int c)
+      private void preedit_replace (int from, int to, int c,
+                                   Candidates candidates)
       {
        preedit.Del (from, to);
        preedit.Ins (from, c);
-       adjust_markers (from, to, c);
+       if (candidates != null)
+         {
+           preedit.PushProp (from, from + 1, Mcandidates, candidates);
+           changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                       | CandidateAll);
+         }
+       adjust_markers (from, to, 1);
       }
 
-      private void preedit_replace (int from, int to, MText mt)
+      private void preedit_replace (int from, int to, MText mt,
+                                   Candidates candidates)
       {
        preedit[from, to] = mt;
-       adjust_markers (from, to, mt);
-      }
-
-      internal void insert (int c)
-      {
-       preedit_replace (cursor_pos, cursor_pos, c);
-       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
-      }
-
-      internal void insert (string str)
-      {
-       preedit_replace (cursor_pos, cursor_pos, (MText) str);
-       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
-      }
-
-      private void update_candidate ()
-      {
-       object candidate = candidates.Current;
-
-       if (candidate is MText)
-         {
-           preedit_replace (candidate_from, candidate_to, (MText) candidate);
-           candidate_to = candidate_from + ((MText) candidate).Length;
-         }
-       else
+       if (candidates != null)
          {
-           preedit_replace (candidate_from, candidate_to, (int) candidate);
-           candidate_to = candidate_from + 1;
+           preedit.PushProp (from, from + mt.Length, Mcandidates, candidates);
+           changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
+                       | CandidateAll);
          }
-       preedit.PushProp (candidate_from, candidate_to,
-                         Mcandidates, this);
-       cursor_pos = candidate_from;
-       changed |= (ChangedStatus.Preedit | ChangedStatus.CursorPos
-                   | CandidateAll);
+       adjust_markers (from, to, mt == null ? 0 : mt.Length);
       }
 
-      internal void insert_candidates (Candidates candidates)
+      internal void insert (int c, Candidates candidates)
       {
-       this.candidates = candidates;
-       candidate_from = candidate_to = cursor_pos;
-       update_candidate ();
+       preedit_replace (cursor_pos, cursor_pos, c, candidates);
+       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
       }
 
-      internal void select (int n)
+      internal void insert (MText mt, Candidates candidates)
       {
-       if (candidates != null)
-         {
-           candidates.Select (n);
-           update_candidate ();
-         }
+       preedit_replace (cursor_pos, cursor_pos, mt, candidates);
+       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
       }
 
       internal int delete (int pos)
@@ -2481,10 +2634,11 @@ namespace M17N.Input
          {
            if (pos < 0)
              {
-               if (DeleteSurroundingText != null)
+               if (DelSurroundingText != null)
                  {
+                   Console.WriteLine ("deleting the prev {0} chars", - pos);
                    callback_arg.Set (MSymbol.integer, pos);
-                   if (DeleteSurroundingText (this, callback_arg))
+                   if (DelSurroundingText (this, callback_arg))
                      {
                        if (callback_arg.IsInteger)
                          deleted = callback_arg.Integer - cursor_pos;
@@ -2496,16 +2650,18 @@ namespace M17N.Input
                pos = 0;
              }
            if (pos < cursor_pos)
-             preedit_replace (pos, cursor_pos, null);
+             preedit_replace (pos, cursor_pos, null, null);
          }
        else
          {
            if (pos > preedit.Length)
              {
-               if (DeleteSurroundingText != null)
+               if (DelSurroundingText != null)
                  {
+                   Console.WriteLine ("deleting the next {0} chars",
+                                      pos - preedit.Length);
                    callback_arg.Set (MSymbol.integer, pos - preedit.Length);
-                   if (DeleteSurroundingText (this, callback_arg))
+                   if (DelSurroundingText (this, callback_arg))
                      {
                        if (callback_arg.IsInteger)
                          deleted = callback_arg.Integer - cursor_pos;
@@ -2517,7 +2673,7 @@ namespace M17N.Input
                pos = preedit.Length;
              }
            if (pos > cursor_pos)
-             preedit_replace (cursor_pos, pos, null);
+             preedit_replace (cursor_pos, pos, null, null);
          }
        if (deleted != 0)
          changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
@@ -2544,7 +2700,7 @@ namespace M17N.Input
          pos = preedit.Length;
        if (pos != cursor_pos)
          {
-           cursor_pos = pos;
+           set_cursor ("move", pos);
            changed |= ChangedStatus.Preedit;
          }
       }
@@ -2594,9 +2750,12 @@ namespace M17N.Input
 
       internal void commit ()
       {
-       produced.Cat (preedit);
-       preedit.Del ();
-       changed |= ChangedStatus.Preedit;
+       if (preedit.Length > 0)
+         {
+           Candidates.Detach (this);
+           produced.Cat (preedit);
+           preedit_replace (0, preedit.Length, null, null);
+         }
       }
 
       internal void shift (State state)
@@ -2608,7 +2767,7 @@ namespace M17N.Input
            state = prev_state;
          }
 
-       if (state == im.initial_state)
+       if (state == initial_state)
          {
            commit ();
            keys.keyseq.RemoveRange (0, key_head);
@@ -2628,9 +2787,10 @@ namespace M17N.Input
            prev_state = this.state;
          }
        save_state ();
-       if (this.state.title != state.title)
+       if (this.state == null || this.state.title != state.title)
          this.changed |= ChangedStatus.StateTitle;
        this.state = state;
+       keymap = state.keymap;
       }
 
       public Context (MInputMethod im)
@@ -2639,7 +2799,8 @@ namespace M17N.Input
            && ! im.Open ())
          throw new Exception ("Openging " + im.tag + " failed");
        this.im = im;
-       domain = new Xex.Domain (im.domain, this);
+       domain = new Xex.Domain ("context", im.domain, this);
+       initial_state = (State) im.states.Val;
        state_initial_var_values = domain.SaveValues ();
        reset ();
        active = true;
@@ -2684,21 +2845,20 @@ namespace M17N.Input
        domain.RestoreValues (state_var_values);
        preedit.Del ();
        preedit.Ins (0, state_preedit);
-       key_head = state_key_head;
-       cursor_pos = state_pos;
+       set_cursor ("restore", state_pos);
       }
 
       private bool handle_key ()
       {
-       State current_state = state;
+       Console.WriteLine ("{0}:key='{1}'", state.name, keys.keyseq[key_head]);
        Keymap sub = keymap.Lookup (keys, ref key_head);
 
        if (sub != keymap)
          {
+           restore_state ();
            keymap = sub;
            if (keymap.map_actions != null)
              {
-               restore_state ();
                if (! take_actions (keymap.map_actions))
                  return false;
              }
@@ -2706,7 +2866,7 @@ namespace M17N.Input
              {
                for (int i = state_key_head; i < key_head; i++)
                  preedit_replace (cursor_pos, cursor_pos,
-                                  keys.keyseq[i].ToChar ());
+                                  keys.keyseq[i].ToChar (), null);
              }
            if (keymap.submaps == null)
              {
@@ -2721,6 +2881,8 @@ namespace M17N.Input
          }
        else
          {
+           State current_state = state;
+
            if (keymap.branch_actions != null)
              {
                if (! take_actions (keymap.branch_actions))
@@ -2728,13 +2890,13 @@ namespace M17N.Input
              }
            if (state == current_state)
              {
-               if (state == im.initial_state
+               if (state == 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);
+                 shift (initial_state);
              }
          }
        return true;
@@ -2752,11 +2914,8 @@ namespace M17N.Input
        return key_unhandled;
       }
 
-      public bool Produced (out MText mt)
-      {
-       mt = produced;
-       return (produced.Length > 0);
-      }
+      public MText Preedit { get { return preedit; } }
+      public MText Produced { get { return produced; } }
 
       // Return value:
       //   true: All keys are handled and there's no text to commit.
@@ -2790,10 +2949,11 @@ namespace M17N.Input
                key_unhandled = true;
                break;
              }
-           if (++count == 100)
+           if (++count == 10)
              break;
          }
        keys.keyseq.RemoveRange (0, key_head);
+       key_head = 0;
 
        if ((changed & ChangedStatus.Preedit) != ChangedStatus.None
            && PreeditChanged != null)
@@ -2812,9 +2972,109 @@ namespace M17N.Input
          {
            CandidateChanged (this, callback_arg);
          }
-
        return (! key_unhandled && produced.Length == 0);
       }
+
+      public bool Filter ()
+      {
+       changed = ChangedStatus.None;
+       produced.Del ();
+       preceding_text.Del ();
+       following_text.Del ();
+
+       commit ();
+       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);
+         }
+       return (produced.Length == 0);
+      }
+    }
+
+    public class Session
+    {
+      Context ic;
+      MText mt;
+      int pos;
+
+      public Session (MInputMethod im, MText mt, int pos)
+      {
+       ic = new Context (im);
+       this.mt = mt;
+       this.pos = pos;
+       ic.GetSurroundingText = get_surrounding_text;
+       ic.DelSurroundingText = del_surrounding_text;
+      }
+
+      private bool get_surrounding_text (Context ic, MPlist args)
+      {
+       int len = args.Integer;
+       if (len < 0)
+         args.Set (MSymbol.mtext, mt[0, pos]);
+       else
+         args.Set (MSymbol.mtext, mt[pos, mt.Length]);
+       return true;
+      }
+
+      private bool del_surrounding_text (Context ic, MPlist args)
+      {
+       int pos = this.pos + args.Integer;
+       Console.WriteLine ("del-surround: {0}-{1}", this.pos, pos);
+       if (pos < this.pos)
+         {
+           mt.Del (pos, this.pos);
+           this.pos = pos;
+         }
+       else
+         mt.Del (this.pos, pos);
+       return true;
+      }
+
+      public bool HandleKey (ref Key key)
+      {
+       if (! ic.Filter (key))
+         {
+           MText produced = ic.Produced;
+           mt.Ins (pos, produced);
+           pos += produced.Length;
+           Key unhandled;
+           if (ic.UnhandledKey (out unhandled))
+             {
+               key = unhandled;
+               return false;
+             }
+         }
+       return true;
+      }
+
+      public bool Close ()
+      {
+       bool result = ic.Filter ();
+       if (! result)
+         {
+           mt.Ins (pos, ic.Produced);
+           pos += ic.Produced.Length;
+         }
+       ic = null;
+       mt = null;
+       return result;
+      }
+
+      public int CurrentPos { get { return pos; } set { pos = value; } }
+      public MText Preedit { get { return ic.Preedit; } }
     }
   }
 }