3 using System.Collections;
4 using System.Collections.Generic;
10 public enum MTextFormat
12 MTEXT_FORMAT_US_ASCII,
14 MTEXT_FORMAT_UTF_16BE,
15 MTEXT_FORMAT_UTF_16LE,
16 MTEXT_FORMAT_UTF_32BE,
17 MTEXT_FORMAT_UTF_32LE,
21 public class MTextProperty
23 internal MProperty prop;
24 internal bool front_sticky;
25 internal bool rear_sticky;
26 internal bool merginable;
27 public MProperty Prop { get { return prop; } }
28 public bool FrontSticky { get { return front_sticky; } }
29 public bool RearSticky { get { return rear_sticky; } }
30 public bool Merginable { get { return merginable; } }
32 public MTextProperty (bool front_sticky, bool rear_sticky)
34 this.front_sticky = front_sticky;
35 this.rear_sticky = rear_sticky;
37 public MTextProperty (bool front_sticky, bool rear_sticky, bool merginable)
39 this.front_sticky = front_sticky;
40 this.rear_sticky = rear_sticky;
41 this.merginable = merginable;
45 public class MText : IEnumerable, IEquatable<MText>, IComparable<MText>
48 public enum MTextFormat format;
51 private StringBuilder sb;
53 private int cache_pos;
54 private int cache_idx;
55 private MInterval root_interval;
56 private bool read_only;
58 private static UTF8Encoding utf8 = new UTF8Encoding ();
60 private static int count_chars (String str)
62 int len = str.Length, n = 0;
64 for (int i = 0; i < len; i++, n++)
65 if (surrogate_high_p (str[i]))
70 private static int count_chars (StringBuilder str)
72 int len = str.Length, n = 0;
74 for (int i = 0; i < len; i++, n++)
75 if (surrogate_high_p (str[i]))
82 sb = new StringBuilder ();
85 public MText (byte[] str)
87 sb = new StringBuilder (utf8.GetString (str));
88 nchars = count_chars (sb);
91 public MText (String str)
93 sb = new StringBuilder (str);
94 nchars = count_chars (str);
97 public MText (StringBuilder str)
100 nchars = count_chars (str);
103 public static MText operator+ (MText mt1, MText mt2)
105 MText mt = new MText ();
107 mt.sb.Append (mt1.sb);
108 mt.sb.Append (mt2.sb);
109 mt.nchars = mt1.nchars + mt2.nchars;
114 public bool ReadOnly { get { return read_only; } }
115 public int Length { get { return nchars; } }
119 // for IEnumerable interface
120 public IEnumerator GetEnumerator() { return new MTextEnum (this); }
122 // for IEquatable interface
123 public bool Equals (MText other) { return this.sb.Equals (other.sb); }
125 // for IComparable interface
126 public int CompareTo (MText other)
128 return this.sb.ToString ().CompareTo (other.sb.ToString ());
131 public override String ToString () { return sb.ToString (); }
133 private static bool surrogate_high_p (char c)
135 return (c >= 0xD800 && c < 0xDC00);
138 private static bool surrogate_low_p (char c)
140 return (c >= 0xDC00 && c < 0xE000);
143 private static int inc_idx (StringBuilder sb, int i)
145 return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
148 private static int dec_idx (StringBuilder sb, int i)
150 return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
153 private static int pos_to_idx (MText mt, int pos)
155 if (pos == mt.cache_pos)
161 if (pos < mt.cache_pos)
163 if (mt.cache_pos == mt.cache_idx)
165 if (pos < mt.cache_pos - pos)
172 p = mt.cache_pos; i = mt.cache_idx;
178 if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
179 return (mt.cache_idx + pos - mt.cache_pos);
180 if (pos - mt.cache_pos < mt.nchars - pos)
182 p = mt.cache_pos; i = mt.cache_idx;
187 p = mt.nchars; i = mt.sb.Length;
192 for (; p < pos; i = inc_idx (mt.sb, i), p++);
194 for (; p > pos; i = dec_idx (mt.sb, i), p--);
200 private void insert (int pos, MText mt2, int from, int to)
202 int pos_idx = pos_to_idx (this, pos);
203 int from_idx = pos_to_idx (mt2, from);
204 int to_idx = pos_to_idx (mt2, to);
206 sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
208 if (root_interval != null)
212 if (mt2.root_interval != null)
213 interval = mt2.root_interval.Copy (from, to);
214 root_interval.Insert (pos, interval);
220 public int this[int i]
223 i = pos_to_idx (this, i);
226 if (surrogate_high_p (sb[i]))
228 sb[i] = (char) value;
232 char high = (char) (0xD800 + ((value - 0x10000) >> 10));
233 char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
235 if (! surrogate_high_p (sb[i]))
242 i = pos_to_idx (this, i);
243 return (surrogate_high_p (sb[i])
244 ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
251 return (new MText (sb.ToString ()));
254 public MText ins (int pos, MText mt)
256 insert (pos, mt, 0, mt.nchars);
260 public MText ins (int pos, MText mt, int from, int to)
262 insert (pos, mt, from, to);
266 public MText del (int from, int to)
268 sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
273 public void PushProp (int from, int to, MTextProperty prop)
275 if (root_interval == null)
276 root_interval = new MInterval (0, true, sb.Length, true);
277 root_interval.Push (from, to, prop);
280 private class MInterval
282 // Start and end positions of this interval and its children.
283 // If this is the left node, the values are relative to the
284 // parent's total_start. Otherwise, the values are relatie to
285 // the parent's total_end.
286 private int total_start, total_end;
287 // Stack of MTextProperty
288 private Stack<MTextProperty> stack;
290 private MInterval left, right, parent;
292 private static int adjust_position (int pos, bool front_inclusive)
294 return (pos << 2) + (front_inclusive ? -1 : 1);
297 public MInterval (int start, bool front_inclusive,
298 int end, bool rear_inclusive)
301 throw new Exception ("Invalid Interval Range");
302 this.total_start = (start << 2) + (front_inclusive ? -1 : 1);
303 this.total_end = (end << 2) + (rear_inclusive ? 1 : -1);
304 this.stack = new Stack<MTextProperty> ();
308 private MInterval (int start, int end, Stack<MTextProperty> stack)
310 this.total_start = start;
311 this.total_end = end;
312 this.stack = new Stack<MTextProperty> (stack);
316 public MInterval Copy (int start, int end, MInterval interval)
318 MInterval new_interval;
320 start = adjust_position (start, true);
321 end = adjust_position (end, true);
322 new_interval = new MInterval (start, end, interval.stack);
326 private MInterval Copy ()
328 return new MInterval (total_start, total_end, stack);
333 return (left == null ? total_start : total_start + left.total_end);
339 return (right == null ? total_end : total_end + right.total_start);
343 private MInterval Left {
348 for (interval = left;
349 interval.right != null;
350 interval = interval.right);
352 for (interval = parent;
353 interval != null && interval.total_start == 0;
354 interval = interval.parent);
360 private MInterval Right {
365 for (interval = right;
366 interval.left != null;
367 interval = interval.left);
369 for (interval = parent;
370 interval != null && interval.total_start < 0;
371 interval = interval.parent);
377 public void Push (int start, int end, MTextProperty prop)
380 if (prop.FrontSticky)
390 throw new Exception ("Invalid Text Property Range");
392 push (start, end, prop);
395 public void Insert (int start, int end, MInterval interval)
399 private MInterval divide_right (int pos)
401 MInterval interval = Copy ();
402 int this_start = Start;
405 if (left == null || (right != null && left.depth < right.depth))
407 interval.left = this;
408 interval.right = right;
409 interval.parent = parent;
412 if (total_start == 0)
413 parent.left = interval;
415 parent.right = interval;
417 interval.depth = depth;
421 total_end = pos - this_start;
425 interval.total_start = pos - this_end;
426 interval.total_end = 0;
428 right.parent = interval;
435 private MInterval divide_left (int pos)
437 MInterval interval = Copy ();
438 int this_start = Start;
441 if (left == null || (right != null && left.depth < right.depth))
443 interval.total_start = 0;
444 interval.total_end = pos - this_start;
446 left.parent = interval;
451 interval.right = this;
452 interval.left = left;
453 interval.parent = parent;
456 if (total_start == 0)
457 parent.left = interval;
459 parent.right = interval;
461 interval.depth = depth;
464 total_start = pos - this_end;
471 private void push (int start, int end, MTextProperty prop)
473 int this_start = Start;
476 if (start < this_start)
478 if (end <= this_start)
480 Left.push (start, end, prop);
483 Left.push (start, this_start, prop);
488 if (this_end < start)
490 Right.push (start, end, prop);
493 Right.push (this_end, end, prop);
496 if (this_start < start)
504 private class MTextEnum : IEnumerator
507 private int pos = -1;
509 public MTextEnum (MText mt)
514 public bool MoveNext ()
517 return (pos < mt.nchars);
525 public object Current
528 //if (pos < 0 || pos >= mt.nchars)
529 //throw new InvalidOperationException ();