public class MTextProperty
{
+ private enum MTextPropertyFlagMask
+ {
+ FRONT_STICKY = 1,
+ REAR_STICKY = 2
+ };
+
internal MProperty prop;
- internal bool front_sticky;
- internal bool rear_sticky;
- internal bool merginable;
- public MProperty Prop { get { return prop; } }
- public bool FrontSticky { get { return front_sticky; } }
- public bool RearSticky { get { return rear_sticky; } }
- public bool Merginable { get { return merginable; } }
+ internal byte flags;
+ internal MText mtext;
+ internal from;
+ internal to;
- public MTextProperty (bool front_sticky, bool rear_sticky)
+ public MProperty Prop
+ {
+ get { return prop; }
+ }
+ public bool FrontSticky
+ {
+ get { return (flags & MTextPropertyFlagMask.FRONT_STICKY) != 0; }
+ }
+ public bool RearSticky
+ {
+ get { return (flags & MTextPropertyFlagMask.REAR_STICKY) != 0; }
+ }
+ public MText mtext
{
- this.front_sticky = front_sticky;
- this.rear_sticky = rear_sticky;
+ get { return mtext; }
}
- public MTextProperty (bool front_sticky, bool rear_sticky, bool merginable)
+ public int from
{
- this.front_sticky = front_sticky;
- this.rear_sticky = rear_sticky;
- this.merginable = merginable;
+ get { return from; }
+ }
+ public int to
+ {
+ get { return to; }
+ }
+
+ public MTextProperty (bool front_sticky, bool rear_sticky)
+ {
+ this.flags = ((front_sticky ? MTextPropertyFlagMask.FRONT_STICKY : 0)
+ | (rear_sticky ? MTextPropertyFlagMask.REAR_STICKY : 0));
}
}
public enum MTextFormat format;
#endif
+ private struct KeyIntervalPair
+ {
+ object key;
+ MInterval interval;
+ };
+
private StringBuilder sb;
private int nchars;
private int cache_pos;
private int cache_idx;
- private MInterval root_interval;
+ private stack<KeyIntervalPair> root_intervals;
+ private MProperty default_property;
private bool read_only;
private static UTF8Encoding utf8 = new UTF8Encoding ();
private class MInterval
{
- // position: 0 1 2 3
- // index: -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13
- // | | A | | B | | C | |
- // interval |<--------->|<--->|<--------------->|<----|
+ // position: 0 1 2 3
+ // | A | B | C |
+ // interval |<--------->|<--------->|<--------->|
//
- // [-1 99 (9 89)]
- // [0 10 (-1 9)] [-10 0 (89 99)]
- // [0 4 (-1 3)] [-4 0 (5 9)] [0 4 (89 93)] [-2 0 (97 99)]
- //
- // Start and end positions of this interval and its children.
- // If this is the left node, the values are relative to the
- // parent's total_start. Otherwise, the values are relative to
- // the parent's total_end. So:
- // total_start total_end
- // left-side interval 0 positive even
- // right-side interval negative even 0
- // top-most interval -1 positive odd
- private int total_start, total_end;
- // Stack of MTextProperty
+ // [3 (1 2)]
+ // [1 (0 1)] [1 (2 3)]
+ private int total_length;
+ private int from, to;
+ private object key;
private Stack<MTextProperty> stack;
private MInterval left, right, parent;
- private MText mt;
-
- private static int adjust_position (int pos, bool front_inclusive)
- {
- return (pos << 2) + (front_inclusive ? -1 : 1);
- }
+ private MText mtext;
- private static bool before_point_p (int pos)
+ public MInterval (object key, int length)
{
- return ((pos + 1) % 4) == 0;
+ if (length <= 0)
+ throw new Exception ("Invalid interval length");
+ this.key = key;
+ total_length = length;
+ stack = new Stack<MTextProperty> ();
}
- public MInterval (int start, bool front_inclusive,
- int end, bool rear_inclusive)
+ private MInterval (object key, int length, Stack<MTextProperty> stack)
{
- if (start > end)
- throw new Exception ("Invalid Interval Range");
- this.total_start = (start << 2) + (front_inclusive ? -1 : 1);
- this.total_end = (end << 2) + (rear_inclusive ? 1 : -1);
- this.stack = new Stack<MTextProperty> ();
+ this.key = key;
+ total_length = length;
+ from = 0;
+ to = total_length;
+ stack = new Stack<MTextProperty> (stack);
}
- public MInterval (MText mt)
- {
- this.mt = mt;
- this.total_start = -1;
- this.total_end = (mt.sb.Length << 2) + 1;
- this.stack = new Stack<MTextProperty> ();
- }
-
- private MInterval () { }
-
- private MInterval (int start, int end, Stack<MTextProperty> stack)
+ public MInterval (object key, MText mt)
{
- this.total_start = start;
- this.total_end = end;
- this.stack = new Stack<MTextProperty> (stack);
+ this.key = key;
+ mtext = mt;
+ total_length = mt.sb.Length;
+ from = 0;
+ to = total_length;
+ stack = new Stack<MTextProperty> ();
}
- private MInterval find (int pos, out int offset)
+ private MInterval find (int pos)
{
- if (pos < total_start || total_end < pos)
+ if (parent != null)
{
- offset = 0;
- return null;
+ from = parent.from - total_length;
+ if (left != null)
+ from += left.total_length;
+ to = parent.from;
+ if (right != null)
+ to -= right.total_length;
}
- if (pos < Start)
- return left.find (pos, out offset);
- if (End < pos)
- return right.find (pos - total_end, out offset);
- offset = pos - Start;
+ if (pos < from)
+ return left.find (pos);
+ if (pos >= to)
+ return right.find (pos);
return this;
}
// c1 c2 left c1
private MInterval promote_right ()
{
- int right_length = - right.total_start;
+ int right_length = right.total_length;
MInterval c1;
if (parent == null)
- mt.root_interval = right;
- else if (total_start == 0)
+ mtext.update_root_interval (key, right);
+ else if (parent.left == this)
parent.left = right;
else
parent.right = right;
parent = right;
right = c1;
- if (c1 != null)
- c1.parent = this;
-
- parent.total_start = total_start;
- parent.total_end = total_end;
- total_start = 0;
- total_end -= right_length - (c1 == null ? 0 : c1.total_end);
-
+ parent.total_length += total_length;
+ total_length -= right_length;
if (c1 != null)
{
- c1.total_end = - c1.total_start;
- c1.total_start = 0;
+ c1.parent = this;
+ parent.total_length -= c1.total_length;
+ total_length += c1.total_length;
}
return parent;
}
// c1 c2 c2 right
private MInterval promote_left ()
{
- int left_length = left.total_end;
- MInterval c2;
+ int left_length = left.total_length;
+ MInterval c1;
if (parent == null)
- mt.root_interval = left;
- else if (total_start == 0)
+ mtext.update_root_interval (key, left);
+ else if (parent.left == this)
parent.left = left;
else
parent.right = left;
left.parent = parent;
- c2 = left.right;
+ c1 = left.left;
left.right = this;
parent = left;
- left = c2;
- if (c2 != null)
- c2.parent = this;
-
- parent.total_start = total_start;
- parent.total_end = total_end;
- total_start -= left_length + (c2 == null ? 0 : c2.total_end);;
- total_end = 0;
-
- if (c2 != null)
+ left = c1;
+ parent.total_length += total_length;
+ total_length -= left_length;
+ if (c1 != null)
{
- c2.total_start = - c2.total_end;
- c2.total_end = 0;
+ c1.parent = this;
+ parent.total_length -= c1.total_length;
+ total_length += c1.total_length;
}
return parent;
}
while (true)
{
- int this_start = interval.Start;
- int this_end = interval.End;
- int diff = ((this_start - interval.total_start)
+ // .-this-.
+ // .-left-. .-right-.
+ int left_length = (left == null ? 0 : left.total_length);
+ int right_length = (right == null ? 0 : right.total_length);
+ int length = total_length - left_length - right_length;
+ int diff = ((left_length + length) -
- (interval.total_end - this_end));
int abs = Math.Abs (diff);
+ if (left == null)
+ diff =
+
if (abs < this_end - this_start)
break;
if (diff < 0)