+ var b = this.blocks[this.row];
+ if (this.column > 0)
+ {
+ if (this.index + 1 < this.total)
+ {
+ this.index += this.column - get_col.call (this) + 1;
+ while (b.Index + b.Count () <= this.index)
+ b = this.blocks[++this.row];
+ }
+ }
+ else
+ this.index = b.Index + b.Count () - 1;
+ }
+
+ MIM.Candidates.prototype.Current = function ()
+ {
+ var b = this.blocks[this.row];
+ return b.get (this.index - b.Index);
+ }
+
+ MIM.Candidates.prototype.Select = function (selector)
+ {
+ var idx = this.index;
+ var gidx = this.column > 0 ? idx / this.column : this.row;
+ if (selector.type == 'selector')
+ {
+ switch (selector.val)
+ {
+ case '@<': first.call (this); break;
+ case '@>': last.call (this); break;
+ case '@-': prev.call (this); break;
+ case '@+': next.call (this); break;
+ case '@[': prev_group.call (this); break;
+ case '@]': next_group.call (this); break;
+ default: break;
+ }
+ }
+ else
+ {
+ var col, start, end
+ if (this.column > 0)
+ {
+ col = this.index % this.column;
+ start = this.index - col;
+ end = start + this.column;
+ }
+ else
+ {
+ start = this.blocks[this.row].Index;
+ col = this.index - start;
+ end = start + this.blocks[this.row].Count;
+ }
+ if (end > this.total)
+ end = this.total;
+ this.index += selector.val - col;
+ if (this.index >= end)
+ this.index = end - 1;
+ if (this.column > 0)
+ {
+ if (selector.val > col)
+ while (this.blocks[this.row].Index + this.blocks[this.row].Count
+ < this.index)
+ this.row++;
+ else
+ while (this.blocks[this.row].Index > this.index)
+ this.row--;
+ }
+ }
+ var newgidx = this.column > 0 ? this.index / this.column : this.row;
+ if (this.index != idx)
+ this.ic.changed |= (gidx == newgidx
+ ? MIM.ChangedStatus.CandidateIndex
+ : MIM.ChangedStatus.CandidateList);
+ return this.Current ();
+ }
+
+ MIM.Candidates.prototype.CurrentCol = function ()
+ {
+ return get_col.call (this);
+ }
+
+ MIM.Candidates.prototype.CurrentGroup = function ()
+ {
+ var col, start, end, gnum, gidx;
+ if (this.column > 0)
+ {
+ gnum = Math.floor ((this.total - 1) / this.column) + 1;
+ col = this.index % this.column;
+ start = this.index - col;
+ gidx = start / this.column + 1;
+ end = start + this.column;
+ if (end > this.total)
+ end = this.total;
+ }
+ else
+ {
+ gnum = this.blocks.length;
+ gidx = this.row + 1;
+ start = this.blocks[this.row].Index;
+ col = this.index - start;
+ end = start + this.blocks[this.row].Count ();
+ }
+ var group = new Array ();
+ var indices = new Array (gnum, gidx, col);
+ group.push (indices);
+ var row = this.row;
+ var block = this.blocks[row++];
+ while (start < end)
+ {
+ var c = block.get (start - block.Index);
+ group.push (c);
+ start++;
+ if (start == block.Index + block.Count ())
+ block = this.blocks[row++];
+ }
+ return group;
+ }
+}) ();
+
+MIM.im_domain = new Xex.Domain ('input-method', null, null);
+MIM.im_domain.DefType (MIM.KeySeq.prototype);
+MIM.im_domain.DefType (MIM.Marker.prototype);
+MIM.im_domain.DefType (MIM.Selector.prototype);
+MIM.im_domain.DefType (MIM.Rule.prototype);
+MIM.im_domain.DefType (MIM.Map.prototype);
+MIM.im_domain.DefType (MIM.State.prototype);
+
+(function () {
+ var im_domain = MIM.im_domain;
+
+ function Finsert (domain, vari, args)
+ {
+ var text;
+ if (args[0].type == 'integer')
+ text = String.fromCharCode (args[0].val);
+ else
+ text = args[0].val;
+ domain.context.ins (text, null);
+ return args[0];
+ }
+
+ function Finsert_candidates (domain, vari, args)
+ {
+ var ic = domain.context;
+ var gsize = domain.variables['candidates-group-size'];
+ var candidates = new MIM.Candidates (ic, args,
+ gsize ? gsize.val.Intval () : 0);
+ ic.ins (candidates.Current (), candidates);
+ return args[0];
+ }
+
+ function Fdelete (domain, vari, args)
+ {
+ var ic = domain.context;
+ var pos = args[0].IsInt ? args[0].Intval () : args[0].Position (ic);
+ return new Xex.IntTerm (ic.del (pos));
+ }
+
+ function Fselect (domain, vari, args)
+ {
+ var ic = domain.context;
+ var can = ic.candidates;
+
+ if (can)
+ {
+ var old_text = can.Current ();
+ var new_text = can.Select (args[0]);
+ ic.rep (old_text, new_text, can);
+ }
+ else
+ Xex.Log ('no candidates at ' + ic.cursor_pos + ' of ' + ic.candidate_table.table.length);
+ return args[0];
+ }
+
+ function Fshow (domain, vari, args)
+ {
+ domain.context.candidate_show = true;
+ domain.context.changed |= MIM.ChangedStatus.CandidateShow;
+ return Xex.nil;
+ }
+
+ function Fhide (domain, vari, args)
+ {
+ domain.context.candidate_show = false;
+ domain.context.changed |= MIM.ChangedStatus.CandidateShow;
+ return Xex.nil;
+ }
+
+ function Fchar_at (domain, vari, args)
+ {
+ return new Xex.IntTerm (args[0].CharAt (domain.context));
+ }
+
+ function Fmove (domain, vari, args)
+ {
+ var ic = domain.context;
+ var pos = args[0].IsInt ? args[0].val : args[0].Position (ic);
+ ic.move (pos);
+ return new Xex.IntTerm (pos);
+ }
+
+ function Fmark (domain, vari, args)
+ {
+ args[0].Mark (domain.context);
+ return args[0];
+ }
+
+ function Fpushback (domain, vari, args)
+ {
+ var a = (args[0].IsInt ? args[0].Intval ()
+ : args[0].IsStr ? new KeySeq (args[0])
+ : args[0]);
+ domain.context.pushback (a);
+ return args[0];
+ }
+
+ function Fpop (domain, vari, args)
+ {
+ var ic = domain.context;
+ if (ic.key_head < ic.keys.val.length)
+ ic.keys.val.splice (ic.keys_head, 1);
+ return Xex.nil;
+ }
+
+ function Fundo (domain, vari, args)
+ {
+ var ic = domain.context;
+ var n = args.length == 0 ? -2 : args[0].val;
+ Xex.Log ('undo with arg ' + args[0]);
+ if (n < 0)
+ ic.keys.val.splice (ic.keys.val.length + n, -n);
+ else
+ ic.keys.val.splice (n, ic.keys.val.length);
+ ic.reset ();
+ return Xex.nil;
+ }
+
+ function Fcommit (domain, vari, args)
+ {
+ domain.context.commit ();
+ return Xex.nil;
+ }
+
+ function Funhandle (domain, vari, args)
+ {
+ domain.context.commit ();
+ return Xex.Fthrow (domain, vari, Xex.CatchTag._mimtag);
+ }
+
+ function Fshift (domain, vari, args)
+ {
+ var ic = domain.context;
+ var state_name = args[0].val;