+ public int PrevGroup ()
+ {
+ int nitems;
+ int col = Column;
+
+ if (IsFixed)
+ {
+ nitems = group.Length;
+ if ((index -= col + nitems) < 0)
+ index = (Total / nitems) * nitems;
+ nitems = fill_group (index);
+ }
+ else
+ {
+ row = row > 0 ? row-- : blocks.Length - 1;
+ nitems = blocks[row].Count;
+ index = blocks[row].Index;
+ }
+ index += col < nitems ? col : nitems - 1;
+ return nitems;
+ }
+
+ public int NextGroup ()
+ {
+ int nitems;
+ int col = Column;
+
+ if (IsFixed)
+ {
+ nitems = group.Length;
+ if ((index += nitems - col) >= Total)
+ index = 0;
+ nitems = fill_group (index);
+ }
+ else
+ {
+ row = row < blocks.Length - 1 ? row + 1 : 0;
+ nitems = blocks[row].Count;
+ index = blocks[row].Count;
+ }
+ index += col < nitems ? col : nitems - 1;
+ return nitems;
+ }
+
+ public void Prev ()
+ {
+ int col = Column;
+
+ if (col == 0)
+ {
+ int nitems = PrevGroup ();
+ index += col < nitems - 1 ? col : nitems - 1;
+ }
+ else
+ index--;
+ }
+
+ public void Next ()
+ {
+ int col = Column;
+ int nitems = GroupLength;
+
+ if (col == nitems - 1)
+ {
+ nitems = NextGroup ();
+ index -= Column;
+ }
+ else
+ index++;
+ }
+
+ public void First ()
+ {
+ index -= Column;
+ }
+
+ public void Last ()
+ {
+ index += GroupLength - (Column + 1);
+ }
+
+ public void Select (int col)
+ {
+ int maxcol = GroupLength - 1;
+ if (col > maxcol)
+ col = maxcol;
+ index = index - Column + col;
+ }
+ }
+
+ internal abstract class Selector : Xex.TermValue
+ {
+ private Selector () { }
+
+ public abstract void Select (Candidates candidates);
+
+ public static Xex.TermValue parser (Xex.Domain domain, XmlNode node)
+ {
+ MSymbol name = node.InnerText;
+ Predefined pred;
+
+ if (predefined_selectors.TryGetValue (name, out pred))
+ return pred;
+ if (name.Name[0] == '@')
+ throw new Exception ("Invalid selector name: " + name);
+ int index;
+ if (! Int32.TryParse (node.InnerText, out index))
+ throw new Exception ("Invalid selector name: " + name);
+ return new Numbered (index);
+ }
+
+ public override Xex.TermValue Clone () { return this; }
+
+ public class Numbered : Selector
+ {
+ int index;
+
+ public Numbered (int index) { this.index = index; }
+
+ public override void Select (Candidates can) { can.Select (index); }
+ }
+
+ public class Predefined : Selector
+ {
+ private char tag;
+
+ internal Predefined (MSymbol sym) { this.tag = sym.Name[1]; }
+
+ public override void Select (Candidates can)
+ {
+ switch (tag)
+ {
+ case '<': candidates.First (); break;
+ case '>': candidates.Last (); break;
+ case '-': candidates.Prev (); break;
+ case '+': candidates.Next (); break;
+ case '[': candidates.PrevGroup (); break;
+ case ']': candidates.NextGroup (); break;
+ default: break;
+ }
+ }
+ }
+
+ static new Dictionary<MSymbol, Predefined> predefined_selectors;
+
+ static Selector ()
+ {
+ predefined_selectors = new Dictionary<MSymbol, Predefined> ();
+ MSymbol[] symlist = new MSymbol[] { "@<", "@=", "@>", "@-", "@+",
+ "@[", "@]" };
+ foreach (MSymbol s in symlist)
+ predefined_selectors[s] = new Predefined (s);
+ }
+ }
+
+ internal class Map
+ {
+ public MSymbol name;
+ public Dictionary<Key, Map> submaps;
+ public Xex.Term actions;
+
+ public void Add (KeySeq keys, int index, Xex.Term actions)
+ {
+ Map sub = null;
+
+ if (submaps == null)
+ submaps = new Dictionary<Key, Map> ();
+ else
+ submaps.TryGetValue (keys.keyseq[index], out sub);
+ if (sub == null)
+ {
+ Key key = keys.keyseq[index];
+ submaps[key] = sub = new Map ();
+ }
+ if (index + 1 < keys.keyseq.Count)
+ sub.Add (keys, index + 1, actions);
+ else
+ this.actions = actions;
+ }
+
+ public Xex.Term Lookup (KeySeq keys, int index)
+ {
+ Map sub;
+
+ if (index + 1 == keys.keyseq.Count)
+ return actions;
+ if (submaps.TryGetValue (keys.keyseq[index], out sub))
+ return sub.Lookup (keys, index + 1);
+ return Tnil;
+ }
+
+ private void describe (MText mt, KeySeq keyseq)
+ {
+ if (keyseq.keyseq.Count > 0)
+ {
+ mt.Cat (" (").Cat (keyseq.ToString ());
+ mt.Cat (' ').Cat (actions.ToString ());
+ mt.Cat (')');
+ }
+ if (submaps != null)
+ foreach (KeyValuePair<Key, Map> kv in submaps)
+ {
+ keyseq.keyseq.Add (kv.Key);
+ kv.Value.describe (mt, keyseq);
+ keyseq.keyseq.RemoveAt (keyseq.keyseq.Count - 1);
+ }
+ }
+
+ public override string ToString ()
+ {
+ MText mt = "(" + name.Name;
+ KeySeq keyseq = new KeySeq ();
+
+ describe (mt, keyseq);
+ mt.Cat (')');
+ return (string) mt;
+ }
+ }
+
+ internal class State
+ {
+ public MSymbol name;
+ public MText title;
+ public MPlist branches = new MPlist ();
+
+ public State (MSymbol name)
+ {
+ this.name = name;
+ }
+
+ public override string ToString ()
+ {
+ MText mt = "(" + name.Name;
+
+ if (title != null)
+ mt.Cat (" \"" + title + "\"");
+ for (MPlist p = branches; ! p.IsEmpty; p = p.next)
+ mt.Cat (" (" + p.Key + " " + (Xex) p.Val + ")");
+ return (string) mt + ")";
+ }
+ }
+
+ // Instance members
+ internal Xex.Domain domain = new Xex.Domain (im_domain, null);
+
+ private LoadStatus load_status = LoadStatus.None;
+ private MDatabase.Tag tag;
+ private MDatabase mdb;
+
+ private MText description;
+ internal MText title;
+ internal Command[] commands;
+ internal Xex.Symbol[] var_names;
+ internal Dictionary<MSymbol, Plugin> plugins;
+ internal Dictionary<MSymbol, Map> maps;
+ internal MPlist states;
+
+ static MInputMethod ()
+ {
+ im_domain.DefTerm ("keyseq", KeySeq.parser);
+ im_domain.DefTerm ("marker", Marker.parser);
+ im_domain.DefTerm ("selector", Selector.parser);
+
+ im_domain.DefSubr (Finsert, "insert", true, 1, 1);
+ im_domain.DefSubr (Finsert_candidates, "candidates", true, 1, -1);
+ im_domain.DefSubr (Fdelete, "delete", true, 1, 1);
+ im_domain.DefSubr (Fselect, "select", true, 1, 1);
+ im_domain.DefSubr (Fshow, "show", true, 0, 0);
+ im_domain.DefSubr (Fhide, "hide", true, 0, 0);
+ im_domain.DefSubr (Fmove, "move", true, 1, 1);
+ im_domain.DefSubr (Fmark, "mark", true, 1, 1);
+ im_domain.DefSubr (Fpushback, "pushback", true, 1, 1);
+ im_domain.DefSubr (Fpop, "pop", true, 0, 0);
+ im_domain.DefSubr (Fundo, "undo", true, 0, 1);
+ im_domain.DefSubr (Fcommit, "commit", true, 0, 0);
+ im_domain.DefSubr (Funhandle, "unhandle", true, 0, 0);
+ im_domain.DefSubr (Fshift, "shift", true, 1, 1);
+ im_domain.DefSubr (Fmarker, "marker", true, 1, 1);
+ im_domain.DefSubr (Fchar_at, "char-at", true, 1, 1);
+
+ MDatabase.Tag tag = new MDatabase.Tag (Minput_method, "*", "*", "*");
+ List<MDatabase> list = MDatabase.List (tag);