*** empty log message ***
[m17n/m17n-lib-cs.git] / MInputMethod.cs
index 9e180d6..3f80c2f 100644 (file)
@@ -19,12 +19,9 @@ namespace M17N.Input
     public delegate bool Callback (Context ic, MPlist args);
 
     // Class members
-    public static Callback PreeditStart, PreeditDone, PreeditDraw;
-    public static Callback StatusStart, StatusDone, StatusDraw;
-    public static Callback CandidateStart, CandidateDone, CandidateDraw;
-    public static Callback SetSpot;
-    public static Callback Toggle;
-    public static Callback Reset;
+    public static event Callback PreeditChanged;
+    public static event Callback StatusChanged;
+    public static event Callback CandidateChanged;
     public static Callback GetSurroundingText;
     public static Callback DeleteSurroundingText;
 
@@ -42,10 +39,6 @@ namespace M17N.Input
     private static MSymbol Mstate = "state";
     internal static MSymbol Mcandidates = "candidates";
 
-
-    private static MSymbol Mget_surrounding_text = "get-surrounding-text";
-    private static MSymbol Mdel_surrounding_text = "del-surrounding-text";
-
     private static Xex.Symbol Qmap = "map";
     private static Xex.Symbol Qrule = "rule";
     private static Xex.Symbol Qkeyseq = "keyseq";
@@ -75,6 +68,7 @@ namespace M17N.Input
     private static Xex.Symbol Qtitle = "title";
     private static Xex.Symbol Qeq = "=";
     private static Xex.Symbol Qeqeq = "==";
+    private static Xex.Symbol Qcandidates_group_size = "candidates-group-size";
 
     private static Xex.Term Tnil = new Xex.Term ((Xex.Symbol) "nil");
     private static Xex.Term Tcatch_tag = new Xex.Term ((Xex.Symbol) "@mimtag");
@@ -85,7 +79,7 @@ namespace M17N.Input
     internal static MInputMethod im_global = null;
 
     [FlagsAttribute]
-    private enum LoadStatus
+    protected enum LoadStatus
     {
       None =   0x00,
       Header = 0x01,
@@ -99,11 +93,13 @@ namespace M17N.Input
     {
       None =           0x00,
       StateTitle =     0x01,
-      Preedit   =      0x02,
+      PreeditText =    0x02,
       CursorPos =      0x04,
       CandidateList =  0x08,
       CandidateIndex = 0x10,
       CandidateShow =  0x20,
+      Preedit =                PreeditText | CursorPos,
+      Candidate =      CandidateList | CandidateIndex | CandidateShow,
     }
 
     private static ChangedStatus CandidateAll = (ChangedStatus.CandidateList
@@ -804,13 +800,13 @@ namespace M17N.Input
          group = new object[column];
       }
 
-      public Candidates (List<Xex.Term> list, int column)
+      public Candidates (Xex.Term[] candidates, int column)
       {
-       int nblocks = list.Count;
+       int nblocks = candidates.Length;
 
        blocks = new Block[nblocks];
        for (int i = 0, start = 0; i < nblocks; i++)
-         start += (blocks[i] = new Block (index, list[i])).Count;
+         start += (blocks[i] = new Block (index, candidates[i])).Count;
        if (column > 0)
          group = new object[column];
       }
@@ -1103,7 +1099,7 @@ namespace M17N.Input
            if (branch_actions != null)
              foreach (Xex.Term term in branch_actions)
                mt.Cat (' ').Cat (term.ToString ());
-           mt.Cat (')');           
+           mt.Cat (')');
          }
        if (submaps != null)
          foreach (KeyValuePair<Key, Keymap> kv in submaps)
@@ -1120,7 +1116,6 @@ namespace M17N.Input
        KeySeq keyseq = new KeySeq ();
 
        describe (mt, keyseq);
-       mt.Cat (')');
        return (string) mt;
       }
     }
@@ -1144,6 +1139,8 @@ namespace M17N.Input
        XmlAttribute attr = node.Attributes[Qtitle];
        if (attr != null)
          title = (MText) attr.Value;
+       else
+         title = im.title;
        keymap = new Keymap ();
        for (node = node.FirstChild; node != null; node = node.NextSibling)
          {
@@ -1172,9 +1169,11 @@ namespace M17N.Input
        plist = plist.next;
        if (plist.IsMText)
          {
-           this.title = plist.Text;
+           title = plist.Text;
            plist = plist.next;
          }
+       else
+         title = im.title;
        keymap = new Keymap (); 
        for (; ! plist.IsEmpty; plist = plist.next)
          {
@@ -1213,8 +1212,8 @@ namespace M17N.Input
     // Instance members
     internal Xex.Domain domain = new Xex.Domain (im_domain, null);
 
-    private LoadStatus load_status = LoadStatus.None;
-    private MDatabase.Tag tag;
+    protected LoadStatus load_status = LoadStatus.None;
+    protected MDatabase.Tag tag;
     private MDatabase mdb;
 
     private MText description;
@@ -1314,7 +1313,7 @@ namespace M17N.Input
       return (im_table.TryGetValue (tag, out im) ? im : null);
     }
 
-    public bool Open ()
+    private bool Open ()
     {
       return ((load_status == LoadStatus.Full) || load_body ());
     }
@@ -1378,7 +1377,7 @@ namespace M17N.Input
     private void add_default_state ()
     {
       Xex.Symbol Qinit = "init";
-      State state = new State (Qinit, null);
+      State state = new State (Qinit, title);
       foreach (KeyValuePair<MSymbol, Map>kv in maps)
        state.keymap.AddMap (kv.Value, null);
       states[Qinit] = initial_state = state;
@@ -1765,6 +1764,8 @@ namespace M17N.Input
          {
            State state = new State (this, plist.Plist);
            states[state.name] = state;     
+           if (initial_state == null)
+             initial_state = state;
          }
     }
 
@@ -1775,6 +1776,8 @@ namespace M17N.Input
          {
            State state = new State (this, node);
            states[state.name] = state;
+           if (initial_state == null)
+             initial_state = state;
          }
     }
 
@@ -2071,7 +2074,10 @@ namespace M17N.Input
     private static Xex.Term Finsert (Xex.Domain domain, Xex.Variable vari,
                                     Xex.Term[] args)
     {
-      ((Context) domain.context).insert (args[0]);
+      if (args[0].IsInt)
+       ((Context) domain.context).insert (args[0].Intval);
+      else
+       ((Context) domain.context).insert (args[0].Strval);
       return args[0];
     }
 
@@ -2079,7 +2085,11 @@ namespace M17N.Input
                                                Xex.Variable vari,
                                                Xex.Term[] args)
     {
-      ((Context) domain.context).insert_candidates (args[0]);
+      Context ic = (Context) domain.context;
+      Xex.Variable v = ic.domain.GetVar (Qcandidates_group_size, false);
+      int column = (v == null ? 0 : v.Value.Intval);
+
+      ic.insert_candidates (new Candidates (args, column));
       return args[0];
     }
 
@@ -2089,14 +2099,23 @@ namespace M17N.Input
       Context ic = (Context) domain.context;
       Marker m = (Marker) args[0].Objval;
 
-      return new Xex.Term (ic.char_at (m.Position (ic)));
+      return new Xex.Term (m.CharAt (ic));
     }
 
     private static Xex.Term Fdelete (Xex.Domain domain, Xex.Variable vari,
-                                  Xex.Term[] args)
+                                    Xex.Term[] args)
     {
-      ((Context) domain.context).delete ((int) args[0].Intval);
-      return args[0];
+      Context ic = (Context) domain.context;
+      int pos;
+
+      if (args[0].IsInt)
+       pos = args[0].Intval;
+      else
+       {
+         Marker m = (Marker) args[0].Objval;
+         pos = m.Position (ic);
+       }
+      return new Xex.Term (ic.delete (pos));
     }
 
     private static Xex.Term Fselect (Xex.Domain domain, Xex.Variable vari,
@@ -2110,30 +2129,26 @@ namespace M17N.Input
     }
 
     private static Xex.Term Fshow (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+                                  Xex.Term[] args)
     {
       ((Context) domain.context).show ();
       return Tnil;
     }
 
     private static Xex.Term Fhide (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+                                  Xex.Term[] args)
     {
       ((Context) domain.context).hide ();
       return Tnil;
     }
 
     private static Xex.Term Fmove (Xex.Domain domain, Xex.Variable vari,
-                                Xex.Term[] args)
+                                  Xex.Term[] args)
     {
-      if (args[0].IsInt)
-       ((Context) domain.context).move (args[0].Intval);
-      else
-       {
-         Marker m = (Marker) args[0].Objval;
-         Context ic = (Context) domain.context;
-         ((Context) domain.context).move (m.Position (ic));
-       }
+      Context ic = (Context) domain.context;
+      int pos = (args[0].IsInt ? args[0].Intval
+                : ((Marker) args[0].Objval).Position (ic));
+      ic.move (pos);
       return args[0];
     }
 
@@ -2219,11 +2234,12 @@ namespace M17N.Input
                                               Xex.Variable vari,
                                               Xex.Term[] args)
     {
-      return new Xex.Term (((Context) domain.context).SurroundingFlag);
+      return new Xex.Term (GetSurroundingText == null ? 0 : 1);
     }
 
     public override string ToString ()
     {
+      this.Open ();
       string str = (String.Format ("({0} (title \"{1}\")", tag, title));
       if (commands != null)
        {
@@ -2259,39 +2275,35 @@ namespace M17N.Input
 
     public class Context
     {
-      internal static Xex.Symbol Qcandidates_group_size
-        = "candidates-group-size";
       internal MInputMethod im;
-      private Dictionary<MSymbol, Callback> callbacks
-       = new Dictionary<MSymbol, Callback> ();
-
-      private MText produced;
+      internal Xex.Domain domain;
       private bool active;
+
       private MText status;
-      internal MText preedit;
+      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 Candidates candidates;
       private int candidate_from, candidate_to;
       private bool candidate_show;
       public bool CandidateShow { get { return candidate_show; } }
 
-      private List<State> state_list = new List<State> ();
-      private Keymap keymap;
+      private State state, prev_state;
+      private MText state_preedit = new MText ();
+      private int state_key_head;
+      private object state_var_values, state_initial_var_values;
+      private int state_pos;
 
+      private Keymap keymap;
       // Sequence of input keys.
       internal KeySeq keys = new KeySeq ();
-
       // Index into KEYS specifying the next key to handle.
       internal int key_head;
 
-
-      private int state_key_head;
-      private object state_var_values;
       private int commit_key_head;
-      private MText state_preedit;
-      private int state_pos;
       internal MText preceding_text = new MText ();
       internal MText following_text = new MText ();
 
@@ -2303,15 +2315,39 @@ namespace M17N.Input
       // 'key_unhandled' is true.
       private Key unhandled_key;
 
-      internal Xex.Domain domain;
-
       internal ChangedStatus changed;
 
-      static MPlist callback_arg = new MPlist ();
+      internal void reset ()
+      {
+       status = im.initial_state.title;
+       produced.Del ();
+       preedit.Del ();
+
+       cursor_pos = 0;
+       marker_positions.Clear ();
+       candidates = null;
+       candidate_show = false;
+
+       state = im.initial_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;
+
+       preceding_text.Del ();
+       following_text.Del ();
+
+       changed = ChangedStatus.None;
+      }
 
       static Xex.Term[] catch_args = new Xex.Term[2];
 
-      private bool take_action (Xex.Term[] actions)
+      private bool take_actions (Xex.Term[] actions)
       {
        catch_args[0] = Tcatch_tag;
        catch_args[1]= new Xex.Term (domain, Qprogn, actions);
@@ -2320,22 +2356,17 @@ namespace M17N.Input
        return (! term.IsSymbol || term.Symval != Tcatch_tag.Symval);
       }
 
-
-      private bool call_callback (MSymbol name, MPlist arg)
-      {
-       Callback callback;
-       if (! callbacks.TryGetValue (name, out callback))
-         return false;
-       return callback (this, arg);
-      }
+      static MPlist callback_arg = new MPlist ();
 
       private bool get_surrounding_text (int len)
       {
        if (len < 0 ? -len <= preceding_text.Length
            : len <= following_text.Length)
          return true;
+       if (GetSurroundingText == null)
+         return false;
        callback_arg.Set (MSymbol.integer, len);
-       if (! call_callback (Mget_surrounding_text, callback_arg)
+       if (! GetSurroundingText (this, callback_arg)
            || ! callback_arg.IsMText)
          return false;
        if (len < 0)
@@ -2347,11 +2378,6 @@ namespace M17N.Input
        return (len <= following_text.Length);
       }
 
-      internal int SurroundingFlag
-      {
-       get { return (callbacks.ContainsKey (Mget_surrounding_text) ? 1 : 0); }
-      }
-
       internal int GetSurroundingChar (int pos)
       {
        if (! get_surrounding_text (pos < 0 ? pos : pos + 1))
@@ -2398,12 +2424,15 @@ namespace M17N.Input
        adjust_markers (from, to, mt);
       }
 
-      internal void insert (Xex.Term arg)
+      internal void insert (int c)
       {
-       if (arg.IsInt)
-         preedit_replace (cursor_pos, cursor_pos, arg.Intval);
-       else
-         preedit_replace (cursor_pos, cursor_pos, new MText (arg.Strval));
+       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;
       }
 
@@ -2428,14 +2457,9 @@ namespace M17N.Input
                    | CandidateAll);
       }
 
-      internal void insert_candidates (Xex.Term arg)
+      internal void insert_candidates (Candidates candidates)
       {
-       int column = 0;
-       Xex.Variable v = domain.GetVar (Qcandidates_group_size, false);
-
-       if (v != null)
-         column = v.Value.Intval;
-       candidates = new Candidates (arg.Listval, column);
+       this.candidates = candidates;
        candidate_from = candidate_to = cursor_pos;
        update_candidate ();
       }
@@ -2449,53 +2473,55 @@ namespace M17N.Input
          }
       }
 
-      internal int char_at (int pos)
+      internal int delete (int pos)
       {
-       int c;
+       int deleted = pos - cursor_pos;
 
-       pos += cursor_pos;
-       if (pos < 0)
+       if (pos < cursor_pos)
          {
-           if (preceding_text.Length < -pos)
+           if (pos < 0)
              {
-               MPlist plist = new MPlist ();
-               plist.Push (MSymbol.integer, pos);
-               if (GetSurroundingText != null
-                   && GetSurroundingText (this, plist)
-                   && plist.IsMText
-                   && preceding_text.Length < plist.Text.Length)
-                 preceding_text = plist.Text;
+               if (DeleteSurroundingText != null)
+                 {
+                   callback_arg.Set (MSymbol.integer, pos);
+                   if (DeleteSurroundingText (this, callback_arg))
+                     {
+                       if (callback_arg.IsInteger)
+                         deleted = callback_arg.Integer - cursor_pos;
+                       preceding_text.Del ();
+                     }
+                   else
+                     deleted = - cursor_pos;
+                 }
+               pos = 0;
              }
-           c = (-pos < preceding_text.Length
-                ? preceding_text[preceding_text.Length + pos] : -1);
+           if (pos < cursor_pos)
+             preedit_replace (pos, cursor_pos, null);
          }
-       else if (pos >= 0 && pos < preedit.Length)
-         c = preedit[pos];
        else
          {
-           pos -= preedit.Length;
-           if (pos >= following_text.Length)
+           if (pos > preedit.Length)
              {
-               MPlist plist = new MPlist ();
-               plist.Push (MSymbol.integer, pos + 1);
-               if (GetSurroundingText != null
-                   && GetSurroundingText (this, plist)
-                   && plist.IsMText
-                   && following_text.Length < plist.Text.Length)
-                 following_text = plist.Text;
+               if (DeleteSurroundingText != null)
+                 {
+                   callback_arg.Set (MSymbol.integer, pos - preedit.Length);
+                   if (DeleteSurroundingText (this, callback_arg))
+                     {
+                       if (callback_arg.IsInteger)
+                         deleted = callback_arg.Integer - cursor_pos;
+                       preceding_text.Del ();
+                     }
+                   else
+                     deleted = preedit.Length - cursor_pos;
+                 }
+               pos = preedit.Length;
              }
-           c = (pos < following_text.Length ? following_text[pos] : -1);
+           if (pos > cursor_pos)
+             preedit_replace (cursor_pos, pos, null);
          }
-       return c;
-      }
-
-      internal void delete (int pos)
-      {
-       if (pos < cursor_pos)
-         preedit_replace (pos, cursor_pos, null);
-       else
-         preedit_replace (cursor_pos, pos, null);
-       changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
+       if (deleted != 0)
+         changed |= ChangedStatus.Preedit | ChangedStatus.CursorPos;
+       return deleted;
       }
 
       internal void show ()
@@ -2575,74 +2601,62 @@ namespace M17N.Input
 
       internal void shift (State state)
       {
-       bool changed;
-
        if (state == null)
          {
-           if (state_list.Count > 1)
-             state_list.RemoveAt (state_list.Count - 1);
-           state = state_list[state_list.Count - 1];
-           changed = true;
-         }
-       else
-         {
-           changed = state != state_list[state_list.Count - 1];
-           if (changed)
-             state_list.Add (state);
+           if (prev_state == null)
+             return;
+           state = prev_state;
          }
-       if (state_list.Count == 1)
+
+       if (state == im.initial_state)
          {
            commit ();
-           reset ();
-         }
-       else
-         {
-           state_key_head = key_head;
-           state_pos = cursor_pos;
-           state_preedit = preedit.Dup ();
-           state_var_values = domain.SaveValues ();
-           if (changed)
+           keys.keyseq.RemoveRange (0, key_head);
+           key_head = 0;
+           if (state != this.state)
              {
-               status = state.title;
-               if (status == null)
-                 status = im.title;
-               this.changed |= ChangedStatus.StateTitle;
+               domain.RestoreValues (state_initial_var_values);
                if (state.enter_actions != null)
-                 take_action (state.enter_actions);
+                 take_actions (state.enter_actions);
              }
+           prev_state = null;
          }
-      }
-
-      internal void reset ()
-      {
-       preedit.Del ();
-       state_preedit.Del ();
-       produced.Del ();
-       marker_positions.Clear ();
-       cursor_pos = 0;
-       keys.keyseq.Clear ();
-       key_head = commit_key_head = 0;
-       state_list.Clear ();
-       state_list.Add (im.initial_state);
-       keymap = im.initial_state.keymap;
-       key_head = state_key_head = 0;
-       state_pos = 0;
+       else
+         {
+           if (state != this.state && state.enter_actions != null)
+             take_actions (state.enter_actions);
+           prev_state = this.state;
+         }
+       save_state ();
+       if (this.state.title != state.title)
+         this.changed |= ChangedStatus.StateTitle;
+       this.state = state;
       }
 
       public Context (MInputMethod im)
       {
+       if (im.load_status != LoadStatus.Full
+           && ! im.Open ())
+         throw new Exception ("Openging " + im.tag + " failed");
        this.im = im;
        domain = new Xex.Domain (im.domain, this);
+       state_initial_var_values = domain.SaveValues ();
        reset ();
+       active = true;
+       if (PreeditChanged != null)
+         {
+           callback_arg.Set (MSymbol.mtext, preedit);
+           PreeditChanged (this, callback_arg);
+         }
+       if (StatusChanged != null)
+         {
+           callback_arg.Set (MSymbol.mtext, status);
+           StatusChanged (this, callback_arg);
+         }
       }
 
       public ChangedStatus Changed { get { return changed; } }
 
-      public void AddCallback (MSymbol name, Callback callback)
-      {
-       callbacks[name] = callback;
-      }
-
       internal object GetCandidates (out int column)
       {
        column = 0;
@@ -2656,13 +2670,27 @@ namespace M17N.Input
        return candidates.Current;
       }
 
+      private void save_state ()
+      {
+       state_var_values = domain.SaveValues ();
+       state_preedit.Del ();
+       state_preedit.Ins (0, preedit);
+       state_key_head = key_head;
+       state_pos = cursor_pos;
+      }
+
       private void restore_state ()
       {
+       domain.RestoreValues (state_var_values);
+       preedit.Del ();
+       preedit.Ins (0, state_preedit);
+       key_head = state_key_head;
+       cursor_pos = state_pos;
       }
 
       private bool handle_key ()
       {
-       State state = state_list[state_list.Count - 1];
+       State current_state = state;
        Keymap sub = keymap.Lookup (keys, ref key_head);
 
        if (sub != keymap)
@@ -2671,7 +2699,7 @@ namespace M17N.Input
            if (keymap.map_actions != null)
              {
                restore_state ();
-               if (! take_action (keymap.map_actions))
+               if (! take_actions (keymap.map_actions))
                  return false;
              }
            else if (keymap.submaps != null)
@@ -2684,7 +2712,7 @@ namespace M17N.Input
              {
                if (keymap.branch_actions != null)
                  {
-                   if (! take_action (keymap.branch_actions))
+                   if (! take_actions (keymap.branch_actions))
                      return false;
                  }
                if (keymap != state.keymap)
@@ -2695,10 +2723,10 @@ namespace M17N.Input
          {
            if (keymap.branch_actions != null)
              {
-               if (! take_action (keymap.branch_actions))
+               if (! take_actions (keymap.branch_actions))
                  return false;
              }
-           if (state == state_list[state_list.Count - 1])
+           if (state == current_state)
              {
                if (state == im.initial_state
                    && key_head < keys.keyseq.Count)
@@ -2732,11 +2760,18 @@ namespace M17N.Input
 
       // Return value:
       //   true: All keys are handled and there's no text to commit.
-      //   false: Some key is unhandled or there's a text to commit.
-      //      The caller should use methods UnhandledKey and Produced.
+      //   false: Some key is left unhandled or there's a text to
+      //      commit.  The caller should refer to UnhandledKey and
+      //      Produced.
 
       public bool Filter (Key key)
       {
+       if (! active)
+         {
+           key_unhandled = true;
+           unhandled_key = key;
+           return false;
+         }
        if (key == Key.Reload)
          return true;
        changed = ChangedStatus.None;
@@ -2759,6 +2794,25 @@ namespace M17N.Input
              break;
          }
        keys.keyseq.RemoveRange (0, key_head);
+
+       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 (! key_unhandled && produced.Length == 0);
       }
     }