fixed tests
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextControl.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 // NOT COMPLETE
28
29 // There's still plenty of things missing, I've got most of it planned, just hadn't had
30 // the time to write it all yet.
31 // Stuff missing (in no particular order):
32 // - Align text after RecalculateLine
33 // - Implement tag types for hotlinks, etc.
34 // - Implement CaretPgUp/PgDown
35
36 // NOTE:
37 // selection_start.pos and selection_end.pos are 0-based
38 // selection_start.pos = first selected char
39 // selection_end.pos = first NOT-selected char
40 //
41 // FormatText methods are 1-based (as are all tags, LineTag.Start is 1 for 
42 // the first character on a line; the reason is that 0 is the position 
43 // *before* the first character on a line
44
45
46 #undef Debug
47
48 using System;
49 using System.Collections;
50 using System.Drawing;
51 using System.Drawing.Text;
52 using System.Text;
53
54 namespace System.Windows.Forms {
55         internal enum LineColor {
56                 Red     = 0,
57                 Black   = 1
58         }
59
60         internal enum CaretSelection {
61                 Position,       // Selection=Caret
62                 Word,           // Selection=Word under caret
63                 Line            // Selection=Line under caret
64         }
65
66         internal class FontDefinition {
67                 internal String         face;
68                 internal int            size;
69                 internal FontStyle      add_style;
70                 internal FontStyle      remove_style;
71                 internal Color          color;
72                 internal Font           font_obj;
73         }
74
75         [Flags]
76         internal enum FormatSpecified {
77                 None,
78
79                 BackColor = 2,
80                 Font = 4,
81                 Color = 8,
82         }
83
84         internal enum CaretDirection {
85                 CharForward,    // Move a char to the right
86                 CharBack,       // Move a char to the left
87                 LineUp,         // Move a line up
88                 LineDown,       // Move a line down
89                 Home,           // Move to the beginning of the line
90                 End,            // Move to the end of the line
91                 PgUp,           // Move one page up
92                 PgDn,           // Move one page down
93                 CtrlPgUp,       // Move caret to the first visible char in the viewport
94                 CtrlPgDn,       // Move caret to the last visible char in the viewport
95                 CtrlHome,       // Move to the beginning of the document
96                 CtrlEnd,        // Move to the end of the document
97                 WordBack,       // Move to the beginning of the previous word (or beginning of line)
98                 WordForward,    // Move to the beginning of the next word (or end of line)
99                 SelectionStart, // Move to the beginning of the current selection
100                 SelectionEnd,   // Move to the end of the current selection
101                 CharForwardNoWrap,   // Move a char forward, but don't wrap onto the next line
102                 CharBackNoWrap      // Move a char backward, but don't wrap onto the previous line
103         }
104
105         // Being cloneable should allow for nice line and document copies...
106         internal class Line : ICloneable, IComparable {
107                 #region Local Variables
108
109                 internal Document document;
110
111                 // Stuff that matters for our line
112                 internal StringBuilder          text;                   // Characters for the line
113                 internal float[]                widths;                 // Width of each character; always one larger than text.Length
114                 internal int                    space;                  // Number of elements in text and widths
115                 internal int                    line_no;                // Line number
116                 internal LineTag                tags;                   // Tags describing the text
117                 internal int                    offset;                 // Baseline can be on the X or Y axis depending if we are in multiline mode or not
118                 internal int                    height;                 // Height of the line (height of tallest tag)
119                 internal int                    ascent;                 // Ascent of the line (ascent of the tallest tag)
120                 internal HorizontalAlignment    alignment;              // Alignment of the line
121                 internal int                    align_shift;            // Pixel shift caused by the alignment
122                 internal bool                   soft_break;             // Tag is 'broken soft' and continuation from previous line
123                 internal int                    indent;                 // Left indent for the first line
124                 internal int                    hanging_indent;         // Hanging indent (left indent for all but the first line)
125                 internal int                    right_indent;           // Right indent for all lines
126                 internal bool carriage_return;
127
128
129                 // Stuff that's important for the tree
130                 internal Line                   parent;                 // Our parent line
131                 internal Line                   left;                   // Line with smaller line number
132                 internal Line                   right;                  // Line with higher line number
133                 internal LineColor              color;                  // We're doing a black/red tree. this is the node color
134                 internal int                    DEFAULT_TEXT_LEN;       // 
135                 internal bool                   recalc;                 // Line changed
136                 #endregion      // Local Variables
137
138                 #region Constructors
139                 internal Line (Document document)
140                 {
141                         this.document = document; 
142                         color = LineColor.Red;
143                         left = null;
144                         right = null;
145                         parent = null;
146                         text = null;
147                         recalc = true;
148                         soft_break = false;
149                         alignment = HorizontalAlignment.Left;
150                 }
151
152                 internal Line(Document document, int LineNo, string Text, Font font, SolidBrush color) : this (document) {
153                         space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
154
155                         text = new StringBuilder(Text, space);
156                         line_no = LineNo;
157
158                         widths = new float[space + 1];
159                         tags = new LineTag(this, 1);
160                         tags.font = font;
161                         tags.color = color;
162                 }
163
164                 internal Line(Document document, int LineNo, string Text, HorizontalAlignment align, Font font, SolidBrush color) : this(document) {
165                         space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
166
167                         text = new StringBuilder(Text, space);
168                         line_no = LineNo;
169                         alignment = align;
170
171                         widths = new float[space + 1];
172                         tags = new LineTag(this, 1);
173                         tags.font = font;
174                         tags.color = color;
175                 }
176
177                 internal Line(Document document, int LineNo, string Text, LineTag tag) : this(document) {
178                         space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
179
180                         text = new StringBuilder(Text, space);
181                         line_no = LineNo;
182
183                         widths = new float[space + 1];
184                         tags = tag;
185                 }
186
187                 #endregion      // Constructors
188
189                 #region Internal Properties
190
191                 internal int Y {
192                         get {
193                                 if (!document.multiline)
194                                         return 0;
195                                 return offset;
196                         }
197                 }
198
199                 internal int X {
200                         get {
201                                 if (document.multiline)
202                                         return align_shift;
203                                 return offset;
204                         }
205                 }
206
207                 internal int Width {
208                         get {
209                                 int res = (int) widths [text.Length];
210                                 if (!document.multiline) {
211
212                                 }
213                                 return res;
214                         }
215                 }
216
217                 internal int Indent {
218                         get {
219                                 return indent;
220                         }
221
222                         set {
223                                 indent = value;
224                                 recalc = true;
225                         }
226                 }
227
228                 internal int HangingIndent {
229                         get {
230                                 return hanging_indent;
231                         }
232
233                         set {
234                                 hanging_indent = value;
235                                 recalc = true;
236                         }
237                 }
238
239                 internal int RightIndent {
240                         get {
241                                 return right_indent;
242                         }
243
244                         set {
245                                 right_indent = value;
246                                 recalc = true;
247                         }
248                 }
249                         
250
251                 internal int Height {
252                         get {
253                                 return height;
254                         }
255
256                         set {
257                                 height = value;
258                         }
259                 }
260
261                 internal int LineNo {
262                         get {
263                                 return line_no;
264                         }
265
266                         set {
267                                 line_no = value;
268                         }
269                 }
270
271                 internal string Text {
272                         get {
273                                 return text.ToString();
274                         }
275
276                         set {
277                                 text = new StringBuilder(value, value.Length > DEFAULT_TEXT_LEN ? value.Length : DEFAULT_TEXT_LEN);
278                         }
279                 }
280
281                 internal HorizontalAlignment Alignment {
282                         get {
283                                 return alignment;
284                         }
285
286                         set {
287                                 if (alignment != value) {
288                                         alignment = value;
289                                         recalc = true;
290                                 }
291                         }
292                 }
293 #if no
294                 internal StringBuilder Text {
295                         get {
296                                 return text;
297                         }
298
299                         set {
300                                 text = value;
301                         }
302                 }
303 #endif
304                 #endregion      // Internal Properties
305
306                 #region Internal Methods
307                 // Make sure we always have enoughs space in text and widths
308                 internal void Grow(int minimum) {
309                         int     length;
310                         float[] new_widths;
311
312                         length = text.Length;
313
314                         if ((length + minimum) > space) {
315                                 // We need to grow; double the size
316
317                                 if ((length + minimum) > (space * 2)) {
318                                         new_widths = new float[length + minimum * 2 + 1];
319                                         space = length + minimum * 2;
320                                 } else {                                
321                                         new_widths = new float[space * 2 + 1];
322                                         space *= 2;
323                                 }
324                                 widths.CopyTo(new_widths, 0);
325
326                                 widths = new_widths;
327                         }
328                 }
329
330                 internal void Streamline(int lines) {
331                         LineTag current;
332                         LineTag next;
333
334                         current = this.tags;
335                         next = current.next;
336
337                         //
338                         // Catch what the loop below wont; eliminate 0 length 
339                         // tags, but only if there are other tags after us
340                         // We only eliminate text tags if there is another text tag
341                         // after it.  Otherwise we wind up trying to type on image tags
342                         //
343                         while ((current.length == 0) && (next != null) && (next.IsTextTag)) {
344                                 tags = next;
345                                 tags.previous = null;
346                                 current = next;
347                                 next = current.next;
348                         }
349                         
350
351                         if (next == null) {
352                                 return;
353                         }
354
355                         while (next != null) {
356                                 // Take out 0 length tags unless it's the last tag in the document
357                                 if (current.IsTextTag && next.length == 0 && next.IsTextTag) {
358                                         if ((next.next != null) || (line_no != lines)) {
359                                                 current.next = next.next;
360                                                 if (current.next != null) {
361                                                         current.next.previous = current;
362                                                 }
363                                                 next = current.next;
364                                                 continue;
365                                         }
366                                 }
367                                 if (current.Combine(next)) {
368                                         next = current.next;
369                                         continue;
370                                 }
371
372                                 current = current.next;
373                                 next = current.next;
374                         }
375                 }
376
377                 /// <summary> Find the tag on a line based on the character position, pos is 0-based</summary>
378                 internal LineTag FindTag(int pos) {
379                         LineTag tag;
380
381                         if (pos == 0) {
382                                 return tags;
383                         }
384
385                         tag = this.tags;
386
387                         if (pos >= text.Length) {
388                                 pos = text.Length - 1;
389                         }
390
391                         while (tag != null) {
392                                 if (((tag.start - 1) <= pos) && (pos < (tag.start + tag.length - 1))) {
393                                         return LineTag.GetFinalTag (tag);
394                                 }
395                                 tag = tag.next;
396                         }
397                         return null;
398                 }
399
400                 /// <summary>
401                 /// Recalculate a single line using the same char for every character in the line
402                 /// </summary>
403                 
404                 internal bool RecalculatePasswordLine(Graphics g, Document doc) {
405                         LineTag tag;
406                         int     pos;
407                         int     len;
408                         float   w;
409                         bool    ret;
410                         int     descent;
411
412                         pos = 0;
413                         len = this.text.Length;
414                         tag = this.tags;
415                         ascent = 0;
416                         tag.shift = 0;
417
418                         this.recalc = false;
419                         widths[0] = indent;
420
421                         w = g.MeasureString(doc.password_char, tags.font, 10000, Document.string_format).Width;
422
423                         if (this.height != (int)tag.font.Height) {
424                                 ret = true;
425                         } else {
426                                 ret = false;
427                         }
428
429                         this.height = (int)tag.font.Height;
430                         tag.height = this.height;
431
432                         XplatUI.GetFontMetrics(g, tag.font, out tag.ascent, out descent);
433                         this.ascent = tag.ascent;
434
435                         while (pos < len) {
436                                 pos++;
437                                 widths[pos] = widths[pos-1] + w;
438                         }
439
440                         return ret;
441                 }
442
443                 /// <summary>
444                 /// Go through all tags on a line and recalculate all size-related values;
445                 /// returns true if lineheight changed
446                 /// </summary>
447                 internal bool RecalculateLine(Graphics g, Document doc) {
448                         LineTag tag;
449                         int     pos;
450                         int     len;
451                         SizeF   size;
452                         float   w;
453                         int     prev_height;
454                         bool    retval;
455                         bool    wrapped;
456                         Line    line;
457                         int     wrap_pos;
458
459                         pos = 0;
460                         len = this.text.Length;
461                         tag = this.tags;
462                         prev_height = this.height;      // For drawing optimization calculations
463                         this.height = 0;                // Reset line height
464                         this.ascent = 0;                // Reset the ascent for the line
465                         tag.shift = 0;
466
467                         if (this.soft_break) {
468                                 widths[0] = hanging_indent;
469                         } else {
470                                 widths[0] = indent;
471                         }
472
473                         this.recalc = false;
474                         retval = false;
475                         wrapped = false;
476
477                         wrap_pos = 0;
478
479                         while (pos < len) {
480
481                                 while (tag.length == 0) {       // We should always have tags after a tag.length==0 unless len==0
482                                         tag.ascent = 0;
483                                         tag.shift = 0;
484                                         tag = tag.next;
485                                 }
486
487                                 size = tag.SizeOfPosition (g, pos);
488                                 w = size.Width;
489
490                                 if (Char.IsWhiteSpace(text[pos])) {
491                                         wrap_pos = pos + 1;
492                                 }
493
494                                 if (doc.wrap) {
495                                         if ((wrap_pos > 0) && (wrap_pos != len) && (widths[pos] + w) + 5 > (doc.viewport_width - this.right_indent)) {
496                                                 // Make sure to set the last width of the line before wrapping
497                                                 widths [pos + 1] = widths [pos] + w;
498
499                                                 pos = wrap_pos;
500                                                 len = text.Length;
501                                                 doc.Split(this, tag, pos, this.soft_break);
502                                                 this.soft_break = true;
503                                                 len = this.text.Length;
504                                                 
505                                                 retval = true;
506                                                 wrapped = true;
507                                         }  else if (pos > 1 && (widths[pos] + w) > (doc.viewport_width - this.right_indent)) {
508                                                 // No suitable wrap position was found so break right in the middle of a word
509
510                                                 // Make sure to set the last width of the line before wrapping
511                                                 widths [pos + 1] = widths [pos] + w;
512
513                                                 doc.Split(this, tag, pos, this.soft_break);
514                                                 this.soft_break = true;
515                                                 len = this.text.Length;
516                                                 retval = true;
517                                                 wrapped = true;
518                                         }
519                                 }
520
521                                 // Contract all soft lines that follow back into our line
522                                 if (!wrapped) {
523                                         pos++;
524
525                                         widths[pos] = widths[pos-1] + w;
526
527                                         if (pos == len) {
528                                                 line = doc.GetLine(this.line_no + 1);
529                                                 if ((line != null) && soft_break) {
530                                                         // Pull the two lines together
531                                                         doc.Combine(this.line_no, this.line_no + 1);
532                                                         len = this.text.Length;
533                                                         retval = true;
534                                                 }
535                                         }
536                                 }
537
538                                 if (pos == (tag.start-1 + tag.length)) {
539                                         // We just found the end of our current tag
540                                         tag.height = tag.MaxHeight ();
541
542                                         // Check if we're the tallest on the line (so far)
543                                         if (tag.height > this.height) {
544                                                 this.height = tag.height;               // Yep; make sure the line knows
545                                         }
546
547                                         if (tag.ascent == 0) {
548                                                 int     descent;
549
550                                                 XplatUI.GetFontMetrics(g, tag.font, out tag.ascent, out descent);
551                                         }
552
553                                         if (tag.ascent > this.ascent) {
554                                                 LineTag         t;
555
556                                                 // We have a tag that has a taller ascent than the line;
557                                                 t = tags;
558                                                 while (t != null && t != tag) {
559                                                         t.shift = tag.ascent - t.ascent;
560                                                         t = t.next;
561                                                 }
562
563                                                 // Save on our line
564                                                 this.ascent = tag.ascent;
565                                         } else {
566                                                 tag.shift = this.ascent - tag.ascent;
567                                         }
568
569                                         tag = tag.next;
570                                         if (tag != null) {
571                                                 tag.shift = 0;
572                                                 wrap_pos = pos;
573                                         }
574                                 }
575                         }
576
577                         if (this.height == 0) {
578                                 this.height = tags.font.Height;
579                                 tag.height = this.height;
580                         }
581
582                         if (prev_height != this.height) {
583                                 retval = true;
584                         }
585                         return retval;
586                 }
587                 #endregion      // Internal Methods
588
589                 #region Administrative
590                 public int CompareTo(object obj) {
591                         if (obj == null) {
592                                 return 1;
593                         }
594
595                         if (! (obj is Line)) {
596                                 throw new ArgumentException("Object is not of type Line", "obj");
597                         }
598
599                         if (line_no < ((Line)obj).line_no) {
600                                 return -1;
601                         } else if (line_no > ((Line)obj).line_no) {
602                                 return 1;
603                         } else {
604                                 return 0;
605                         }
606                 }
607
608                 public object Clone() {
609                         Line    clone;
610
611                         clone = new Line (document);
612
613                         clone.text = text;
614
615                         if (left != null) {
616                                 clone.left = (Line)left.Clone();
617                         }
618
619                         if (left != null) {
620                                 clone.left = (Line)left.Clone();
621                         }
622
623                         return clone;
624                 }
625
626                 internal object CloneLine() {
627                         Line    clone;
628
629                         clone = new Line (document);
630
631                         clone.text = text;
632
633                         return clone;
634                 }
635
636                 public override bool Equals(object obj) {
637                         if (obj == null) {
638                                 return false;
639                         }
640
641                         if (!(obj is Line)) {
642                                 return false;
643                         }
644
645                         if (obj == this) {
646                                 return true;
647                         }
648
649                         if (line_no == ((Line)obj).line_no) {
650                                 return true;
651                         }
652
653                         return false;
654                 }
655
656                 public override int GetHashCode() {
657                         return base.GetHashCode ();
658                 }
659
660                 public override string ToString() {
661                         return "Line " + line_no;
662                 }
663
664                 #endregion      // Administrative
665         }
666
667         internal class Document : ICloneable, IEnumerable {
668                 #region Structures
669                 // FIXME - go through code and check for places where
670                 // we do explicit comparisons instead of using the compare overloads
671                 internal struct Marker {
672                         internal Line           line;
673                         internal LineTag        tag;
674                         internal int            pos;
675                         internal int            height;
676
677                         public static bool operator<(Marker lhs, Marker rhs) {
678                                 if (lhs.line.line_no < rhs.line.line_no) {
679                                         return true;
680                                 }
681
682                                 if (lhs.line.line_no == rhs.line.line_no) {
683                                         if (lhs.pos < rhs.pos) {
684                                                 return true;
685                                         }
686                                 }
687                                 return false;
688                         }
689
690                         public static bool operator>(Marker lhs, Marker rhs) {
691                                 if (lhs.line.line_no > rhs.line.line_no) {
692                                         return true;
693                                 }
694
695                                 if (lhs.line.line_no == rhs.line.line_no) {
696                                         if (lhs.pos > rhs.pos) {
697                                                 return true;
698                                         }
699                                 }
700                                 return false;
701                         }
702
703                         public static bool operator==(Marker lhs, Marker rhs) {
704                                 if ((lhs.line.line_no == rhs.line.line_no) && (lhs.pos == rhs.pos)) {
705                                         return true;
706                                 }
707                                 return false;
708                         }
709
710                         public static bool operator!=(Marker lhs, Marker rhs) {
711                                 if ((lhs.line.line_no != rhs.line.line_no) || (lhs.pos != rhs.pos)) {
712                                         return true;
713                                 }
714                                 return false;
715                         }
716
717                         public void Combine(Line move_to_line, int move_to_line_length) {
718                                 line = move_to_line;
719                                 pos += move_to_line_length;
720                                 tag = LineTag.FindTag(line, pos);
721                         }
722
723                         // This is for future use, right now Document.Split does it by hand, with some added shortcut logic
724                         public void Split(Line move_to_line, int split_at) {
725                                 line = move_to_line;
726                                 pos -= split_at;
727                                 tag = LineTag.FindTag(line, pos);
728                         }
729
730                         public override bool Equals(object obj) {
731                                    return this==(Marker)obj;
732                         }
733
734                         public override int GetHashCode() {
735                                 return base.GetHashCode ();
736                         }
737
738                         public override string ToString() {
739                                 return "Marker Line " + line + ", Position " + pos;
740                         }
741
742                 }
743                 #endregion Structures
744
745                 #region Local Variables
746                 private Line            document;
747                 private int             lines;
748                 private Line            sentinel;
749                 private int             document_id;
750                 private Random          random = new Random();
751                 internal string         password_char;
752                 private StringBuilder   password_cache;
753                 private bool            calc_pass;
754                 private int             char_count;
755
756                 // For calculating widths/heights
757                 public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic);
758
759                 private int             recalc_suspended;
760                 private bool            recalc_pending;
761                 private int             recalc_start = 1;   // This starts at one, since lines are 1 based
762                 private int             recalc_end;
763                 private bool            recalc_optimize;
764
765                 private int             update_suspended;
766                 private bool update_pending;
767                 private int update_start = 1;
768
769                 internal bool           multiline;
770                 internal bool           wrap;
771
772                 internal UndoManager    undo;
773
774                 internal Marker         caret;
775                 internal Marker         selection_start;
776                 internal Marker         selection_end;
777                 internal bool           selection_visible;
778                 internal Marker         selection_anchor;
779                 internal Marker         selection_prev;
780                 internal bool           selection_end_anchor;
781
782                 internal int            viewport_x;
783                 internal int            viewport_y;             // The visible area of the document
784                 internal int            viewport_width;
785                 internal int            viewport_height;
786
787                 internal int            document_x;             // Width of the document
788                 internal int            document_y;             // Height of the document
789
790                 internal Rectangle      invalid;
791
792                 internal int            crlf_size;              // 1 or 2, depending on whether we use \r\n or just \n
793
794                 internal TextBoxBase    owner;                  // Who's owning us?
795                 static internal int     caret_width = 1;
796                 static internal int     caret_shift = 1;
797                 #endregion      // Local Variables
798
799                 #region Constructors
800                 internal Document(TextBoxBase owner) {
801                         lines = 0;
802
803                         this.owner = owner;
804
805                         multiline = true;
806                         password_char = "";
807                         calc_pass = false;
808                         recalc_pending = false;
809
810                         // Tree related stuff
811                         sentinel = new Line (this);
812                         sentinel.color = LineColor.Black;
813
814                         document = sentinel;
815
816                         // We always have a blank line
817                         owner.HandleCreated += new EventHandler(owner_HandleCreated);
818                         owner.VisibleChanged += new EventHandler(owner_VisibleChanged);
819
820                         Add(1, "", owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush(owner.ForeColor));
821                         Line l = GetLine (1);
822                         l.soft_break = true;
823
824                         undo = new UndoManager (this);
825
826                         selection_visible = false;
827                         selection_start.line = this.document;
828                         selection_start.pos = 0;
829                         selection_start.tag = selection_start.line.tags;
830                         selection_end.line = this.document;
831                         selection_end.pos = 0;
832                         selection_end.tag = selection_end.line.tags;
833                         selection_anchor.line = this.document;
834                         selection_anchor.pos = 0;
835                         selection_anchor.tag = selection_anchor.line.tags;
836                         caret.line = this.document;
837                         caret.pos = 0;
838                         caret.tag = caret.line.tags;
839
840                         viewport_x = 0;
841                         viewport_y = 0;
842
843                         crlf_size = 2;
844
845                         // Default selection is empty
846
847                         document_id = random.Next();
848
849                         string_format.Trimming = StringTrimming.None;
850                         string_format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
851                 }
852                 #endregion
853
854                 #region Internal Properties
855                 internal Line Root {
856                         get {
857                                 return document;
858                         }
859
860                         set {
861                                 document = value;
862                         }
863                 }
864
865                 internal int Lines {
866                         get {
867                                 return lines;
868                         }
869                 }
870
871                 internal Line CaretLine {
872                         get {
873                                 return caret.line;
874                         }
875                 }
876
877                 internal int CaretPosition {
878                         get {
879                                 return caret.pos;
880                         }
881                 }
882
883                 internal Point Caret {
884                         get {
885                                 return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.X, caret.line.Y);
886                         }
887                 }
888
889                 internal LineTag CaretTag {
890                         get {
891                                 return caret.tag;
892                         }
893
894                         set {
895                                 caret.tag = value;
896                         }
897                 }
898
899                 internal int CRLFSize {
900                         get {
901                                 return crlf_size;
902                         }
903
904                         set {
905                                 crlf_size = value;
906                         }
907                 }
908
909                 internal string PasswordChar {
910                         get {
911                                 return password_char;
912                         }
913
914                         set {
915                                 password_char = value;
916                                 PasswordCache.Length = 0;
917                                 if ((password_char.Length != 0) && (password_char[0] != '\0')) {
918                                         calc_pass = true;
919                                 } else {
920                                         calc_pass = false;
921                                 }
922                         }
923                 }
924
925                 private StringBuilder PasswordCache {
926                         get { 
927                                 if (password_cache == null) 
928                                           password_cache = new StringBuilder(); 
929                                 return password_cache;
930                         }
931                 }
932
933                 internal int ViewPortX {
934                         get {
935                                 return viewport_x;
936                         }
937
938                         set {
939                                 viewport_x = value;
940                         }
941                 }
942
943                 internal int Length {
944                         get {
945                                 return char_count + lines - 1;  // Add \n for each line but the last
946                         }
947                 }
948
949                 private int CharCount {
950                         get {
951                                 return char_count;
952                         }
953
954                         set {
955                                 char_count = value;
956
957                                 if (LengthChanged != null) {
958                                         LengthChanged(this, EventArgs.Empty);
959                                 }
960                         }
961                 }
962
963                 internal int ViewPortY {
964                         get {
965                                 return viewport_y;
966                         }
967
968                         set {
969                                 viewport_y = value;
970                         }
971                 }
972
973                 internal int ViewPortWidth {
974                         get {
975                                 return viewport_width;
976                         }
977
978                         set {
979                                 viewport_width = value;
980                         }
981                 }
982
983                 internal int ViewPortHeight {
984                         get {
985                                 return viewport_height;
986                         }
987
988                         set {
989                                 viewport_height = value;
990                         }
991                 }
992
993
994                 internal int Width {
995                         get {
996                                 return this.document_x;
997                         }
998                 }
999
1000                 internal int Height {
1001                         get {
1002                                 return this.document_y;
1003                         }
1004                 }
1005
1006                 internal bool SelectionVisible {
1007                         get {
1008                                 return selection_visible;
1009                         }
1010                 }
1011
1012                 internal bool Wrap {
1013                         get {
1014                                 return wrap;
1015                         }
1016
1017                         set {
1018                                 wrap = value;
1019                         }
1020                 }
1021
1022                 #endregion      // Internal Properties
1023
1024                 #region Private Methods
1025
1026                 internal void SuspendRecalc ()
1027                 {
1028                         recalc_suspended++;
1029                 }
1030
1031                 internal void ResumeRecalc (bool immediate_update)
1032                 {
1033                         if (recalc_suspended > 0)
1034                                 recalc_suspended--;
1035
1036                         if (immediate_update && recalc_suspended == 0 && recalc_pending) {
1037                                 RecalculateDocument (owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
1038                                 recalc_pending = false;
1039                         }
1040                 }
1041
1042                 internal void SuspendUpdate ()
1043                 {
1044                         update_suspended++;
1045                 }
1046
1047                 internal void ResumeUpdate (bool immediate_update)
1048                 {
1049                         if (update_suspended > 0)
1050                                 update_suspended--;
1051
1052                         if (immediate_update && update_suspended == 0 && update_pending) {
1053                                 UpdateView (GetLine (update_start), 0);
1054                                 update_pending = false;
1055                         }
1056                 }
1057
1058                 // For debugging
1059                 internal int DumpTree(Line line, bool with_tags) {
1060                         int     total;
1061
1062                         total = 1;
1063
1064                         Console.Write("Line {0} [# {1}], Y: {2}, soft: {3},  Text: '{4}'",
1065                                         line.line_no, line.GetHashCode(), line.Y, line.soft_break,
1066                                         line.text != null ? line.text.ToString() : "undefined");
1067
1068                         if (line.left == sentinel) {
1069                                 Console.Write(", left = sentinel");
1070                         } else if (line.left == null) {
1071                                 Console.Write(", left = NULL");
1072                         }
1073
1074                         if (line.right == sentinel) {
1075                                 Console.Write(", right = sentinel");
1076                         } else if (line.right == null) {
1077                                 Console.Write(", right = NULL");
1078                         }
1079
1080                         Console.WriteLine("");
1081
1082                         if (with_tags) {
1083                                 LineTag tag;
1084                                 int     count;
1085                                 int     length;
1086
1087                                 tag = line.tags;
1088                                 count = 1;
1089                                 length = 0;
1090                                 Console.Write("   Tags: ");
1091                                 while (tag != null) {
1092                                         Console.Write("{0} <{1}>-<{2}>", count++, tag.start, tag.end
1093                                                         /*line.text.ToString (tag.start - 1, tag.length)*/);
1094                                         length += tag.length;
1095
1096                                         if (tag.line != line) {
1097                                                 Console.Write("BAD line link");
1098                                                 throw new Exception("Bad line link in tree");
1099                                         }
1100                                         tag = tag.next;
1101                                         if (tag != null) {
1102                                                 Console.Write(", ");
1103                                         }
1104                                 }
1105                                 if (length > line.text.Length) {
1106                                         throw new Exception(String.Format("Length of tags more than length of text on line (expected {0} calculated {1})", line.text.Length, length));
1107                                 } else if (length < line.text.Length) {
1108                                         throw new Exception(String.Format("Length of tags less than length of text on line (expected {0} calculated {1})", line.text.Length, length));
1109                                 }
1110                                 Console.WriteLine("");
1111                         }
1112                         if (line.left != null) {
1113                                 if (line.left != sentinel) {
1114                                         total += DumpTree(line.left, with_tags);
1115                                 }
1116                         } else {
1117                                 if (line != sentinel) {
1118                                         throw new Exception("Left should not be NULL");
1119                                 }
1120                         }
1121
1122                         if (line.right != null) {
1123                                 if (line.right != sentinel) {
1124                                         total += DumpTree(line.right, with_tags);
1125                                 }
1126                         } else {
1127                                 if (line != sentinel) {
1128                                         throw new Exception("Right should not be NULL");
1129                                 }
1130                         }
1131
1132                         for (int i = 1; i <= this.lines; i++) {
1133                                 if (GetLine(i) == null) {
1134                                         throw new Exception(String.Format("Hole in line order, missing {0}", i));
1135                                 }
1136                         }
1137
1138                         if (line == this.Root) {
1139                                 if (total < this.lines) {
1140                                         throw new Exception(String.Format("Not enough nodes in tree, found {0}, expected {1}", total, this.lines));
1141                                 } else if (total > this.lines) {
1142                                         throw new Exception(String.Format("Too many nodes in tree, found {0}, expected {1}", total, this.lines));
1143                                 }
1144                         }
1145
1146                         return total;
1147                 }
1148
1149                 private void SetSelectionVisible (bool value)
1150                 {
1151                         selection_visible = value;
1152
1153                         // cursor and selection are enemies, we can't have both in the same room at the same time
1154                         if (owner.IsHandleCreated && !owner.show_caret_w_selection)
1155                                 XplatUI.CaretVisible (owner.Handle, !selection_visible);
1156                 }
1157
1158                 private void DecrementLines(int line_no) {
1159                         int     current;
1160
1161                         current = line_no;
1162                         while (current <= lines) {
1163                                 GetLine(current).line_no--;
1164                                 current++;
1165                         }
1166                         return;
1167                 }
1168
1169                 private void IncrementLines(int line_no) {
1170                         int     current;
1171
1172                         current = this.lines;
1173                         while (current >= line_no) {
1174                                 GetLine(current).line_no++;
1175                                 current--;
1176                         }
1177                         return;
1178                 }
1179
1180                 private void RebalanceAfterAdd(Line line1) {
1181                         Line    line2;
1182
1183                         while ((line1 != document) && (line1.parent.color == LineColor.Red)) {
1184                                 if (line1.parent == line1.parent.parent.left) {
1185                                         line2 = line1.parent.parent.right;
1186
1187                                         if ((line2 != null) && (line2.color == LineColor.Red)) {
1188                                                 line1.parent.color = LineColor.Black;
1189                                                 line2.color = LineColor.Black;
1190                                                 line1.parent.parent.color = LineColor.Red;
1191                                                 line1 = line1.parent.parent;
1192                                         } else {
1193                                                 if (line1 == line1.parent.right) {
1194                                                         line1 = line1.parent;
1195                                                         RotateLeft(line1);
1196                                                 }
1197
1198                                                 line1.parent.color = LineColor.Black;
1199                                                 line1.parent.parent.color = LineColor.Red;
1200
1201                                                 RotateRight(line1.parent.parent);
1202                                         }
1203                                 } else {
1204                                         line2 = line1.parent.parent.left;
1205
1206                                         if ((line2 != null) && (line2.color == LineColor.Red)) {
1207                                                 line1.parent.color = LineColor.Black;
1208                                                 line2.color = LineColor.Black;
1209                                                 line1.parent.parent.color = LineColor.Red;
1210                                                 line1 = line1.parent.parent;
1211                                         } else {
1212                                                 if (line1 == line1.parent.left) {
1213                                                         line1 = line1.parent;
1214                                                         RotateRight(line1);
1215                                                 }
1216
1217                                                 line1.parent.color = LineColor.Black;
1218                                                 line1.parent.parent.color = LineColor.Red;
1219                                                 RotateLeft(line1.parent.parent);
1220                                         }
1221                                 }
1222                         }
1223                         document.color = LineColor.Black;
1224                 }
1225
1226                 private void RebalanceAfterDelete(Line line1) {
1227                         Line line2;
1228
1229                         while ((line1 != document) && (line1.color == LineColor.Black)) {
1230                                 if (line1 == line1.parent.left) {
1231                                         line2 = line1.parent.right;
1232                                         if (line2.color == LineColor.Red) { 
1233                                                 line2.color = LineColor.Black;
1234                                                 line1.parent.color = LineColor.Red;
1235                                                 RotateLeft(line1.parent);
1236                                                 line2 = line1.parent.right;
1237                                         }
1238                                         if ((line2.left.color == LineColor.Black) && (line2.right.color == LineColor.Black)) { 
1239                                                 line2.color = LineColor.Red;
1240                                                 line1 = line1.parent;
1241                                         } else {
1242                                                 if (line2.right.color == LineColor.Black) {
1243                                                         line2.left.color = LineColor.Black;
1244                                                         line2.color = LineColor.Red;
1245                                                         RotateRight(line2);
1246                                                         line2 = line1.parent.right;
1247                                                 }
1248                                                 line2.color = line1.parent.color;
1249                                                 line1.parent.color = LineColor.Black;
1250                                                 line2.right.color = LineColor.Black;
1251                                                 RotateLeft(line1.parent);
1252                                                 line1 = document;
1253                                         }
1254                                 } else { 
1255                                         line2 = line1.parent.left;
1256                                         if (line2.color == LineColor.Red) {
1257                                                 line2.color = LineColor.Black;
1258                                                 line1.parent.color = LineColor.Red;
1259                                                 RotateRight(line1.parent);
1260                                                 line2 = line1.parent.left;
1261                                         }
1262                                         if ((line2.right.color == LineColor.Black) && (line2.left.color == LineColor.Black)) {
1263                                                 line2.color = LineColor.Red;
1264                                                 line1 = line1.parent;
1265                                         } else {
1266                                                 if (line2.left.color == LineColor.Black) {
1267                                                         line2.right.color = LineColor.Black;
1268                                                         line2.color = LineColor.Red;
1269                                                         RotateLeft(line2);
1270                                                         line2 = line1.parent.left;
1271                                                 }
1272                                                 line2.color = line1.parent.color;
1273                                                 line1.parent.color = LineColor.Black;
1274                                                 line2.left.color = LineColor.Black;
1275                                                 RotateRight(line1.parent);
1276                                                 line1 = document;
1277                                         }
1278                                 }
1279                         }
1280                         line1.color = LineColor.Black;
1281                 }
1282
1283                 private void RotateLeft(Line line1) {
1284                         Line    line2 = line1.right;
1285
1286                         line1.right = line2.left;
1287
1288                         if (line2.left != sentinel) {
1289                                 line2.left.parent = line1;
1290                         }
1291
1292                         if (line2 != sentinel) {
1293                                 line2.parent = line1.parent;
1294                         }
1295
1296                         if (line1.parent != null) {
1297                                 if (line1 == line1.parent.left) {
1298                                         line1.parent.left = line2;
1299                                 } else {
1300                                         line1.parent.right = line2;
1301                                 }
1302                         } else {
1303                                 document = line2;
1304                         }
1305
1306                         line2.left = line1;
1307                         if (line1 != sentinel) {
1308                                 line1.parent = line2;
1309                         }
1310                 }
1311
1312                 private void RotateRight(Line line1) {
1313                         Line line2 = line1.left;
1314
1315                         line1.left = line2.right;
1316
1317                         if (line2.right != sentinel) {
1318                                 line2.right.parent = line1;
1319                         }
1320
1321                         if (line2 != sentinel) {
1322                                 line2.parent = line1.parent;
1323                         }
1324
1325                         if (line1.parent != null) {
1326                                 if (line1 == line1.parent.right) {
1327                                         line1.parent.right = line2;
1328                                 } else {
1329                                         line1.parent.left = line2;
1330                                 }
1331                         } else {
1332                                 document = line2;
1333                         }
1334
1335                         line2.right = line1;
1336                         if (line1 != sentinel) {
1337                                 line1.parent = line2;
1338                         }
1339                 }        
1340
1341
1342                 internal void UpdateView(Line line, int pos) {
1343                         if (!owner.IsHandleCreated) {
1344                                 return;
1345                         }
1346
1347                         if (update_suspended > 0) {
1348                                 update_start = Math.Min (update_start, line.line_no);
1349                                 // update_end = Math.Max (update_end, line.line_no);
1350                                 // recalc_optimize = true;
1351                                 update_pending = true;
1352                                 return;
1353                         }
1354
1355                         // Optimize invalidation based on Line alignment
1356                         if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no, true)) {
1357                                 // Lineheight changed, invalidate the rest of the document
1358                                 if ((line.Y - viewport_y) >=0 ) {
1359                                         // We formatted something that's in view, only draw parts of the screen
1360                                         owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
1361                                 } else {
1362                                         // The tag was above the visible area, draw everything
1363                                         owner.Invalidate();
1364                                 }
1365                         } else {
1366                                 switch(line.alignment) {
1367                                         case HorizontalAlignment.Left: {
1368                                                 owner.Invalidate(new Rectangle(line.X + (int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, viewport_width, line.height + 1));
1369                                                 break;
1370                                         }
1371
1372                                         case HorizontalAlignment.Center: {
1373                                                 owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, viewport_width, line.height + 1));
1374                                                 break;
1375                                         }
1376
1377                                         case HorizontalAlignment.Right: {
1378                                                 owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, (int)line.widths[pos + 1] - viewport_x + line.X, line.height + 1));
1379                                                 break;
1380                                         }
1381                                 }
1382                         }
1383                 }
1384
1385
1386                 // Update display from line, down line_count lines; pos is unused, but required for the signature
1387                 internal void UpdateView(Line line, int line_count, int pos) {
1388                         if (!owner.IsHandleCreated) {
1389                                 return;
1390                         }
1391
1392                         if (recalc_suspended > 0) {
1393                                 recalc_start = Math.Min (recalc_start, line.line_no);
1394                                 recalc_end = Math.Max (recalc_end, line.line_no + line_count - 1);
1395                                 recalc_optimize = true;
1396                                 recalc_pending = true;
1397                                 return;
1398                         }
1399
1400                         if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count - 1, true)) {
1401                                 // Lineheight changed, invalidate the rest of the document
1402                                 if ((line.Y - viewport_y) >=0 ) {
1403                                         // We formatted something that's in view, only draw parts of the screen
1404 //blah Console.WriteLine("TextControl.cs(981) Invalidate called in UpdateView(line, line_count, pos)");
1405                                         owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
1406                                 } else {
1407                                         // The tag was above the visible area, draw everything
1408 //blah Console.WriteLine("TextControl.cs(985) Invalidate called in UpdateView(line, line_count, pos)");
1409                                         owner.Invalidate();
1410                                 }
1411                         } else {
1412                                 Line    end_line;
1413
1414                                 end_line = GetLine(line.line_no + line_count -1);
1415                                 if (end_line == null) {
1416                                         end_line = line;
1417                                 }
1418
1419 //blah Console.WriteLine("TextControl.cs(996) Invalidate called in UpdateView(line, line_count, pos)");
1420                                 owner.Invalidate(new Rectangle(0 - viewport_x, line.Y - viewport_y, (int)line.widths[line.text.Length], end_line.Y + end_line.height));
1421                         }
1422                 }
1423                 #endregion      // Private Methods
1424
1425                 #region Internal Methods
1426                 // Clear the document and reset state
1427                 internal void Empty() {
1428
1429                         document = sentinel;
1430                         lines = 0;
1431
1432                         // We always have a blank line
1433                         Add(1, "", owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush(owner.ForeColor));
1434                         Line l = GetLine (1);
1435                         l.soft_break = true;
1436                         
1437                         this.RecalculateDocument(owner.CreateGraphicsInternal());
1438                         PositionCaret(0, 0);
1439
1440                         SetSelectionVisible (false);
1441
1442                         selection_start.line = this.document;
1443                         selection_start.pos = 0;
1444                         selection_start.tag = selection_start.line.tags;
1445                         selection_end.line = this.document;
1446                         selection_end.pos = 0;
1447                         selection_end.tag = selection_end.line.tags;
1448                         char_count = 0;
1449
1450                         viewport_x = 0;
1451                         viewport_y = 0;
1452
1453                         document_x = 0;
1454                         document_y = 0;
1455
1456                         if (owner.IsHandleCreated)
1457                                 owner.Invalidate ();
1458                 }
1459
1460                 internal void PositionCaret(Line line, int pos) {
1461                         caret.tag = line.FindTag (pos);
1462
1463                         MoveCaretToTextTag ();
1464
1465                         caret.line = line;
1466                         caret.pos = pos;
1467
1468                         if (owner.IsHandleCreated) {
1469                                 if (owner.Focused) {
1470                                         if (caret.height != caret.tag.height)
1471                                                 XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
1472                                         XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
1473                                 }
1474
1475                                 if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
1476                         }
1477
1478                         // We set this at the end because we use the heights to determine whether or
1479                         // not we need to recreate the caret
1480                         caret.height = caret.tag.height;
1481
1482                 }
1483
1484                 internal void PositionCaret(int x, int y) {
1485                         if (!owner.IsHandleCreated) {
1486                                 return;
1487                         }
1488
1489                         caret.tag = FindCursor(x, y, out caret.pos);
1490
1491                         MoveCaretToTextTag ();
1492                         
1493                         caret.line = caret.tag.line;
1494                         caret.height = caret.tag.height;
1495
1496                         if (owner.Focused) {
1497                                 XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
1498                                 XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
1499                         }
1500
1501                         if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
1502                 }
1503
1504                 internal void CaretHasFocus() {
1505                         if ((caret.tag != null) && owner.IsHandleCreated) {
1506                                 XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
1507                                 XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
1508
1509                                 DisplayCaret ();
1510                         }
1511
1512                         if (owner.IsHandleCreated && selection_visible) {
1513                                 InvalidateSelectionArea ();
1514                         }
1515                 }
1516
1517                 internal void CaretLostFocus() {
1518                         if (!owner.IsHandleCreated) {
1519                                 return;
1520                         }
1521                         XplatUI.DestroyCaret(owner.Handle);
1522                 }
1523
1524                 internal void AlignCaret() {
1525                         if (!owner.IsHandleCreated) {
1526                                 return;
1527                         }
1528
1529                         caret.tag = LineTag.FindTag (caret.line, caret.pos);
1530
1531                         MoveCaretToTextTag ();
1532
1533                         caret.height = caret.tag.height;
1534
1535                         if (owner.Focused) {
1536                                 XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
1537                                 XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
1538                                 DisplayCaret ();
1539                         }
1540
1541                         if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
1542                 }
1543
1544                 internal void UpdateCaret() {
1545                         if (!owner.IsHandleCreated || caret.tag == null) {
1546                                 return;
1547                         }
1548
1549                         MoveCaretToTextTag ();
1550
1551                         if (caret.tag.height != caret.height) {
1552                                 caret.height = caret.tag.height;
1553                                 if (owner.Focused) {
1554                                         XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
1555                                 }
1556                         }
1557
1558                         XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
1559
1560                         DisplayCaret ();
1561
1562                         if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
1563                 }
1564
1565                 internal void DisplayCaret() {
1566                         if (!owner.IsHandleCreated) {
1567                                 return;
1568                         }
1569
1570                         if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
1571                                 XplatUI.CaretVisible(owner.Handle, true);
1572                         }
1573                 }
1574
1575                 internal void HideCaret() {
1576                         if (!owner.IsHandleCreated) {
1577                                 return;
1578                         }
1579
1580                         if (owner.Focused) {
1581                                 XplatUI.CaretVisible(owner.Handle, false);
1582                         }
1583                 }
1584
1585                 
1586                 internal void MoveCaretToTextTag ()
1587                 {
1588                         if (caret.tag == null || caret.tag.IsTextTag)
1589                                 return;
1590
1591                         
1592
1593                         if (caret.pos < caret.tag.start) {
1594                                 caret.tag = caret.tag.previous;
1595                         } else {
1596                                 caret.tag = caret.tag.next;
1597                         }
1598                 }
1599
1600                 internal void MoveCaret(CaretDirection direction) {
1601                         // FIXME should we use IsWordSeparator to detect whitespace, instead 
1602                         // of looking for actual spaces in the Word move cases?
1603
1604                         bool nowrap = false;
1605                         switch(direction) {
1606                                 case CaretDirection.CharForwardNoWrap:
1607                                         nowrap = true;
1608                                         goto case CaretDirection.CharForward;
1609                                 case CaretDirection.CharForward: {
1610                                         caret.pos++;
1611                                         if (caret.pos > caret.line.text.Length) {
1612                                                 if (!nowrap) {
1613                                                         // Go into next line
1614                                                         if (caret.line.line_no < this.lines) {
1615                                                                 caret.line = GetLine(caret.line.line_no+1);
1616                                                                 caret.pos = 0;
1617                                                                 caret.tag = caret.line.tags;
1618                                                         } else {
1619                                                                 caret.pos--;
1620                                                         }
1621                                                 } else {
1622                                                         // Single line; we stay where we are
1623                                                         caret.pos--;
1624                                                 }
1625                                         } else {
1626                                                 if ((caret.tag.start - 1 + caret.tag.length) < caret.pos) {
1627                                                         caret.tag = caret.tag.next;
1628                                                 }
1629                                         }
1630                                         UpdateCaret();
1631                                         return;
1632                                 }
1633
1634                                 case CaretDirection.CharBackNoWrap:
1635                                         nowrap = true;
1636                                         goto case CaretDirection.CharBack;
1637                                 case CaretDirection.CharBack: {
1638                                         if (caret.pos > 0) {
1639                                                 // caret.pos--; // folded into the if below
1640                                                 
1641                                                 if (--caret.pos > 0) {
1642                                                         if (caret.tag.start > caret.pos) {
1643                                                                 caret.tag = caret.tag.previous;
1644                                                         }
1645                                                 }
1646                                         } else {
1647                                                 if (caret.line.line_no > 1 && !nowrap) {
1648                                                         caret.line = GetLine(caret.line.line_no - 1);
1649                                                         caret.pos = caret.line.text.Length;
1650                                                         caret.tag = LineTag.FindTag(caret.line, caret.pos);
1651                                                 }
1652                                         }
1653                                         UpdateCaret();
1654                                         return;
1655                                 }
1656
1657                                 case CaretDirection.WordForward: {
1658                                         int len;
1659
1660                                         len = caret.line.text.Length;
1661                                         if (caret.pos < len) {
1662                                                 while ((caret.pos < len) && (caret.line.text[caret.pos] != ' ')) {
1663                                                         caret.pos++;
1664                                                 }
1665                                                 if (caret.pos < len) {
1666                                                         // Skip any whitespace
1667                                                         while ((caret.pos < len) && (caret.line.text[caret.pos] == ' ')) {
1668                                                                 caret.pos++;
1669                                                         }
1670                                                 }
1671                                                 caret.tag = LineTag.FindTag(caret.line, caret.pos);
1672                                         } else {
1673                                                 if (caret.line.line_no < this.lines) {
1674                                                         caret.line = GetLine(caret.line.line_no + 1);
1675                                                         caret.pos = 0;
1676                                                         caret.tag = caret.line.tags;
1677                                                 }
1678                                         }
1679                                         UpdateCaret();
1680                                         return;
1681                                 }
1682
1683                                 case CaretDirection.WordBack: {
1684                                         if (caret.pos > 0) {
1685                                                 caret.pos--;
1686
1687                                                 while ((caret.pos > 0) && (caret.line.text[caret.pos] == ' ')) {
1688                                                         caret.pos--;
1689                                                 }
1690
1691                                                 while ((caret.pos > 0) && (caret.line.text[caret.pos] != ' ')) {
1692                                                         caret.pos--;
1693                                                 }
1694
1695                                                 if (caret.line.text.ToString(caret.pos, 1) == " ") {
1696                                                         if (caret.pos != 0) {
1697                                                                 caret.pos++;
1698                                                         } else {
1699                                                                 caret.line = GetLine(caret.line.line_no - 1);
1700                                                                 caret.pos = caret.line.text.Length;
1701                                                         }
1702                                                 }
1703                                                 caret.tag = LineTag.FindTag(caret.line, caret.pos);
1704                                         } else {
1705                                                 if (caret.line.line_no > 1) {
1706                                                         caret.line = GetLine(caret.line.line_no - 1);
1707                                                         caret.pos = caret.line.text.Length;
1708                                                         caret.tag = LineTag.FindTag(caret.line, caret.pos);
1709                                                 }
1710                                         }
1711                                         UpdateCaret();
1712                                         return;
1713                                 }
1714
1715                                 case CaretDirection.LineUp: {
1716                                         if (caret.line.line_no > 1) {
1717                                                 int     pixel;
1718
1719                                                 pixel = (int)caret.line.widths[caret.pos];
1720                                                 PositionCaret(pixel, GetLine(caret.line.line_no - 1).Y);
1721
1722                                                 DisplayCaret ();
1723                                         }
1724                                         return;
1725                                 }
1726
1727                                 case CaretDirection.LineDown: {
1728                                         if (caret.line.line_no < lines) {
1729                                                 int     pixel;
1730
1731                                                 pixel = (int)caret.line.widths[caret.pos];
1732                                                 PositionCaret(pixel, GetLine(caret.line.line_no + 1).Y);
1733
1734                                                 DisplayCaret ();
1735                                         }
1736                                         return;
1737                                 }
1738
1739                                 case CaretDirection.Home: {
1740                                         if (caret.pos > 0) {
1741                                                 caret.pos = 0;
1742                                                 caret.tag = caret.line.tags;
1743                                                 UpdateCaret();
1744                                         }
1745                                         return;
1746                                 }
1747
1748                                 case CaretDirection.End: {
1749                                         if (caret.pos < caret.line.text.Length) {
1750                                                 caret.pos = caret.line.text.Length;
1751                                                 caret.tag = LineTag.FindTag(caret.line, caret.pos);
1752                                                 UpdateCaret();
1753                                         }
1754                                         return;
1755                                 }
1756
1757                                 case CaretDirection.PgUp: {
1758
1759                                         int new_y, y_offset;
1760
1761                                         if (viewport_y == 0) {
1762
1763                                                 // This should probably be handled elsewhere
1764                                                 if (!(owner is RichTextBox)) {
1765                                                         // Page down doesn't do anything in a regular TextBox
1766                                                         // if the bottom of the document
1767                                                         // is already visible, the page and the caret stay still
1768                                                         return;
1769                                                 }
1770
1771                                                 // We're just placing the caret at the end of the document, no scrolling needed
1772                                                 owner.vscroll.Value = 0;
1773                                                 Line line = GetLine (1);
1774                                                 PositionCaret (line, 0);
1775                                         }
1776
1777                                         y_offset = caret.line.Y - viewport_y;
1778                                         new_y = caret.line.Y - viewport_height;
1779
1780                                         owner.vscroll.Value = Math.Max (new_y, 0);
1781                                         PositionCaret ((int)caret.line.widths[caret.pos], y_offset + viewport_y);
1782                                         return;
1783                                 }
1784
1785                                 case CaretDirection.PgDn: {
1786                                         int new_y, y_offset;
1787
1788                                         if ((viewport_y + viewport_height) > document_y) {
1789
1790                                                 // This should probably be handled elsewhere
1791                                                 if (!(owner is RichTextBox)) {
1792                                                         // Page up doesn't do anything in a regular TextBox
1793                                                         // if the bottom of the document
1794                                                         // is already visible, the page and the caret stay still
1795                                                         return;
1796                                                 }
1797
1798                                                 // We're just placing the caret at the end of the document, no scrolling needed
1799                                                 owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1;
1800                                                 Line line = GetLine (lines);
1801                                                 PositionCaret (line, line.Text.Length);
1802                                         }
1803
1804                                         y_offset = caret.line.Y - viewport_y;
1805                                         new_y = caret.line.Y + viewport_height;
1806                                         
1807                                         owner.vscroll.Value = Math.Min (new_y, owner.vscroll.Maximum - viewport_height + 1);
1808                                         PositionCaret ((int)caret.line.widths[caret.pos], y_offset + viewport_y);
1809                                         
1810                                         return;
1811                                 }
1812
1813                                 case CaretDirection.CtrlPgUp: {
1814                                         PositionCaret(0, viewport_y);
1815                                         DisplayCaret ();
1816                                         return;
1817                                 }
1818
1819                                 case CaretDirection.CtrlPgDn: {
1820                                         Line    line;
1821                                         LineTag tag;
1822                                         int     index;
1823
1824                                         tag = FindTag(0, viewport_y + viewport_height, out index, false);
1825                                         if (tag.line.line_no > 1) {
1826                                                 line = GetLine(tag.line.line_no - 1);
1827                                         } else {
1828                                                 line = tag.line;
1829                                         }
1830                                         PositionCaret(line, line.Text.Length);
1831                                         DisplayCaret ();
1832                                         return;
1833                                 }
1834
1835                                 case CaretDirection.CtrlHome: {
1836                                         caret.line = GetLine(1);
1837                                         caret.pos = 0;
1838                                         caret.tag = caret.line.tags;
1839
1840                                         UpdateCaret();
1841                                         return;
1842                                 }
1843
1844                                 case CaretDirection.CtrlEnd: {
1845                                         caret.line = GetLine(lines);
1846                                         caret.pos = caret.line.text.Length;
1847                                         caret.tag = LineTag.FindTag(caret.line, caret.pos);
1848
1849                                         UpdateCaret();
1850                                         return;
1851                                 }
1852
1853                                 case CaretDirection.SelectionStart: {
1854                                         caret.line = selection_start.line;
1855                                         caret.pos = selection_start.pos;
1856                                         caret.tag = selection_start.tag;
1857
1858                                         UpdateCaret();
1859                                         return;
1860                                 }
1861
1862                                 case CaretDirection.SelectionEnd: {
1863                                         caret.line = selection_end.line;
1864                                         caret.pos = selection_end.pos;
1865                                         caret.tag = selection_end.tag;
1866
1867                                         UpdateCaret();
1868                                         return;
1869                                 }
1870                         }
1871                 }
1872
1873                 internal void DumpDoc ()
1874                 {
1875                         Console.WriteLine ("<doc>");
1876                         for (int i = 1; i < lines; i++) {
1877                                 Line line = GetLine (i);
1878                                 Console.WriteLine ("<line no='{0}'>", line.line_no);
1879
1880                                 LineTag tag = line.tags;
1881                                 while (tag != null) {
1882                                         Console.Write ("\t<tag type='{0}' span='{1}->{2}'>", tag.GetType (), tag.start, tag.length);
1883                                         Console.Write (tag.Text ());
1884                                         Console.WriteLine ("</tag>");
1885                                         tag = tag.next;
1886                                 }
1887                                 Console.WriteLine ("</line>");
1888                         }
1889                         Console.WriteLine ("</doc>");
1890                 }
1891
1892                 internal void Draw (Graphics g, Rectangle clip)
1893                 {
1894                         Line line;              // Current line being drawn
1895                         LineTag tag;            // Current tag being drawn
1896                         int start;              // First line to draw
1897                         int end;                // Last line to draw
1898                         StringBuilder text;     // String representing the current line
1899                         int line_no;
1900                         Brush tag_brush;
1901                         Brush current_brush;
1902                         Brush disabled_brush;
1903                         Brush hilight;
1904                         Brush hilight_text;
1905
1906                         // First, figure out from what line to what line we need to draw
1907
1908                         if (multiline) {
1909                                 start = GetLineByPixel(clip.Top + viewport_y, false).line_no;
1910                                 end = GetLineByPixel(clip.Bottom + viewport_y, false).line_no;
1911                         } else {
1912                                 start = GetLineByPixel(clip.Left + viewport_x, false).line_no;
1913                                 end = GetLineByPixel(clip.Right + viewport_x, false).line_no;
1914                         }
1915
1916                         /// Make sure that we aren't drawing one more line then we need to
1917                         line = GetLine (end - 1);
1918                         if (line != null && clip.Bottom == line.Y + line.height + viewport_y)
1919                                 end--;                  
1920
1921                         line_no = start;
1922
1923                         #if Debug
1924                                 DateTime        n = DateTime.Now;
1925                                 Console.WriteLine ("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
1926                                 Console.WriteLine ("CLIP:  {0}", clip);
1927                                 Console.WriteLine ("S: {0}", GetLine (start).text);
1928                                 Console.WriteLine ("E: {0}", GetLine (end).text);
1929                         #endif
1930
1931                         disabled_brush = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorGrayText);
1932                         hilight = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHighlight);
1933                         hilight_text = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHighlightText);
1934
1935                         // Non multiline selection can be handled outside of the loop
1936                         if (!multiline && selection_visible && owner.ShowSelection) {
1937                                 g.FillRectangle (hilight,
1938                                                 selection_start.line.widths [selection_start.pos] +
1939                                                 selection_start.line.X - viewport_x, 
1940                                                 selection_start.line.Y,
1941                                                 (selection_end.line.X + selection_end.line.widths [selection_end.pos]) -
1942                                                 (selection_start.line.X + selection_start.line.widths [selection_start.pos]), 
1943                                                 selection_start.line.height);
1944                         }
1945
1946                         while (line_no <= end) {
1947                                 line = GetLine (line_no);
1948                                 float line_y = line.Y - viewport_y;
1949                                 
1950                                 tag = line.tags;
1951                                 if (!calc_pass) {
1952                                         text = line.text;
1953                                 } else {
1954                                         if (PasswordCache.Length < line.text.Length)
1955                                                 PasswordCache.Append(Char.Parse(password_char), line.text.Length - PasswordCache.Length);
1956                                         else if (PasswordCache.Length > line.text.Length)
1957                                                 PasswordCache.Remove(line.text.Length, PasswordCache.Length - line.text.Length);
1958                                         text = PasswordCache;
1959                                 }
1960
1961                                 int line_selection_start = text.Length + 1;
1962                                 int line_selection_end = text.Length + 1;
1963                                 if (selection_visible && owner.ShowSelection &&
1964                                                 (line_no >= selection_start.line.line_no) &&
1965                                                 (line_no <= selection_end.line.line_no)) {
1966
1967                                         if (line_no == selection_start.line.line_no)
1968                                                 line_selection_start = selection_start.pos + 1;
1969                                         else
1970                                                 line_selection_start = 1;
1971
1972                                         if (line_no == selection_end.line.line_no)
1973                                                 line_selection_end = selection_end.pos + 1;
1974                                         else
1975                                                 line_selection_end = text.Length + 1;
1976
1977                                         if (line_selection_end == line_selection_start) {
1978                                                 // There isn't really selection
1979                                                 line_selection_start = text.Length + 1;
1980                                                 line_selection_end = line_selection_start;
1981                                         } else if (multiline) {
1982                                                 // lets draw some selection baby!!  (non multiline selection is drawn outside the loop)
1983                                                 g.FillRectangle (hilight,
1984                                                                 line.widths [line_selection_start - 1] + line.X - viewport_x, 
1985                                                                 line_y, line.widths [line_selection_end - 1] - line.widths [line_selection_start - 1], 
1986                                                                 line.height);
1987                                         }
1988                                 }
1989
1990                                 current_brush = line.tags.color;
1991                                 while (tag != null) {
1992
1993                                         // Skip empty tags
1994                                         if (tag.length == 0) {
1995                                                 tag = tag.next;
1996                                                 continue;
1997                                         }
1998
1999                                         if (((tag.X + tag.width) < (clip.Left - viewport_x)) && (tag.X > (clip.Right - viewport_x))) {
2000                                                 tag = tag.next;
2001                                                 continue;
2002                                         }
2003
2004                                         if (tag.back_color != null) {
2005                                                 g.FillRectangle (tag.back_color, tag.X + line.X - viewport_x,
2006                                                                 line_y + tag.shift, tag.width, line.height);
2007                                         }
2008
2009                                         tag_brush = tag.color;
2010                                         current_brush = tag_brush;
2011                                         
2012                                         if (!owner.is_enabled) {
2013                                                 Color a = ((SolidBrush) tag.color).Color;
2014                                                 Color b = ThemeEngine.Current.ColorWindowText;
2015
2016                                                 if ((a.R == b.R) && (a.G == b.G) && (a.B == b.B)) {
2017                                                         tag_brush = disabled_brush;
2018                                                 }
2019                                         }
2020
2021                                         int tag_pos = tag.start;
2022                                         current_brush = tag_brush;
2023                                         while (tag_pos < tag.start + tag.length) {
2024                                                 int old_tag_pos = tag_pos;
2025
2026                                                 if (tag_pos >= line_selection_start && tag_pos < line_selection_end) {
2027                                                         current_brush = hilight_text;
2028                                                         tag_pos = Math.Min (tag.end, line_selection_end);
2029                                                 } else if (tag_pos < line_selection_start) {
2030                                                         current_brush = tag.color;
2031                                                         tag_pos = Math.Min (tag.end, line_selection_start);
2032                                                 } else {
2033                                                         current_brush = tag.color;
2034                                                         tag_pos = tag.end;
2035                                                 }
2036
2037                                                 tag.Draw (g, current_brush,
2038                                                                 line.widths [Math.Max (0, old_tag_pos - 1)] + line.X - viewport_x,
2039                                                                 line_y + tag.shift,
2040                                                                 old_tag_pos - 1, Math.Min (tag.length, tag_pos - old_tag_pos),
2041                                                                 text.ToString() );
2042                                         }
2043                                         tag = tag.next;
2044                                 }
2045                                 line_no++;
2046                         }
2047                 }
2048
2049                 private void InsertLineString (Line line, int pos, string s)
2050                 {
2051                         bool carriage_return = false;
2052
2053                         if (s.EndsWith ("\r")) {
2054                                 s = s.Substring (0, s.Length - 1);
2055                                 carriage_return = true;
2056                         }
2057
2058                         InsertString (line, pos, s);
2059
2060                         if (carriage_return) {
2061                                 Line l = GetLine (line.line_no);
2062                                 l.carriage_return = true;
2063                         }
2064                 }
2065
2066                 // Insert multi-line text at the given position; use formatting at insertion point for inserted text
2067                 internal void Insert(Line line, int pos, bool update_caret, string s) {
2068                         int break_index;
2069                         int base_line;
2070                         int old_line_count;
2071                         int count = 1;
2072                         LineTag tag = LineTag.FindTag (line, pos);
2073                         
2074                         SuspendRecalc ();
2075                         
2076                         base_line = line.line_no;
2077                         old_line_count = lines;
2078
2079                         break_index = s.IndexOf ('\n');
2080
2081                         // Bump the text at insertion point a line down if we're inserting more than one line
2082                         if (break_index > -1) {
2083                                 Split(line, pos);
2084                                 line.soft_break = false;
2085                                 // Remainder of start line is now in base_line + 1
2086                         }
2087
2088                         if (break_index == -1)
2089                                 break_index = s.Length;
2090
2091                         InsertLineString (line, pos, s.Substring (0, break_index));
2092                         break_index++;
2093
2094                         while (break_index < s.Length) {
2095                                 bool soft = false;
2096                                 int next_break = s.IndexOf ('\n', break_index);
2097                                 int adjusted_next_break;
2098                                 bool carriage_return = false;
2099
2100                                 if (next_break == -1) {
2101                                         next_break = s.Length;
2102                                         soft = true;
2103                                 }
2104
2105                                 adjusted_next_break = next_break;
2106                                 if (s [next_break - 1] == '\r') {
2107                                         adjusted_next_break--;
2108                                         carriage_return = true;
2109                                 }
2110
2111                                 string line_text = s.Substring (break_index, adjusted_next_break - break_index);
2112                                 Add (base_line + count, line_text, line.alignment, tag.font, tag.color);
2113
2114                                 if (carriage_return) {
2115                                         Line last = GetLine (base_line + count);
2116                                         last.carriage_return = true;
2117
2118                                         if (soft)
2119                                                 last.soft_break = true;
2120                                 } else if (soft) {
2121                                         Line last = GetLine (base_line + count);
2122                                         last.soft_break = true;
2123                                 }
2124
2125                                 count++;
2126                                 break_index = next_break + 1;
2127                         }
2128
2129                         ResumeRecalc (true);
2130
2131                         UpdateView(line, lines - old_line_count + 1, pos);
2132
2133                         if (update_caret) {
2134                                 // Move caret to the end of the inserted text
2135                                 Line l = GetLine (line.line_no + lines - old_line_count);
2136                                 PositionCaret(l, l.text.Length);
2137                                 DisplayCaret ();
2138                         }
2139                 }
2140
2141                 // Inserts a character at the given position
2142                 internal void InsertString(Line line, int pos, string s) {
2143                         InsertString(line.FindTag(pos), pos, s);
2144                 }
2145
2146                 // Inserts a string at the given position
2147                 internal void InsertString(LineTag tag, int pos, string s) {
2148                         Line    line;
2149                         int     len;
2150
2151                         len = s.Length;
2152
2153                         CharCount += len;
2154
2155                         line = tag.line;
2156                         line.text.Insert(pos, s);
2157
2158                         tag = tag.next;
2159                         while (tag != null) {
2160                                 tag.start += len;
2161                                 tag = tag.next;
2162                         }
2163                         line.Grow(len);
2164                         line.recalc = true;
2165
2166                         UpdateView(line, pos);
2167                 }
2168
2169                 // Inserts a string at the caret position
2170                 internal void InsertStringAtCaret(string s, bool move_caret) {
2171
2172                         InsertString (caret.tag, caret.pos, s);
2173
2174                         UpdateView(caret.line, caret.pos);
2175                         if (move_caret) {
2176                                 caret.pos += s.Length;
2177                                 UpdateCaret();
2178                         }
2179                 }
2180
2181
2182
2183                 // Inserts a character at the given position
2184                 internal void InsertChar(Line line, int pos, char ch) {
2185                         InsertChar(line.FindTag(pos), pos, ch);
2186                 }
2187
2188                 // Inserts a character at the given position
2189                 internal void InsertChar(LineTag tag, int pos, char ch) {
2190                         Line    line;
2191
2192                         CharCount++;
2193
2194                         line = tag.line;
2195                         line.text.Insert(pos, ch);
2196
2197                         tag = tag.next;
2198                         while (tag != null) {
2199                                 tag.start++;
2200                                 tag = tag.next;
2201                         }
2202                         line.Grow(1);
2203                         line.recalc = true;
2204
2205                         undo.RecordTyping (line, pos, ch);
2206                         UpdateView(line, pos);
2207                 }
2208
2209                 // Inserts a character at the current caret position
2210                 internal void InsertCharAtCaret(char ch, bool move_caret) {
2211                         /*
2212                         LineTag tag;
2213
2214                         CharCount++;
2215
2216                         caret.line.text.Insert(caret.pos, ch);
2217                         caret.tag.length++;
2218                         
2219                         if (caret.tag.next != null) {
2220                                 tag = caret.tag.next;
2221                                 while (tag != null) {
2222                                         tag.start++;
2223                                         tag = tag.next;
2224                                 }
2225                         }
2226                         caret.line.Grow(1);
2227                         caret.line.recalc = true;
2228                         */
2229                         InsertChar (caret.tag, caret.pos, ch);
2230
2231                         UpdateView(caret.line, caret.pos);
2232                         if (move_caret) {
2233                                 caret.pos++;
2234                                 UpdateCaret();
2235                                 SetSelectionToCaret(true);
2236                         }
2237
2238                 }
2239                 
2240                 internal void InsertImage (Line line, int pos, Image image)
2241                 {
2242                         LineTag next_tag;
2243                         LineTag tag;
2244                         int len;
2245
2246                         len = 1;
2247
2248                         // Just a place holder basically
2249                         line.text.Insert (pos, "I");
2250
2251                         ImageTag image_tag = new ImageTag (line, pos + 1, image);
2252
2253                         tag = LineTag.FindTag (line, pos);
2254                         image_tag.CopyFormattingFrom (tag);
2255                         next_tag = tag.Break (pos + 1);
2256                         image_tag.previous = tag;
2257                         image_tag.next = tag.next;
2258                         tag.next = image_tag;
2259
2260                         //
2261                         // Images tags need to be surrounded by text tags
2262                         //
2263                         if (image_tag.next == null) {
2264                                 image_tag.next = new LineTag (line, pos + 1);
2265                                 image_tag.next.CopyFormattingFrom (tag);
2266                                 image_tag.next.previous = image_tag;
2267                         }
2268
2269                         tag = image_tag.next;
2270                         while (tag != null) {
2271                                 tag.start += len;
2272                                 tag = tag.next;
2273                         }
2274
2275                         line.Grow (len);
2276                         line.recalc = true;
2277
2278                         UpdateView (line, pos);
2279                 }
2280
2281                 internal void DeleteMultiline (Line start_line, int pos, int length)
2282                 {
2283                         Marker start = new Marker ();
2284                         Marker end = new Marker ();
2285                         int start_index = LineTagToCharIndex (start_line, pos);
2286
2287                         start.line = start_line;
2288                         start.pos = pos;
2289                         start.tag = LineTag.FindTag (start_line, pos);
2290
2291                         CharIndexToLineTag (start_index + length, out end.line,
2292                                         out end.tag, out end.pos);
2293
2294                         SuspendUpdate ();
2295
2296                         if (start.line == end.line) {
2297                                 DeleteChars (start.tag, pos, end.pos - pos);
2298                         } else {
2299
2300                                 // Delete first and last lines
2301                                 DeleteChars (start.tag, start.pos, start.line.text.Length - start.pos);
2302                                 DeleteChars (end.line.tags, 0, end.pos);
2303
2304                                 int current = start.line.line_no + 1;
2305                                 if (current < end.line.line_no) {
2306                                         for (int i = end.line.line_no - 1; i >= current; i--) {
2307                                                 Delete (i);
2308                                         }
2309                                 }
2310
2311                                 // BIG FAT WARNING - selection_end.line might be stale due 
2312                                 // to the above Delete() call. DONT USE IT before hitting the end of this method!
2313
2314                                 // Join start and end
2315                                 Combine (start.line.line_no, current);
2316                         }
2317
2318                         ResumeUpdate (true);
2319                 }
2320
2321                 
2322                 // Deletes n characters at the given position; it will not delete past line limits
2323                 // pos is 0-based
2324                 internal void DeleteChars(LineTag tag, int pos, int count) {
2325                         Line    line;
2326                         bool    streamline;
2327
2328                         streamline = false;
2329                         line = tag.line;
2330
2331                         CharCount -= count;
2332
2333                         if (pos == line.text.Length) {
2334                                 return;
2335                         }
2336
2337                         line.text.Remove(pos, count);
2338
2339                         // Make sure the tag points to the right spot
2340                         while ((tag != null) && (tag.end) < pos) {
2341                                 tag = tag.next;
2342                         }
2343
2344                         if (tag == null) {
2345                                 return;
2346                         }
2347
2348                         // Check if we're crossing tag boundaries
2349                         if ((pos + count) > (tag.start + tag.length - 1)) {
2350                                 int     left;
2351
2352                                 // We have to delete cross tag boundaries
2353                                 streamline = true;
2354                                 left = count;
2355
2356                                 left -= tag.start + tag.length - pos - 1;
2357
2358                                 tag = tag.next;
2359                                 while ((tag != null) && (left > 0)) {
2360                                         tag.start -= count - left;
2361
2362                                         if (tag.length > left) {
2363                                                 left = 0;
2364                                         } else {
2365                                                 left -= tag.length;
2366                                                 tag = tag.next;
2367                                         }
2368
2369                                 }
2370                         } else {
2371                                 // We got off easy, same tag
2372
2373                                 if (tag.length == 0) {
2374                                         streamline = true;
2375                                 }
2376                         }
2377
2378                         // Delete empty orphaned tags at the end
2379                         LineTag walk = tag;
2380                         while (walk != null && walk.next != null && walk.next.length == 0) {
2381                                 LineTag t = walk;
2382                                 walk.next = walk.next.next;
2383                                 if (walk.next != null)
2384                                         walk.next.previous = t;
2385                                 walk = walk.next;
2386                         }
2387
2388                         // Adjust the start point of any tags following
2389                         if (tag != null) {
2390                                 tag = tag.next;
2391                                 while (tag != null) {
2392                                         tag.start -= count;
2393                                         tag = tag.next;
2394                                 }
2395                         }
2396
2397                         line.recalc = true;
2398                         if (streamline) {
2399                                 line.Streamline(lines);
2400                         }
2401
2402                         UpdateView(line, pos);
2403                 }
2404
2405                 // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
2406                 internal void DeleteChar(LineTag tag, int pos, bool forward) {
2407                         Line    line;
2408                         bool    streamline;
2409
2410                         CharCount--;
2411
2412                         streamline = false;
2413                         line = tag.line;
2414
2415                         if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true)) {
2416                                 return;
2417                         }
2418
2419
2420                         if (forward) {
2421                                 line.text.Remove(pos, 1);
2422
2423                                 while ((tag != null) && (tag.start + tag.length - 1) <= pos) {
2424                                         tag = tag.next;
2425                                 }
2426
2427                                 if (tag == null) {
2428                                         return;
2429                                 }
2430
2431                                 //      tag.length--;
2432
2433                                 if (tag.length == 0) {
2434                                         streamline = true;
2435                                 }
2436                         } else {
2437                                 pos--;
2438                                 line.text.Remove(pos, 1);
2439                                 if (pos >= (tag.start - 1)) {
2440                                         //              tag.length--;
2441                                         if (tag.length == 0) {
2442                                                 streamline = true;
2443                                         }
2444                                 } else if (tag.previous != null) {
2445                                         //              tag.previous.length--;
2446                                         if (tag.previous.length == 0) {
2447                                                 streamline = true;
2448                                         }
2449                                 }
2450                         }
2451
2452                         // Delete empty orphaned tags at the end
2453                         LineTag walk = tag;
2454                         while (walk != null && walk.next != null && walk.next.length == 0) {
2455                                 LineTag t = walk;
2456                                 walk.next = walk.next.next;
2457                                 if (walk.next != null)
2458                                         walk.next.previous = t;
2459                                 walk = walk.next;
2460                         }
2461
2462                         tag = tag.next;
2463                         while (tag != null) {
2464                                 tag.start--;
2465                                 tag = tag.next;
2466                         }
2467                         line.recalc = true;
2468                         if (streamline) {
2469                                 line.Streamline(lines);
2470                         }
2471
2472                         UpdateView(line, pos);
2473                 }
2474
2475                 // Combine two lines
2476                 internal void Combine(int FirstLine, int SecondLine) {
2477                         Combine(GetLine(FirstLine), GetLine(SecondLine));
2478                 }
2479
2480                 internal void Combine(Line first, Line second) {
2481                         LineTag last;
2482                         int     shift;
2483
2484                         // Combine the two tag chains into one
2485                         last = first.tags;
2486
2487                         // Maintain the line ending style
2488                         first.soft_break = second.soft_break;
2489
2490                         while (last.next != null) {
2491                                 last = last.next;
2492                         }
2493
2494                         // need to get the shift before setting the next tag since that effects length
2495                         shift = last.start + last.length - 1;
2496                         last.next = second.tags;
2497                         last.next.previous = last;
2498
2499                         // Fix up references within the chain
2500                         last = last.next;
2501                         while (last != null) {
2502                                 last.line = first;
2503                                 last.start += shift;
2504                                 last = last.next;
2505                         }
2506
2507                         // Combine both lines' strings
2508                         first.text.Insert(first.text.Length, second.text.ToString());
2509                         first.Grow(first.text.Length);
2510
2511                         // Remove the reference to our (now combined) tags from the doomed line
2512                         second.tags = null;
2513
2514                         // Renumber lines
2515                         DecrementLines(first.line_no + 2);      // first.line_no + 1 will be deleted, so we need to start renumbering one later
2516
2517                         // Mop up
2518                         first.recalc = true;
2519                         first.height = 0;       // This forces RecalcDocument/UpdateView to redraw from this line on
2520                         first.Streamline(lines);
2521
2522                         // Update Caret, Selection, etc
2523                         if (caret.line == second) {
2524                                 caret.Combine(first, shift);
2525                         }
2526                         if (selection_anchor.line == second) {
2527                                 selection_anchor.Combine(first, shift);
2528                         }
2529                         if (selection_start.line == second) {
2530                                 selection_start.Combine(first, shift);
2531                         }
2532                         if (selection_end.line == second) {
2533                                 selection_end.Combine(first, shift);
2534                         }
2535
2536                         #if Debug
2537                                 Line    check_first;
2538                                 Line    check_second;
2539
2540                                 check_first = GetLine(first.line_no);
2541                                 check_second = GetLine(check_first.line_no + 1);
2542
2543                                 Console.WriteLine("Pre-delete: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
2544                         #endif
2545
2546                         this.Delete(second);
2547
2548                         #if Debug
2549                                 check_first = GetLine(first.line_no);
2550                                 check_second = GetLine(check_first.line_no + 1);
2551
2552                                 Console.WriteLine("Post-delete Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
2553                         #endif
2554                 }
2555
2556                 // Split the line at the position into two
2557                 internal void Split(int LineNo, int pos) {
2558                         Line    line;
2559                         LineTag tag;
2560
2561                         line = GetLine(LineNo);
2562                         tag = LineTag.FindTag(line, pos);
2563                         Split(line, tag, pos, false);
2564                 }
2565
2566                 internal void Split(Line line, int pos) {
2567                         LineTag tag;
2568
2569                         tag = LineTag.FindTag(line, pos);
2570                         Split(line, tag, pos, false);
2571                 }
2572
2573                 ///<summary>Split line at given tag and position into two lines</summary>
2574                 ///<param name="soft">True if the split should be marked as 'soft', indicating that it can be contracted 
2575                 ///if more space becomes available on previous line</param>
2576                 internal void Split(Line line, LineTag tag, int pos, bool soft) {
2577                         LineTag new_tag;
2578                         Line    new_line;
2579                         bool    move_caret;
2580                         bool    move_sel_start;
2581                         bool    move_sel_end;
2582
2583                         move_caret = false;
2584                         move_sel_start = false;
2585                         move_sel_end = false;
2586
2587                         // Adjust selection and cursors
2588                         if (caret.line == line && caret.pos >= pos) {
2589                                 move_caret = true;
2590                         }
2591                         if (selection_start.line == line && selection_start.pos > pos) {
2592                                 move_sel_start = true;
2593                         }
2594
2595                         if (selection_end.line == line && selection_end.pos > pos) {
2596                                 move_sel_end = true;
2597                         }
2598
2599                         // cover the easy case first
2600                         if (pos == line.text.Length) {
2601                                 Add(line.line_no + 1, "", line.alignment, tag.font, tag.color);
2602
2603                                 new_line = GetLine(line.line_no + 1);
2604
2605                                 line.carriage_return = false;
2606                                 new_line.carriage_return = line.carriage_return;
2607                                 new_line.soft_break = soft;
2608                                 
2609                                 if (move_caret) {
2610                                         caret.line = new_line;
2611                                         caret.tag = new_line.tags;
2612                                         caret.pos = 0;
2613                                 }
2614
2615                                 if (move_sel_start) {
2616                                         selection_start.line = new_line;
2617                                         selection_start.pos = 0;
2618                                         selection_start.tag = new_line.tags;
2619                                 }
2620
2621                                 if (move_sel_end) {
2622                                         selection_end.line = new_line;
2623                                         selection_end.pos = 0;
2624                                         selection_end.tag = new_line.tags;
2625                                 }
2626                                 return;
2627                         }
2628
2629                         // We need to move the rest of the text into the new line
2630                         Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.font, tag.color);
2631
2632                         // Now transfer our tags from this line to the next
2633                         new_line = GetLine(line.line_no + 1);
2634
2635                         line.carriage_return = false;
2636                         new_line.carriage_return = line.carriage_return;
2637                         new_line.soft_break = soft;
2638
2639                         line.recalc = true;
2640                         new_line.recalc = true;
2641
2642                         if ((tag.start - 1) == pos) {
2643                                 int     shift;
2644
2645                                 // We can simply break the chain and move the tag into the next line
2646                                 if (tag == line.tags) {
2647                                         new_tag = new LineTag(line, 1);
2648                                         new_tag.CopyFormattingFrom (tag);
2649                                         line.tags = new_tag;
2650                                 }
2651
2652                                 if (tag.previous != null) {
2653                                         tag.previous.next = null;
2654                                 }
2655                                 new_line.tags = tag;
2656                                 tag.previous = null;
2657                                 tag.line = new_line;
2658
2659                                 // Walk the list and correct the start location of the tags we just bumped into the next line
2660                                 shift = tag.start - 1;
2661
2662                                 new_tag = tag;
2663                                 while (new_tag != null) {
2664                                         new_tag.start -= shift;
2665                                         new_tag.line = new_line;
2666                                         new_tag = new_tag.next;
2667                                 }
2668                         } else {
2669                                 int     shift;
2670
2671                                 new_tag = new LineTag (new_line, 1);                    
2672                                 new_tag.next = tag.next;
2673                                 new_tag.CopyFormattingFrom (tag);
2674                                 new_line.tags = new_tag;
2675                                 if (new_tag.next != null) {
2676                                         new_tag.next.previous = new_tag;
2677                                 }
2678                                 tag.next = null;
2679
2680                                 shift = pos;
2681                                 new_tag = new_tag.next;
2682                                 while (new_tag != null) {
2683                                         new_tag.start -= shift;
2684                                         new_tag.line = new_line;
2685                                         new_tag = new_tag.next;
2686
2687                                 }
2688                         }
2689
2690                         if (move_caret) {
2691                                 caret.line = new_line;
2692                                 caret.pos = caret.pos - pos;
2693                                 caret.tag = caret.line.FindTag(caret.pos);
2694                         }
2695
2696                         if (move_sel_start) {
2697                                 selection_start.line = new_line;
2698                                 selection_start.pos = selection_start.pos - pos;
2699                                 selection_start.tag = new_line.FindTag(selection_start.pos);
2700                         }
2701
2702                         if (move_sel_end) {
2703                                 selection_end.line = new_line;
2704                                 selection_end.pos = selection_end.pos - pos;
2705                                 selection_end.tag = new_line.FindTag(selection_end.pos);
2706                         }
2707
2708                         CharCount -= line.text.Length - pos;
2709                         line.text.Remove(pos, line.text.Length - pos);
2710                 }
2711
2712                 // Adds a line of text, with given font.
2713                 // Bumps any line at that line number that already exists down
2714                 internal void Add(int LineNo, string Text, Font font, SolidBrush color) {
2715                         Add(LineNo, Text, HorizontalAlignment.Left, font, color);
2716                 }
2717
2718                 internal void Add(int LineNo, string Text, HorizontalAlignment align, Font font, SolidBrush color) {
2719                         Line    add;
2720                         Line    line;
2721                         int     line_no;
2722
2723                         CharCount += Text.Length;
2724
2725                         if (LineNo<1 || Text == null) {
2726                                 if (LineNo<1) {
2727                                         throw new ArgumentNullException("LineNo", "Line numbers must be positive");
2728                                 } else {
2729                                         throw new ArgumentNullException("Text", "Cannot insert NULL line");
2730                                 }
2731                         }
2732
2733                         add = new Line (this, LineNo, Text, align, font, color);
2734
2735                         line = document;
2736                         while (line != sentinel) {
2737                                 add.parent = line;
2738                                 line_no = line.line_no;
2739
2740                                 if (LineNo > line_no) {
2741                                         line = line.right;
2742                                 } else if (LineNo < line_no) {
2743                                         line = line.left;
2744                                 } else {
2745                                         // Bump existing line numbers; walk all nodes to the right of this one and increment line_no
2746                                         IncrementLines(line.line_no);
2747                                         line = line.left;
2748                                 }
2749                         }
2750
2751                         add.left = sentinel;
2752                         add.right = sentinel;
2753
2754                         if (add.parent != null) {
2755                                 if (LineNo > add.parent.line_no) {
2756                                         add.parent.right = add;
2757                                 } else {
2758                                         add.parent.left = add;
2759                                 }
2760                         } else {
2761                                 // Root node
2762                                 document = add;
2763                         }
2764
2765                         RebalanceAfterAdd(add);
2766
2767                         lines++;
2768                 }
2769
2770                 internal virtual void Clear() {
2771                         lines = 0;
2772                         CharCount = 0;
2773                         document = sentinel;
2774                 }
2775
2776                 public virtual object Clone() {
2777                         Document clone;
2778
2779                         clone = new Document(null);
2780
2781                         clone.lines = this.lines;
2782                         clone.document = (Line)document.Clone();
2783
2784                         return clone;
2785                 }
2786
2787                 internal void Delete(int LineNo) {
2788                         Line    line;
2789
2790                         if (LineNo>lines) {
2791                                 return;
2792                         }
2793
2794                         line = GetLine(LineNo);
2795
2796                         CharCount -= line.text.Length;
2797
2798                         DecrementLines(LineNo + 1);
2799                         Delete(line);
2800                 }
2801
2802                 internal void Delete(Line line1) {
2803                         Line    line2;// = new Line();
2804                         Line    line3;
2805
2806                         if ((line1.left == sentinel) || (line1.right == sentinel)) {
2807                                 line3 = line1;
2808                         } else {
2809                                 line3 = line1.right;
2810                                 while (line3.left != sentinel) {
2811                                         line3 = line3.left;
2812                                 }
2813                         }
2814
2815                         if (line3.left != sentinel) {
2816                                 line2 = line3.left;
2817                         } else {
2818                                 line2 = line3.right;
2819                         }
2820
2821                         line2.parent = line3.parent;
2822                         if (line3.parent != null) {
2823                                 if(line3 == line3.parent.left) {
2824                                         line3.parent.left = line2;
2825                                 } else {
2826                                         line3.parent.right = line2;
2827                                 }
2828                         } else {
2829                                 document = line2;
2830                         }
2831
2832                         if (line3 != line1) {
2833                                 LineTag tag;
2834
2835                                 if (selection_start.line == line3) {
2836                                         selection_start.line = line1;
2837                                 }
2838
2839                                 if (selection_end.line == line3) {
2840                                         selection_end.line = line1;
2841                                 }
2842
2843                                 if (selection_anchor.line == line3) {
2844                                         selection_anchor.line = line1;
2845                                 }
2846
2847                                 if (caret.line == line3) {
2848                                         caret.line = line1;
2849                                 }
2850
2851
2852                                 line1.alignment = line3.alignment;
2853                                 line1.ascent = line3.ascent;
2854                                 line1.hanging_indent = line3.hanging_indent;
2855                                 line1.height = line3.height;
2856                                 line1.indent = line3.indent;
2857                                 line1.line_no = line3.line_no;
2858                                 line1.recalc = line3.recalc;
2859                                 line1.right_indent = line3.right_indent;
2860                                 line1.soft_break = line3.soft_break;
2861                                 line1.space = line3.space;
2862                                 line1.tags = line3.tags;
2863                                 line1.text = line3.text;
2864                                 line1.widths = line3.widths;
2865                                 line1.offset = line3.offset;
2866
2867                                 tag = line1.tags;
2868                                 while (tag != null) {
2869                                         tag.line = line1;
2870                                         tag = tag.next;
2871                                 }
2872                         }
2873
2874                         if (line3.color == LineColor.Black)
2875                                 RebalanceAfterDelete(line2);
2876
2877                         this.lines--;
2878                 }
2879
2880                 // Invalidate a section of the document to trigger redraw
2881                 internal void Invalidate(Line start, int start_pos, Line end, int end_pos) {
2882                         Line    l1;
2883                         Line    l2;
2884                         int     p1;
2885                         int     p2;
2886
2887                         if ((start == end) && (start_pos == end_pos)) {
2888                                 return;
2889                         }
2890
2891                         if (end_pos == -1) {
2892                                 end_pos = end.text.Length;
2893                         }
2894         
2895                         // figure out what's before what so the logic below is straightforward
2896                         if (start.line_no < end.line_no) {
2897                                 l1 = start;
2898                                 p1 = start_pos;
2899
2900                                 l2 = end;
2901                                 p2 = end_pos;
2902                         } else if (start.line_no > end.line_no) {
2903                                 l1 = end;
2904                                 p1 = end_pos;
2905
2906                                 l2 = start;
2907                                 p2 = start_pos;
2908                         } else {
2909                                 if (start_pos < end_pos) {
2910                                         l1 = start;
2911                                         p1 = start_pos;
2912
2913                                         l2 = end;
2914                                         p2 = end_pos;
2915                                 } else {
2916                                         l1 = end;
2917                                         p1 = end_pos;
2918
2919                                         l2 = start;
2920                                         p2 = start_pos;
2921                                 }
2922
2923                                 int endpoint = (int) l1.widths [p2];
2924                                 if (p2 == l1.text.Length + 1) {
2925                                         endpoint = (int) viewport_width;
2926                                 }
2927
2928                                 #if Debug
2929                                         Console.WriteLine("Invaliding backwards from {0}:{1} to {2}:{3}   {4}",
2930                                                         l1.line_no, p1, l2.line_no, p2,
2931                                                         new Rectangle(
2932                                                                 (int)l1.widths[p1] + l1.X - viewport_x, 
2933                                                                 l1.Y - viewport_y, 
2934                                                                 (int)l1.widths[p2], 
2935                                                                 l1.height
2936                                                                 )
2937                                                 );
2938                                 #endif
2939
2940                                 owner.Invalidate(new Rectangle (
2941                                         (int)l1.widths[p1] + l1.X - viewport_x, 
2942                                         l1.Y - viewport_y, 
2943                                         endpoint - (int)l1.widths[p1] + 1, 
2944                                         l1.height));
2945                                 return;
2946                         }
2947
2948                         #if Debug
2949                                 Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Start  => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height);
2950                                 Console.WriteLine ("invalidate start line:  {0}  position:  {1}", l1.text, p1);
2951                         #endif
2952
2953                         // Three invalidates:
2954                         // First line from start
2955                         owner.Invalidate(new Rectangle((int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
2956
2957                         
2958                         // lines inbetween
2959                         if ((l1.line_no + 1) < l2.line_no) {
2960                                 int     y;
2961
2962                                 y = GetLine(l1.line_no + 1).Y;
2963                                 owner.Invalidate(new Rectangle(0, y - viewport_y, viewport_width, l2.Y - y));
2964
2965                                 #if Debug
2966                                         Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Middle => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, 0, y - viewport_y, viewport_width, l2.Y - y);
2967                                 #endif
2968                         }
2969                         
2970
2971                         // Last line to end
2972                         owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height));
2973                         #if Debug
2974                                 Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} End    => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height);
2975
2976                         #endif
2977                 }
2978
2979                 /// <summary>Select text around caret</summary>
2980                 internal void ExpandSelection(CaretSelection mode, bool to_caret) {
2981                         if (to_caret) {
2982                                 // We're expanding the selection to the caret position
2983                                 switch(mode) {
2984                                         case CaretSelection.Line: {
2985                                                 // Invalidate the selection delta
2986                                                 if (caret > selection_prev) {
2987                                                         Invalidate(selection_prev.line, 0, caret.line, caret.line.text.Length);
2988                                                 } else {
2989                                                         Invalidate(selection_prev.line, selection_prev.line.text.Length, caret.line, 0);
2990                                                 }
2991
2992                                                 if (caret.line.line_no <= selection_anchor.line.line_no) {
2993                                                         selection_start.line = caret.line;
2994                                                         selection_start.tag = caret.line.tags;
2995                                                         selection_start.pos = 0;
2996
2997                                                         selection_end.line = selection_anchor.line;
2998                                                         selection_end.tag = selection_anchor.tag;
2999                                                         selection_end.pos = selection_anchor.pos;
3000
3001                                                         selection_end_anchor = true;
3002                                                 } else {
3003                                                         selection_start.line = selection_anchor.line;
3004                                                         selection_start.pos = selection_anchor.height;
3005                                                         selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
3006
3007                                                         selection_end.line = caret.line;
3008                                                         selection_end.tag = caret.line.tags;
3009                                                         selection_end.pos = caret.line.text.Length;
3010
3011                                                         selection_end_anchor = false;
3012                                                 }
3013                                                 selection_prev.line = caret.line;
3014                                                 selection_prev.tag = caret.tag;
3015                                                 selection_prev.pos = caret.pos;
3016
3017                                                 break;
3018                                         }
3019
3020                                         case CaretSelection.Word: {
3021                                                 int     start_pos;
3022                                                 int     end_pos;
3023
3024                                                 start_pos = FindWordSeparator(caret.line, caret.pos, false);
3025                                                 end_pos = FindWordSeparator(caret.line, caret.pos, true);
3026
3027                                                 
3028                                                 // Invalidate the selection delta
3029                                                 if (caret > selection_prev) {
3030                                                         Invalidate(selection_prev.line, selection_prev.pos, caret.line, end_pos);
3031                                                 } else {
3032                                                         Invalidate(selection_prev.line, selection_prev.pos, caret.line, start_pos);
3033                                                 }
3034                                                 if (caret < selection_anchor) {
3035                                                         selection_start.line = caret.line;
3036                                                         selection_start.tag = caret.line.FindTag(start_pos);
3037                                                         selection_start.pos = start_pos;
3038
3039                                                         selection_end.line = selection_anchor.line;
3040                                                         selection_end.tag = selection_anchor.tag;
3041                                                         selection_end.pos = selection_anchor.pos;
3042
3043                                                         selection_prev.line = caret.line;
3044                                                         selection_prev.tag = caret.tag;
3045                                                         selection_prev.pos = start_pos;
3046
3047                                                         selection_end_anchor = true;
3048                                                 } else {
3049                                                         selection_start.line = selection_anchor.line;
3050                                                         selection_start.pos = selection_anchor.height;
3051                                                         selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
3052
3053                                                         selection_end.line = caret.line;
3054                                                         selection_end.tag = caret.line.FindTag(end_pos);
3055                                                         selection_end.pos = end_pos;
3056
3057                                                         selection_prev.line = caret.line;
3058                                                         selection_prev.tag = caret.tag;
3059                                                         selection_prev.pos = end_pos;
3060
3061                                                         selection_end_anchor = false;
3062                                                 }
3063                                                 break;
3064                                         }
3065
3066                                         case CaretSelection.Position: {
3067                                                 SetSelectionToCaret(false);
3068                                                 return;
3069                                         }
3070                                 }
3071                         } else {
3072                                 // We're setting the selection 'around' the caret position
3073                                 switch(mode) {
3074                                         case CaretSelection.Line: {
3075                                                 this.Invalidate(caret.line, 0, caret.line, caret.line.text.Length);
3076
3077                                                 selection_start.line = caret.line;
3078                                                 selection_start.tag = caret.line.tags;
3079                                                 selection_start.pos = 0;
3080
3081                                                 selection_end.line = caret.line;
3082                                                 selection_end.pos = caret.line.text.Length;
3083                                                 selection_end.tag = caret.line.FindTag(selection_end.pos);
3084
3085                                                 selection_anchor.line = selection_end.line;
3086                                                 selection_anchor.tag = selection_end.tag;
3087                                                 selection_anchor.pos = selection_end.pos;
3088                                                 selection_anchor.height = 0;
3089
3090                                                 selection_prev.line = caret.line;
3091                                                 selection_prev.tag = caret.tag;
3092                                                 selection_prev.pos = caret.pos;
3093
3094                                                 this.selection_end_anchor = true;
3095
3096                                                 break;
3097                                         }
3098
3099                                         case CaretSelection.Word: {
3100                                                 int     start_pos;
3101                                                 int     end_pos;
3102
3103                                                 start_pos = FindWordSeparator(caret.line, caret.pos, false);
3104                                                 end_pos = FindWordSeparator(caret.line, caret.pos, true);
3105
3106                                                 this.Invalidate(selection_start.line, start_pos, caret.line, end_pos);
3107
3108                                                 selection_start.line = caret.line;
3109                                                 selection_start.tag = caret.line.FindTag(start_pos);
3110                                                 selection_start.pos = start_pos;
3111
3112                                                 selection_end.line = caret.line;
3113                                                 selection_end.tag = caret.line.FindTag(end_pos);
3114                                                 selection_end.pos = end_pos;
3115
3116                                                 selection_anchor.line = selection_end.line;
3117                                                 selection_anchor.tag = selection_end.tag;
3118                                                 selection_anchor.pos = selection_end.pos;
3119                                                 selection_anchor.height = start_pos;
3120
3121                                                 selection_prev.line = caret.line;
3122                                                 selection_prev.tag = caret.tag;
3123                                                 selection_prev.pos = caret.pos;
3124
3125                                                 this.selection_end_anchor = true;
3126
3127                                                 break;
3128                                         }
3129                                 }
3130                         }
3131
3132                         SetSelectionVisible (!(selection_start == selection_end));
3133                 }
3134
3135                 internal void SetSelectionToCaret(bool start) {
3136                         if (start) {
3137                                 // Invalidate old selection; selection is being reset to empty
3138                                 this.Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3139
3140                                 selection_start.line = caret.line;
3141                                 selection_start.tag = caret.tag;
3142                                 selection_start.pos = caret.pos;
3143
3144                                 // start always also selects end
3145                                 selection_end.line = caret.line;
3146                                 selection_end.tag = caret.tag;
3147                                 selection_end.pos = caret.pos;
3148
3149                                 selection_anchor.line = caret.line;
3150                                 selection_anchor.tag = caret.tag;
3151                                 selection_anchor.pos = caret.pos;
3152                         } else {
3153                                 // Invalidate from previous end to caret (aka new end)
3154                                 if (selection_end_anchor) {
3155                                         if (selection_start != caret) {
3156                                                 this.Invalidate(selection_start.line, selection_start.pos, caret.line, caret.pos);
3157                                         }
3158                                 } else {
3159                                         if (selection_end != caret) {
3160                                                 this.Invalidate(selection_end.line, selection_end.pos, caret.line, caret.pos);
3161                                         }
3162                                 }
3163
3164                                 if (caret < selection_anchor) {
3165                                         selection_start.line = caret.line;
3166                                         selection_start.tag = caret.tag;
3167                                         selection_start.pos = caret.pos;
3168
3169                                         selection_end.line = selection_anchor.line;
3170                                         selection_end.tag = selection_anchor.tag;
3171                                         selection_end.pos = selection_anchor.pos;
3172
3173                                         selection_end_anchor = true;
3174                                 } else {
3175                                         selection_start.line = selection_anchor.line;
3176                                         selection_start.tag = selection_anchor.tag;
3177                                         selection_start.pos = selection_anchor.pos;
3178
3179                                         selection_end.line = caret.line;
3180                                         selection_end.tag = caret.tag;
3181                                         selection_end.pos = caret.pos;
3182
3183                                         selection_end_anchor = false;
3184                                 }
3185                         }
3186
3187                         SetSelectionVisible (!(selection_start == selection_end));
3188                 }
3189
3190                 internal void SetSelection(Line start, int start_pos, Line end, int end_pos) {
3191                         if (selection_visible) {
3192                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3193                         }
3194
3195                         if ((end.line_no < start.line_no) || ((end == start) && (end_pos <= start_pos))) {
3196                                 selection_start.line = end;
3197                                 selection_start.tag = LineTag.FindTag(end, end_pos);
3198                                 selection_start.pos = end_pos;
3199
3200                                 selection_end.line = start;
3201                                 selection_end.tag = LineTag.FindTag(start, start_pos);
3202                                 selection_end.pos = start_pos;
3203
3204                                 selection_end_anchor = true;
3205                         } else {
3206                                 selection_start.line = start;
3207                                 selection_start.tag = LineTag.FindTag(start, start_pos);
3208                                 selection_start.pos = start_pos;
3209
3210                                 selection_end.line = end;
3211                                 selection_end.tag = LineTag.FindTag(end, end_pos);
3212                                 selection_end.pos = end_pos;
3213
3214                                 selection_end_anchor = false;
3215                         }
3216
3217                         selection_anchor.line = start;
3218                         selection_anchor.tag = selection_start.tag;
3219                         selection_anchor.pos = start_pos;
3220
3221                         if (((start == end) && (start_pos == end_pos)) || start == null || end == null) {
3222                                 SetSelectionVisible (false);
3223                         } else {
3224                                 SetSelectionVisible (true);
3225                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3226                         }
3227                 }
3228
3229                 internal void SetSelectionStart(Line start, int start_pos) {
3230                         // Invalidate from the previous to the new start pos
3231                         Invalidate(selection_start.line, selection_start.pos, start, start_pos);
3232
3233                         selection_start.line = start;
3234                         selection_start.pos = start_pos;
3235                         selection_start.tag = LineTag.FindTag(start, start_pos);
3236
3237                         selection_anchor.line = start;
3238                         selection_anchor.pos = start_pos;
3239                         selection_anchor.tag = selection_start.tag;
3240
3241                         selection_end_anchor = false;
3242
3243                         
3244                         if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
3245                                 SetSelectionVisible (true);
3246                         } else {
3247                                 SetSelectionVisible (false);
3248                         }
3249
3250                         Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3251                 }
3252
3253                 internal void SetSelectionStart(int character_index) {
3254                         Line    line;
3255                         LineTag tag;
3256                         int     pos;
3257
3258                         if (character_index < 0) {
3259                                 return;
3260                         }
3261
3262                         CharIndexToLineTag(character_index, out line, out tag, out pos);
3263                         SetSelectionStart(line, pos);
3264                 }
3265
3266                 internal void SetSelectionEnd(Line end, int end_pos) {
3267
3268                         if (end == selection_end.line && end_pos == selection_start.pos) {
3269                                 selection_anchor.line = selection_start.line;
3270                                 selection_anchor.tag = selection_start.tag;
3271                                 selection_anchor.pos = selection_start.pos;
3272
3273                                 selection_end.line = selection_start.line;
3274                                 selection_end.tag = selection_start.tag;
3275                                 selection_end.pos = selection_start.pos;
3276
3277                                 selection_end_anchor = false;
3278                         } else if ((end.line_no < selection_anchor.line.line_no) || ((end == selection_anchor.line) && (end_pos <= selection_anchor.pos))) {
3279                                 selection_start.line = end;
3280                                 selection_start.tag = LineTag.FindTag(end, end_pos);
3281                                 selection_start.pos = end_pos;
3282
3283                                 selection_end.line = selection_anchor.line;
3284                                 selection_end.tag = selection_anchor.tag;
3285                                 selection_end.pos = selection_anchor.pos;
3286
3287                                 selection_end_anchor = true;
3288                         } else {
3289                                 selection_start.line = selection_anchor.line;
3290                                 selection_start.tag = selection_anchor.tag;
3291                                 selection_start.pos = selection_anchor.pos;
3292
3293                                 selection_end.line = end;
3294                                 selection_end.tag = LineTag.FindTag(end, end_pos);
3295                                 selection_end.pos = end_pos;
3296
3297                                 selection_end_anchor = false;
3298                         }
3299
3300                         if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
3301                                 SetSelectionVisible (true);
3302                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3303                         } else {
3304                                 SetSelectionVisible (false);
3305                                 // ?? Do I need to invalidate here, tests seem to work without it, but I don't think they should :-s
3306                         }
3307                 }
3308
3309                 internal void SetSelectionEnd(int character_index) {
3310                         Line    line;
3311                         LineTag tag;
3312                         int     pos;
3313
3314                         if (character_index < 0) {
3315                                 return;
3316                         }
3317
3318                         CharIndexToLineTag(character_index, out line, out tag, out pos);
3319                         SetSelectionEnd(line, pos);
3320                 }
3321
3322                 internal void SetSelection(Line start, int start_pos) {
3323                         if (selection_visible) {
3324                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3325                         }
3326
3327                         selection_start.line = start;
3328                         selection_start.pos = start_pos;
3329                         selection_start.tag = LineTag.FindTag(start, start_pos);
3330
3331                         selection_end.line = start;
3332                         selection_end.tag = selection_start.tag;
3333                         selection_end.pos = start_pos;
3334
3335                         selection_anchor.line = start;
3336                         selection_anchor.tag = selection_start.tag;
3337                         selection_anchor.pos = start_pos;
3338
3339                         selection_end_anchor = false;
3340                         SetSelectionVisible (false);
3341                 }
3342
3343                 internal void InvalidateSelectionArea() {
3344                         Invalidate (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3345                 }
3346
3347                 // Return the current selection, as string
3348                 internal string GetSelection() {
3349                         // We return String.Empty if there is no selection
3350                         if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
3351                                 return string.Empty;
3352                         }
3353
3354                         if (selection_start.line == selection_end.line) {
3355                                 return selection_start.line.text.ToString (selection_start.pos, selection_end.pos - selection_start.pos);
3356                         } else {
3357                                 StringBuilder   sb;
3358                                 int             i;
3359                                 int             start;
3360                                 int             end;
3361
3362                                 sb = new StringBuilder();
3363                                 start = selection_start.line.line_no;
3364                                 end = selection_end.line.line_no;
3365
3366                                 sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos) + Environment.NewLine);
3367
3368                                 if ((start + 1) < end) {
3369                                         for (i = start + 1; i < end; i++) {
3370                                                 sb.Append(GetLine(i).text.ToString() + Environment.NewLine);
3371                                         }
3372                                 }
3373
3374                                 sb.Append(selection_end.line.text.ToString(0, selection_end.pos));
3375
3376                                 return sb.ToString();
3377                         }
3378                 }
3379
3380                 internal void ReplaceSelection(string s, bool select_new) {
3381                         int             i;
3382
3383                         int selection_pos_on_line = selection_start.pos;
3384                         int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos);
3385                         SuspendRecalc ();
3386
3387                         // First, delete any selected text
3388                         if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) {
3389                                 if (selection_start.line == selection_end.line) {
3390                                         undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3391
3392                                         DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
3393
3394                                         // The tag might have been removed, we need to recalc it
3395                                         selection_start.tag = selection_start.line.FindTag(selection_start.pos);
3396                                 } else {
3397                                         int             start;
3398                                         int             end;
3399
3400                                         start = selection_start.line.line_no;
3401                                         end = selection_end.line.line_no;
3402
3403                                         undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
3404
3405                                         // Delete first line
3406                                         DeleteChars(selection_start.tag, selection_start.pos, selection_start.line.text.Length - selection_start.pos);
3407
3408                                         // Delete last line
3409                                         DeleteChars(selection_end.line.tags, 0, selection_end.pos);
3410
3411                                         start++;
3412                                         if (start < end) {
3413                                                 for (i = end - 1; i >= start; i--) {
3414                                                         Delete(i);
3415                                                 }
3416                                         }
3417
3418                                         // BIG FAT WARNING - selection_end.line might be stale due 
3419                                         // to the above Delete() call. DONT USE IT before hitting the end of this method!
3420
3421                                         // Join start and end
3422                                         Combine(selection_start.line.line_no, start);
3423                                 }
3424                         }
3425
3426
3427                         Insert(selection_start.line, selection_start.pos, false, s);
3428                         undo.RecordInsertString (selection_start.line, selection_start.pos, s);
3429                         ResumeRecalc (false);
3430
3431                         if (!select_new) {
3432                                 CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line,
3433                                                 out selection_start.tag, out selection_start.pos);
3434
3435                                 selection_end.line = selection_start.line;
3436                                 selection_end.pos = selection_start.pos;
3437                                 selection_end.tag = selection_start.tag;
3438                                 selection_anchor.line = selection_start.line;
3439                                 selection_anchor.pos = selection_start.pos;
3440                                 selection_anchor.tag = selection_start.tag;
3441
3442                                 SetSelectionVisible (false);
3443                         } else {
3444                                 CharIndexToLineTag(selection_start_pos, out selection_start.line,
3445                                                 out selection_start.tag, out selection_start.pos);
3446
3447                                 CharIndexToLineTag(selection_start_pos + s.Length, out selection_end.line,
3448                                                 out selection_end.tag, out selection_end.pos);
3449
3450                                 selection_anchor.line = selection_start.line;
3451                                 selection_anchor.pos = selection_start.pos;
3452                                 selection_anchor.tag = selection_start.tag;
3453
3454                                 SetSelectionVisible (true);
3455                         }
3456
3457                         PositionCaret (selection_start.line, selection_start.pos);
3458                         UpdateView (selection_start.line, selection_pos_on_line);
3459                 }
3460
3461                 internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
3462                         Line    line;
3463                         LineTag tag;
3464                         int     i;
3465                         int     chars;
3466                         int     start;
3467
3468                         chars = 0;
3469
3470                         for (i = 1; i <= lines; i++) {
3471                                 line = GetLine(i);
3472
3473                                 start = chars;
3474                                 chars += line.text.Length + (line.soft_break ? 0 : crlf_size);
3475
3476                                 if (index <= chars) {
3477                                         // we found the line
3478                                         tag = line.tags;
3479
3480                                         while (tag != null) {
3481                                                 if (index < (start + tag.start + tag.length)) {
3482                                                         line_out = line;
3483                                                         tag_out = LineTag.GetFinalTag (tag);
3484                                                         pos = index - start;
3485                                                         return;
3486                                                 }
3487                                                 if (tag.next == null) {
3488                                                         Line    next_line;
3489
3490                                                         next_line = GetLine(line.line_no + 1);
3491
3492                                                         if (next_line != null) {
3493                                                                 line_out = next_line;
3494                                                                 tag_out = LineTag.GetFinalTag (next_line.tags);
3495                                                                 pos = 0;
3496                                                                 return;
3497                                                         } else {
3498                                                                 line_out = line;
3499                                                                 tag_out = LineTag.GetFinalTag (tag);
3500                                                                 pos = line_out.text.Length;
3501                                                                 return;
3502                                                         }
3503                                                 }
3504                                                 tag = tag.next;
3505                                         }
3506                                 }
3507                         }
3508
3509                         line_out = GetLine(lines);
3510                         tag = line_out.tags;
3511                         while (tag.next != null) {
3512                                 tag = tag.next;
3513                         }
3514                         tag_out = tag;
3515                         pos = line_out.text.Length;
3516                 }
3517
3518                 internal int LineTagToCharIndex(Line line, int pos) {
3519                         int     i;
3520                         int     length;
3521
3522                         // Count first and last line
3523                         length = 0;
3524
3525                         // Count the lines in the middle
3526
3527                         for (i = 1; i < line.line_no; i++) {
3528                                 length += GetLine(i).text.Length + (line.soft_break ? 0 : crlf_size);
3529                         }
3530
3531                         length += pos;
3532
3533                         return length;
3534                 }
3535
3536                 internal int SelectionLength() {
3537                         if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
3538                                 return 0;
3539                         }
3540
3541                         if (selection_start.line == selection_end.line) {
3542                                 return selection_end.pos - selection_start.pos;
3543                         } else {
3544                                 int     i;
3545                                 int     start;
3546                                 int     end;
3547                                 int     length;
3548
3549                                 // Count first and last line
3550                                 length = selection_start.line.text.Length - selection_start.pos + selection_end.pos + crlf_size;
3551
3552                                 // Count the lines in the middle
3553                                 start = selection_start.line.line_no + 1;
3554                                 end = selection_end.line.line_no;
3555
3556                                 if (start < end) {
3557                                         for (i = start; i < end; i++) {
3558                                                 Line line = GetLine (i);
3559                                                 length += line.text.Length + (line.soft_break ? 0 : crlf_size);
3560                                         }
3561                                 }
3562
3563                                 return length;
3564                         }
3565
3566                         
3567                 }
3568
3569
3570                 /// <summary>Give it a Line number and it returns the Line object at with that line number</summary>
3571                 internal Line GetLine(int LineNo) {
3572                         Line    line = document;
3573
3574                         while (line != sentinel) {
3575                                 if (LineNo == line.line_no) {
3576                                         return line;
3577                                 } else if (LineNo < line.line_no) {
3578                                         line = line.left;
3579                                 } else {
3580                                         line = line.right;
3581                                 }
3582                         }
3583
3584                         return null;
3585                 }
3586
3587                 /// <summary>Retrieve the previous tag; walks line boundaries</summary>
3588                 internal LineTag PreviousTag(LineTag tag) {
3589                         Line l; 
3590
3591                         if (tag.previous != null) {
3592                                 return tag.previous;
3593                         }
3594
3595                         // Next line 
3596                         if (tag.line.line_no == 1) {
3597                                 return null;
3598                         }
3599
3600                         l = GetLine(tag.line.line_no - 1);
3601                         if (l != null) {
3602                                 LineTag t;
3603
3604                                 t = l.tags;
3605                                 while (t.next != null) {
3606                                         t = t.next;
3607                                 }
3608                                 return t;
3609                         }
3610
3611                         return null;
3612                 }
3613
3614                 /// <summary>Retrieve the next tag; walks line boundaries</summary>
3615                 internal LineTag NextTag(LineTag tag) {
3616                         Line l;
3617
3618                         if (tag.next != null) {
3619                                 return tag.next;
3620                         }
3621
3622                         // Next line
3623                         l = GetLine(tag.line.line_no + 1);
3624                         if (l != null) {
3625                                 return l.tags;
3626                         }
3627
3628                         return null;
3629                 }
3630
3631                 internal Line ParagraphStart(Line line) {
3632                         while (line.soft_break) {
3633                                 line = GetLine(line.line_no - 1);
3634                         }
3635                         return line;
3636                 }       
3637
3638                 internal Line ParagraphEnd(Line line) {
3639                         Line    l;
3640    
3641                         while (line.soft_break) {
3642                                 l = GetLine(line.line_no + 1);
3643                                 if ((l == null) || (!l.soft_break)) {
3644                                         break;
3645                                 }
3646                                 line = l;
3647                         }
3648                         return line;
3649                 }
3650
3651                 /// <summary>Give it a pixel offset coordinate and it returns the Line covering that are (offset
3652                 /// is either X or Y depending on if we are multiline
3653                 /// </summary>
3654                 internal Line GetLineByPixel (int offset, bool exact)
3655                 {
3656                         Line    line = document;
3657                         Line    last = null;
3658
3659                         if (multiline) {
3660                                 while (line != sentinel) {
3661                                         last = line;
3662                                         if ((offset >= line.Y) && (offset < (line.Y+line.height))) {
3663                                                 return line;
3664                                         } else if (offset < line.Y) {
3665                                                 line = line.left;
3666                                         } else {
3667                                                 line = line.right;
3668                                         }
3669                                 }
3670                         } else {
3671                                 while (line != sentinel) {
3672                                         last = line;
3673                                         if ((offset >= line.X) && (offset < (line.X + line.Width)))
3674                                                 return line;
3675                                         else if (offset < line.X)
3676                                                 line = line.left;
3677                                         else
3678                                                 line = line.right;
3679                                 }
3680                         }
3681
3682                         if (exact) {
3683                                 return null;
3684                         }
3685                         return last;
3686                 }
3687
3688                 // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
3689                 internal LineTag FindTag(int x, int y, out int index, bool exact) {
3690                         Line    line;
3691                         LineTag tag;
3692
3693                         line = GetLineByPixel(y, exact);
3694                         if (line == null) {
3695                                 index = 0;
3696                                 return null;
3697                         }
3698                         tag = line.tags;
3699
3700                         // Alignment adjustment
3701                         x += line.X;
3702
3703                         while (true) {
3704                                 if (x >= tag.X && x < (tag.X+tag.width)) {
3705                                         int     end;
3706
3707                                         end = tag.start + tag.length - 1;
3708
3709                                         for (int pos = tag.start; pos < end; pos++) {
3710                                                 if (x < line.widths[pos]) {
3711                                                         index = pos;
3712                                                         return LineTag.GetFinalTag (tag);
3713                                                 }
3714                                         }
3715                                         index=end;
3716                                         return LineTag.GetFinalTag (tag);
3717                                 }
3718                                 if (tag.next != null) {
3719                                         tag = tag.next;
3720                                 } else {
3721                                         if (exact) {
3722                                                 index = 0;
3723                                                 return null;
3724                                         }
3725
3726                                         index = line.text.Length;
3727                                         return LineTag.GetFinalTag (tag);
3728                                 }
3729                         }
3730                 }
3731
3732                 // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
3733                 internal LineTag FindCursor(int x, int y, out int index) {
3734                         Line    line;
3735                         LineTag tag;
3736
3737                         line = GetLineByPixel(multiline ? y : x, false);
3738                         tag = line.tags;
3739
3740                         while (true) {
3741                                 if (x >= tag.X && x < (tag.X+tag.width)) {
3742                                         int     end;
3743
3744                                         end = tag.end;
3745
3746                                         for (int pos = tag.start - 1; pos < end; pos++) {
3747                                                 // When clicking on a character, we position the cursor to whatever edge
3748                                                 // of the character the click was closer
3749                                                 if (x < (line.X + line.widths[pos] + ((line.widths[pos+1]-line.widths[pos])/2))) {
3750                                                         index = pos;
3751                                                         return LineTag.GetFinalTag (tag);
3752                                                 }
3753                                         }
3754                                         index=end;
3755                                         return LineTag.GetFinalTag (tag);
3756                                 }
3757                                 if (tag.next != null) {
3758                                         tag = tag.next;
3759                                 } else {
3760                                         index = line.text.Length;
3761                                         return LineTag.GetFinalTag (tag);
3762                                 }
3763                         }
3764                 }
3765
3766                 /// <summary>Format area of document in specified font and color</summary>
3767                 /// <param name="start_pos">1-based start position on start_line</param>
3768                 /// <param name="end_pos">1-based end position on end_line </param>
3769                 internal void FormatText (Line start_line, int start_pos, Line end_line, int end_pos, Font font,
3770                                 SolidBrush color, SolidBrush back_color, FormatSpecified specified)
3771                 {
3772                         Line    l;
3773
3774                         // First, format the first line
3775                         if (start_line != end_line) {
3776                                 // First line
3777                                 LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, font, color, back_color, specified);
3778
3779                                 // Format last line
3780                                 LineTag.FormatText(end_line, 1, end_pos, font, color, back_color, specified);
3781
3782                                 // Now all the lines inbetween
3783                                 for (int i = start_line.line_no + 1; i < end_line.line_no; i++) {
3784                                         l = GetLine(i);
3785                                         LineTag.FormatText(l, 1, l.text.Length, font, color, back_color, specified);
3786                                 }
3787                         } else {
3788                                 // Special case, single line
3789                                 LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color, back_color, specified);
3790                         }
3791                 }
3792
3793                 /// <summary>Re-format areas of the document in specified font and color</summary>
3794                 /// <param name="start_pos">1-based start position on start_line</param>
3795                 /// <param name="end_pos">1-based end position on end_line </param>
3796                 /// <param name="font">Font specifying attributes</param>
3797                 /// <param name="color">Color (or NULL) to apply</param>
3798                 /// <param name="apply">Attributes from font and color to apply</param>
3799                 internal void FormatText(Line start_line, int start_pos, Line end_line, int end_pos, FontDefinition attributes) {
3800                         Line    l;
3801
3802                         // First, format the first line
3803                         if (start_line != end_line) {
3804                                 // First line
3805                                 LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, attributes);
3806
3807                                 // Format last line
3808                                 LineTag.FormatText(end_line, 1, end_pos - 1, attributes);
3809
3810                                 // Now all the lines inbetween
3811                                 for (int i = start_line.line_no + 1; i < end_line.line_no; i++) {
3812                                         l = GetLine(i);
3813                                         LineTag.FormatText(l, 1, l.text.Length, attributes);
3814                                 }
3815                         } else {
3816                                 // Special case, single line
3817                                 LineTag.FormatText(start_line, start_pos, end_pos - start_pos, attributes);
3818                         }
3819                 }
3820
3821                 internal void RecalculateAlignments ()
3822                 {
3823                         Line    line;
3824                         int     line_no;
3825
3826                         line_no = 1;
3827
3828                         while (line_no <= lines) {
3829                                 line = GetLine(line_no);
3830
3831                                 if (line != null) {
3832                                         switch (line.alignment) {
3833                                         case HorizontalAlignment.Left:
3834                                                 line.align_shift = 0;
3835                                                 break;
3836                                         case HorizontalAlignment.Center:
3837                                                 line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
3838                                                 break;
3839                                         case HorizontalAlignment.Right:
3840                                                 line.align_shift = viewport_width - (int)line.widths[line.text.Length];
3841                                                 break;
3842                                         }
3843                                 }
3844
3845                                 line_no++;
3846                         }
3847                         return;
3848                 }
3849
3850                 /// <summary>Calculate formatting for the whole document</summary>
3851                 internal bool RecalculateDocument(Graphics g) {
3852                         return RecalculateDocument(g, 1, this.lines, false);
3853                 }
3854
3855                 /// <summary>Calculate formatting starting at a certain line</summary>
3856                 internal bool RecalculateDocument(Graphics g, int start) {
3857                         return RecalculateDocument(g, start, this.lines, false);
3858                 }
3859
3860                 /// <summary>Calculate formatting within two given line numbers</summary>
3861                 internal bool RecalculateDocument(Graphics g, int start, int end) {
3862                         return RecalculateDocument(g, start, end, false);
3863                 }
3864
3865                 /// <summary>With optimize on, returns true if line heights changed</summary>
3866                 internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
3867                         Line    line;
3868                         int     line_no;
3869                         int     offset;
3870                         int     new_width;
3871                         bool    changed;
3872                         int     shift;
3873
3874                         if (recalc_suspended > 0) {
3875                                 recalc_pending = true;
3876                                 recalc_start = Math.Min (recalc_start, start);
3877                                 recalc_end = Math.Max (recalc_end, end);
3878                                 recalc_optimize = optimize;
3879                                 return false;
3880                         }
3881
3882                         // Fixup the positions, they can go kinda nuts
3883                         start = Math.Max (start, 1);
3884                         end = Math.Min (end, lines);
3885
3886                         offset = GetLine(start).offset;
3887                         line_no = start;
3888                         new_width = 0;
3889                         shift = this.lines;
3890                         if (!optimize) {
3891                                 changed = true;         // We always return true if we run non-optimized
3892                         } else {
3893                                 changed = false;
3894                         }
3895
3896                         while (line_no <= (end + this.lines - shift)) {
3897                                 line = GetLine(line_no++);
3898                                 line.offset = offset;
3899
3900                                 if (!calc_pass) {
3901                                         if (!optimize) {
3902                                                 line.RecalculateLine(g, this);
3903                                         } else {
3904                                                 if (line.recalc && line.RecalculateLine(g, this)) {
3905                                                         changed = true;
3906                                                         // If the height changed, all subsequent lines change
3907                                                         end = this.lines;
3908                                                         shift = this.lines;
3909                                                 }
3910                                         }
3911                                 } else {
3912                                         if (!optimize) {
3913                                                 line.RecalculatePasswordLine(g, this);
3914                                         } else {
3915                                                 if (line.recalc && line.RecalculatePasswordLine(g, this)) {
3916                                                         changed = true;
3917                                                         // If the height changed, all subsequent lines change
3918                                                         end = this.lines;
3919                                                         shift = this.lines;
3920                                                 }
3921                                         }
3922                                 }
3923
3924                                 if (line.widths[line.text.Length] > new_width) {
3925                                         new_width = (int)line.widths[line.text.Length];
3926                                 }
3927
3928                                 // Calculate alignment
3929                                 if (line.alignment != HorizontalAlignment.Left) {
3930                                         if (line.alignment == HorizontalAlignment.Center) {
3931                                                 line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
3932                                         } else {
3933                                                 line.align_shift = viewport_width - (int)line.widths[line.text.Length] - 1;
3934                                         }
3935                                 }
3936
3937                                 if (multiline)
3938                                         offset += line.height;
3939                                 else
3940                                         offset += (int) line.widths [line.text.Length] + 2;
3941
3942                                 if (line_no > lines) {
3943                                         break;
3944                                 }
3945                         }
3946
3947                         if (document_x != new_width) {
3948                                 document_x = new_width;
3949                                 if (WidthChanged != null) {
3950                                         WidthChanged(this, null);
3951                                 }
3952                         }
3953
3954                         RecalculateAlignments();
3955
3956                         line = GetLine(lines);
3957
3958                         if (document_y != line.Y + line.height) {
3959                                 document_y = line.Y + line.height;
3960                                 if (HeightChanged != null) {
3961                                         HeightChanged(this, null);
3962                                 }
3963                         }
3964                         UpdateCaret();
3965                         return changed;
3966                 }
3967
3968                 internal int Size() {
3969                         return lines;
3970                 }
3971
3972                 private void owner_HandleCreated(object sender, EventArgs e) {
3973                         RecalculateDocument(owner.CreateGraphicsInternal());
3974                         AlignCaret();
3975                 }
3976
3977                 private void owner_VisibleChanged(object sender, EventArgs e) {
3978                         if (owner.Visible) {
3979                                 RecalculateDocument(owner.CreateGraphicsInternal());
3980                         }
3981                 }
3982
3983                 internal static bool IsWordSeparator(char ch) {
3984                         switch(ch) {
3985                                 case ' ':
3986                                 case '\t':
3987                                 case '(':
3988                                 case ')': {
3989                                         return true;
3990                                 }
3991
3992                                 default: {
3993                                         return false;
3994                                 }
3995                         }
3996                 }
3997                 internal int FindWordSeparator(Line line, int pos, bool forward) {
3998                         int len;
3999
4000                         len = line.text.Length;
4001
4002                         if (forward) {
4003                                 for (int i = pos + 1; i < len; i++) {
4004                                         if (IsWordSeparator(line.Text[i])) {
4005                                                 return i + 1;
4006                                         }
4007                                 }
4008                                 return len;
4009                         } else {
4010                                 for (int i = pos - 1; i > 0; i--) {
4011                                         if (IsWordSeparator(line.Text[i - 1])) {
4012                                                 return i;
4013                                         }
4014                                 }
4015                                 return 0;
4016                         }
4017                 }
4018
4019                 /* Search document for text */
4020                 internal bool FindChars(char[] chars, Marker start, Marker end, out Marker result) {
4021                         Line    line;
4022                         int     line_no;
4023                         int     pos;
4024                         int     line_len;
4025
4026                         // Search for occurence of any char in the chars array
4027                         result = new Marker();
4028
4029                         line = start.line;
4030                         line_no = start.line.line_no;
4031                         pos = start.pos;
4032                         while (line_no <= end.line.line_no) {
4033                                 line_len = line.text.Length;
4034                                 while (pos < line_len) {
4035                                         for (int i = 0; i < chars.Length; i++) {
4036                                                 if (line.text[pos] == chars[i]) {
4037                                                         // Special case
4038                                                         if ((line.line_no == end.line.line_no) && (pos >= end.pos)) {
4039                                                                 return false;
4040                                                         }
4041
4042                                                         result.line = line;
4043                                                         result.pos = pos;
4044                                                         return true;
4045                                                 }
4046                                         }
4047                                         pos++;
4048                                 }
4049
4050                                 pos = 0;
4051                                 line_no++;
4052                                 line = GetLine(line_no);
4053                         }
4054
4055                         return false;
4056                 }
4057
4058                 // This version does not build one big string for searching, instead it handles 
4059                 // line-boundaries, which is faster and less memory intensive
4060                 // FIXME - Depending on culture stuff we might have to create a big string and use culturespecific 
4061                 // search stuff and change it to accept and return positions instead of Markers (which would match 
4062                 // RichTextBox behaviour better but would be inconsistent with the rest of TextControl)
4063                 internal bool Find(string search, Marker start, Marker end, out Marker result, RichTextBoxFinds options) {
4064                         Marker  last;
4065                         string  search_string;
4066                         Line    line;
4067                         int     line_no;
4068                         int     pos;
4069                         int     line_len;
4070                         int     current;
4071                         bool    word;
4072                         bool    word_option;
4073                         bool    ignore_case;
4074                         bool    reverse;
4075                         char    c;
4076
4077                         result = new Marker();
4078                         word_option = ((options & RichTextBoxFinds.WholeWord) != 0);
4079                         ignore_case = ((options & RichTextBoxFinds.MatchCase) == 0);
4080                         reverse = ((options & RichTextBoxFinds.Reverse) != 0);
4081
4082                         line = start.line;
4083                         line_no = start.line.line_no;
4084                         pos = start.pos;
4085                         current = 0;
4086
4087                         // Prep our search string, lowercasing it if we do case-independent matching
4088                         if (ignore_case) {
4089                                 StringBuilder   sb;
4090                                 sb = new StringBuilder(search);
4091                                 for (int i = 0; i < sb.Length; i++) {
4092                                         sb[i] = Char.ToLower(sb[i]);
4093                                 }
4094                                 search_string = sb.ToString();
4095                         } else {
4096                                 search_string = search;
4097                         }
4098
4099                         // We need to check if the character before our start position is a wordbreak
4100                         if (word_option) {
4101                                 if (line_no == 1) {
4102                                         if ((pos == 0) || (IsWordSeparator(line.text[pos - 1]))) {
4103                                                 word = true;
4104                                         } else {
4105                                                 word = false;
4106                                         }
4107                                 } else {
4108                                         if (pos > 0) {
4109                                                 if (IsWordSeparator(line.text[pos - 1])) {
4110                                                         word = true;
4111                                                 } else {
4112                                                         word = false;
4113                                                 }
4114                                         } else {
4115                                                 // Need to check the end of the previous line
4116                                                 Line    prev_line;
4117
4118                                                 prev_line = GetLine(line_no - 1);
4119                                                 if (prev_line.soft_break) {
4120                                                         if (IsWordSeparator(prev_line.text[prev_line.text.Length - 1])) {
4121                                                                 word = true;
4122                                                         } else {
4123                                                                 word = false;
4124                                                         }
4125                                                 } else {
4126                                                         word = true;
4127                                                 }
4128                                         }
4129                                 }
4130                         } else {
4131                                 word = false;
4132                         }
4133
4134                         // To avoid duplication of this loop with reverse logic, we search
4135                         // through the document, remembering the last match and when returning
4136                         // report that last remembered match
4137
4138                         last = new Marker();
4139                         last.height = -1;       // Abused - we use it to track change
4140
4141                         while (line_no <= end.line.line_no) {
4142                                 if (line_no != end.line.line_no) {
4143                                         line_len = line.text.Length;
4144                                 } else {
4145                                         line_len = end.pos;
4146                                 }
4147
4148                                 while (pos < line_len) {
4149                                         if (word_option && (current == search_string.Length)) {
4150                                                 if (IsWordSeparator(line.text[pos])) {
4151                                                         if (!reverse) {
4152                                                                 goto FindFound;
4153                                                         } else {
4154                                                                 last = result;
4155                                                                 current = 0;
4156                                                         }
4157                                                 } else {
4158                                                         current = 0;
4159                                                 }
4160                                         }
4161
4162                                         if (ignore_case) {
4163                                                 c = Char.ToLower(line.text[pos]);
4164                                         } else {
4165                                                 c = line.text[pos];
4166                                         }
4167
4168                                         if (c == search_string[current]) {
4169                                                 if (current == 0) {
4170                                                         result.line = line;
4171                                                         result.pos = pos;
4172                                                 }
4173                                                 if (!word_option || (word_option && (word || (current > 0)))) {
4174                                                         current++;
4175                                                 }
4176
4177                                                 if (!word_option && (current == search_string.Length)) {
4178                                                         if (!reverse) {
4179                                                                 goto FindFound;
4180                                                         } else {
4181                                                                 last = result;
4182                                                                 current = 0;
4183                                                         }
4184                                                 }
4185                                         } else {
4186                                                 current = 0;
4187                                         }
4188                                         pos++;
4189
4190                                         if (!word_option) {
4191                                                 continue;
4192                                         }
4193
4194                                         if (IsWordSeparator(c)) {
4195                                                 word = true;
4196                                         } else {
4197                                                 word = false;
4198                                         }
4199                                 }
4200
4201                                 if (word_option) {
4202                                         // Mark that we just saw a word boundary
4203                                         if (!line.soft_break) {
4204                                                 word = true;
4205                                         }
4206
4207                                         if (current == search_string.Length) {
4208                                                 if (word) {
4209                                                         if (!reverse) {
4210                                                                 goto FindFound;
4211                                                         } else {
4212                                                                 last = result;
4213                                                                 current = 0;
4214                                                         }
4215                                                 } else {
4216                                                         current = 0;
4217                                                 }
4218                                         }
4219                                 }
4220
4221                                 pos = 0;
4222                                 line_no++;
4223                                 line = GetLine(line_no);
4224                         }
4225
4226                         if (reverse) {
4227                                 if (last.height != -1) {
4228                                         result = last;
4229                                         return true;
4230                                 }
4231                         }
4232
4233                         return false;
4234
4235                         FindFound:
4236                         if (!reverse) {
4237 //                              if ((line.line_no == end.line.line_no) && (pos >= end.pos)) {
4238 //                                      return false;
4239 //                              }
4240                                 return true;
4241                         }
4242
4243                         result = last;
4244                         return true;
4245
4246                 }
4247
4248                 /* Marker stuff */
4249                 internal void GetMarker(out Marker mark, bool start) {
4250                         mark = new Marker();
4251
4252                         if (start) {
4253                                 mark.line = GetLine(1);
4254                                 mark.tag = mark.line.tags;
4255                                 mark.pos = 0;
4256                         } else {
4257                                 mark.line = GetLine(lines);
4258                                 mark.tag = mark.line.tags;
4259                                 while (mark.tag.next != null) {
4260                                         mark.tag = mark.tag.next;
4261                                 }
4262                                 mark.pos = mark.line.text.Length;
4263                         }
4264                 }
4265                 #endregion      // Internal Methods
4266
4267                 #region Events
4268                 internal event EventHandler CaretMoved;
4269                 internal event EventHandler WidthChanged;
4270                 internal event EventHandler HeightChanged;
4271                 internal event EventHandler LengthChanged;
4272                 #endregion      // Events
4273
4274                 #region Administrative
4275                 public IEnumerator GetEnumerator() {
4276                         // FIXME
4277                         return null;
4278                 }
4279
4280                 public override bool Equals(object obj) {
4281                         if (obj == null) {
4282                                 return false;
4283                         }
4284
4285                         if (!(obj is Document)) {
4286                                 return false;
4287                         }
4288
4289                         if (obj == this) {
4290                                 return true;
4291                         }
4292
4293                         if (ToString().Equals(((Document)obj).ToString())) {
4294                                 return true;
4295                         }
4296
4297                         return false;
4298                 }
4299
4300                 public override int GetHashCode() {
4301                         return document_id;
4302                 }
4303
4304                 public override string ToString() {
4305                         return "document " + this.document_id;
4306                 }
4307                 #endregion      // Administrative
4308         }
4309
4310         internal class ImageTag : LineTag {
4311
4312                 internal Image image;
4313
4314                 internal ImageTag (Line line, int start, Image image) : base (line, start)
4315                 {
4316                         this.image = image;
4317                 }
4318
4319                 public override bool IsTextTag {
4320                         get { return false; }
4321                 }
4322
4323                 internal override SizeF SizeOfPosition (Graphics dc, int pos)
4324                 {
4325                         return image.Size;
4326                 }
4327
4328                 internal override int MaxHeight ()
4329                 {
4330                         return image.Height;
4331                 }
4332
4333                 internal override void Draw (Graphics dc, Brush brush, float x, float y, int start, int end)
4334                 {
4335                         dc.DrawImage (image, x, y);
4336                 }
4337
4338                 internal override void Draw (Graphics dc, Brush brush, float x, float y, int start, int end, string text)
4339                 {
4340                         dc.DrawImage (image, x, y);
4341                 }
4342
4343                 public override string Text ()
4344                 {
4345                         return "I";
4346                 }
4347         }
4348
4349         internal class LineTag {
4350                 #region Local Variables;
4351                 // Payload; formatting
4352                 internal Font           font;           // System.Drawing.Font object for this tag
4353                 internal SolidBrush     color;          // The font color for this tag
4354
4355                 // In 2.0 tags can have background colours.  I'm not going to #ifdef
4356                 // at this level though since I want to reduce code paths
4357                 internal SolidBrush back_color;  
4358
4359                 // Payload; text
4360                 internal int            start;          // start, in chars; index into Line.text
4361                 internal bool           r_to_l;         // Which way is the font
4362
4363                 // Drawing support
4364                 internal int            height;         // Height in pixels of the text this tag describes
4365
4366                 internal int            ascent;         // Ascent of the font for this tag
4367                 internal int            shift;          // Shift down for this tag, to stay on baseline
4368
4369                 // Administrative
4370                 internal Line           line;           // The line we're on
4371                 internal LineTag        next;           // Next tag on the same line
4372                 internal LineTag        previous;       // Previous tag on the same line
4373                 #endregion;
4374
4375                 #region Constructors
4376                 internal LineTag(Line line, int start) {
4377                         this.line = line;
4378                         this.start = start;
4379                 }
4380                 #endregion      // Constructors
4381
4382                 #region Internal Methods
4383
4384                 public float X {
4385                         get {
4386                                 if (start == 0)
4387                                         return line.X;
4388                                 return line.X + line.widths [start - 1];
4389                         }
4390                 }
4391
4392                 public int end {
4393                         get { return start + length; }
4394                 }
4395
4396                 public float width {
4397                         get {
4398                                 if (length == 0)
4399                                         return 0;
4400                                 return line.widths [start + length - 1] - (start != 0 ? line.widths [start - 1] : 0);
4401                         }
4402                 }
4403
4404                 public int length {
4405                         get {
4406                                 int res = 0;
4407                                 if (next != null)
4408                                         res = next.start - start;
4409                                 else
4410                                         res = line.text.Length - (start - 1);
4411
4412                                 return res > 0 ? res : 0;
4413                         }
4414                 }
4415
4416                 public virtual bool IsTextTag {
4417                         get { return true; }
4418                 }
4419
4420                 internal virtual SizeF SizeOfPosition (Graphics dc, int pos)
4421                 {
4422                         return dc.MeasureString (line.text.ToString (pos, 1), font, 10000, Document.string_format);
4423                 }
4424
4425                 internal virtual int MaxHeight ()
4426                 {
4427                         return font.Height;
4428                 }
4429
4430                 internal virtual void Draw (Graphics dc, Brush brush, float x, float y, int start, int end)
4431                 {
4432                         dc.DrawString (line.text.ToString (start, end), font, brush, x, y, StringFormat.GenericTypographic);
4433                 }
4434
4435                 internal virtual void Draw (Graphics dc, Brush brush, float x, float y, int start, int end, string text)
4436                 {
4437                         dc.DrawString (text.Substring (start, end), font, brush, x, y, StringFormat.GenericTypographic);
4438                 }
4439
4440                 ///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at &gt;pos&lt; or null if end-of-line</summary>
4441                 internal LineTag Break(int pos) {
4442
4443                         LineTag new_tag;
4444
4445                         // Sanity
4446                         if (pos == this.start) {
4447                                 return this;
4448                         } else if (pos >= (start + length)) {
4449                                 return null;
4450                         }
4451
4452                         new_tag = new LineTag(line, pos);
4453                         new_tag.CopyFormattingFrom (this);
4454
4455                         new_tag.next = this.next;
4456                         this.next = new_tag;
4457                         new_tag.previous = this;
4458
4459                         if (new_tag.next != null) {
4460                                 new_tag.next.previous = new_tag;
4461                         }
4462
4463                         return new_tag;
4464                 }
4465
4466                 public virtual string Text ()
4467                 {
4468                         return line.text.ToString (start - 1, length);
4469                 }
4470
4471                 public void CopyFormattingFrom (LineTag other)
4472                 {
4473                         height = other.height;
4474                         font = other.font;
4475                         color = other.color;
4476                         back_color = other.back_color;
4477                 }
4478
4479                 ///<summary>Create new font and brush from existing font and given new attributes. Returns true if fontheight changes</summary>
4480                 internal static bool GenerateTextFormat(Font font_from, SolidBrush color_from, FontDefinition attributes, out Font new_font, out SolidBrush new_color) {
4481                         float           size;
4482                         string          face;
4483                         FontStyle       style;
4484                         GraphicsUnit    unit;
4485
4486                         if (attributes.font_obj == null) {
4487                                 size = font_from.SizeInPoints;
4488                                 unit = font_from.Unit;
4489                                 face = font_from.Name;
4490                                 style = font_from.Style;
4491
4492                                 if (attributes.face != null) {
4493                                         face = attributes.face;
4494                                 }
4495                                 
4496                                 if (attributes.size != 0) {
4497                                         size = attributes.size;
4498                                 }
4499
4500                                 style |= attributes.add_style;
4501                                 style &= ~attributes.remove_style;
4502
4503                                 // Create new font
4504                                 new_font = new Font(face, size, style, unit);
4505                         } else {
4506                                 new_font = attributes.font_obj;
4507                         }
4508
4509                         // Create 'new' color brush
4510                         if (attributes.color != Color.Empty) {
4511                                 new_color = new SolidBrush(attributes.color);
4512                         } else {
4513                                 new_color = color_from;
4514                         }
4515
4516                         if (new_font.Height == font_from.Height) {
4517                                 return false;
4518                         }
4519                         return true;
4520                 }
4521
4522                 /// <summary>Applies 'font' and 'brush' to characters starting at 'start' for 'length' chars; 
4523                 /// Removes any previous tags overlapping the same area; 
4524                 /// returns true if lineheight has changed</summary>
4525                 /// <param name="start">1-based character position on line</param>
4526                 internal static bool FormatText(Line line, int start, int length, Font font, SolidBrush color, SolidBrush back_color, FormatSpecified specified)
4527                 {
4528                         LineTag tag;
4529                         LineTag start_tag;
4530                         LineTag end_tag;
4531                         int     end;
4532                         bool    retval = false;         // Assume line-height doesn't change
4533
4534                         // Too simple?
4535                         if (((FormatSpecified.Font & specified) == FormatSpecified.Font) && font.Height != line.height) {
4536                                 retval = true;
4537                         }
4538                         line.recalc = true;             // This forces recalculation of the line in RecalculateDocument
4539
4540                         // A little sanity, not sure if it's needed, might be able to remove for speed
4541                         if (length > line.text.Length) {
4542                                 length = line.text.Length;
4543                         }
4544
4545                         tag = line.tags;
4546                         end = start + length;
4547
4548                         // Common special case
4549                         if ((start == 1) && (length == tag.length)) {
4550                                 tag.ascent = 0;
4551                                 SetFormat (tag, font, color, back_color, specified);
4552                                 return retval;
4553                         }
4554
4555                         start_tag = FindTag (line, start);
4556
4557                         tag = start_tag.Break (start);
4558
4559                         while (tag != null && tag.end <= end) {
4560                                 SetFormat (tag, font, color, back_color, specified);
4561                                 tag = tag.next;
4562                         }
4563
4564                         if (end != line.text.Length) {
4565                                 /// Now do the last tag
4566                                 end_tag = FindTag (line, end);
4567
4568                                 if (end_tag != null) {
4569                                         end_tag.Break (end);
4570                                         SetFormat (end_tag, font, color, back_color, specified);
4571                                 }
4572                         }
4573
4574                         return retval;
4575                 }
4576
4577                 private static void SetFormat (LineTag tag, Font font, SolidBrush color, SolidBrush back_color, FormatSpecified specified)
4578                 {
4579                         if ((FormatSpecified.Font & specified) == FormatSpecified.Font)
4580                                 tag.font = font;
4581                         if ((FormatSpecified.Color & specified) == FormatSpecified.Color)
4582                                 tag.color = color;
4583                         if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor) {
4584                                 tag.back_color = back_color;
4585                         }
4586                         // Console.WriteLine ("setting format:   {0}  {1}   new color {2}", color.Color, specified, tag.color.Color);
4587                 }
4588
4589                 /// <summary>Applies font attributes specified to characters starting at 'start' for 'length' chars; 
4590                 /// Breaks tags at start and end point, keeping middle tags with altered attributes.
4591                 /// Returns true if lineheight has changed</summary>
4592                 /// <param name="start">1-based character position on line</param>
4593                 internal static bool FormatText(Line line, int start, int length, FontDefinition attributes) {
4594                         LineTag tag;
4595                         LineTag start_tag;
4596                         LineTag end_tag;
4597                         bool    retval = false;         // Assume line-height doesn't change
4598
4599                         line.recalc = true;             // This forces recalculation of the line in RecalculateDocument
4600
4601                         // A little sanity, not sure if it's needed, might be able to remove for speed
4602                         if (length > line.text.Length) {
4603                                 length = line.text.Length;
4604                         }
4605
4606                         tag = line.tags;
4607
4608                         // Common special case
4609                         if ((start == 1) && (length == tag.length)) {
4610                                 tag.ascent = 0;
4611                                 GenerateTextFormat(tag.font, tag.color, attributes, out tag.font, out tag.color);
4612                                 return retval;
4613                         }
4614
4615                         start_tag = FindTag(line, start);
4616                         
4617                         if (start_tag == null) {
4618                                 if (length == 0) {
4619                                         // We are 'starting' after all valid tags; create a new tag with the right attributes
4620                                         start_tag = FindTag(line, line.text.Length - 1);
4621                                         start_tag.next = new LineTag(line, line.text.Length + 1);
4622                                         start_tag.next.CopyFormattingFrom (start_tag);
4623                                         start_tag.next.previous = start_tag;
4624                                         start_tag = start_tag.next;
4625                                 } else {
4626                                         throw new Exception(String.Format("Could not find start_tag in document at line {0} position {1}", line.line_no, start));
4627                                 }
4628                         } else {
4629                                 start_tag = start_tag.Break(start);
4630                         }
4631
4632                         end_tag = FindTag(line, start + length);
4633                         if (end_tag != null) {
4634                                 end_tag = end_tag.Break(start + length);
4635                         }
4636
4637                         // start_tag or end_tag might be null; we're cool with that
4638                         // we now walk from start_tag to end_tag, applying new attributes
4639                         tag = start_tag;
4640                         while ((tag != null) && tag != end_tag) {
4641                                 if (LineTag.GenerateTextFormat(tag.font, tag.color, attributes, out tag.font, out tag.color)) {
4642                                         retval = true;
4643                                 }
4644                                 tag = tag.next;
4645                         }
4646                         return retval;
4647                 }
4648
4649
4650                 /// <summary>Finds the tag that describes the character at position 'pos' on 'line'</summary>
4651                 internal static LineTag FindTag(Line line, int pos) {
4652                         LineTag tag = line.tags;
4653
4654                         // Beginning of line is a bit special
4655                         if (pos == 0) {
4656                                 // Not sure if we should get the final tag here
4657                                 return tag;
4658                         }
4659
4660                         while (tag != null) {
4661                                 if ((tag.start <= pos) && (pos <= tag.end)) {
4662                                         return GetFinalTag (tag);
4663                                 }
4664
4665                                 tag = tag.next;
4666                         }
4667
4668                         return null;
4669                 }
4670
4671                 // There can be multiple tags at the same position, we want to make
4672                 // sure we are using the very last tag at the given position
4673                 internal static LineTag GetFinalTag (LineTag tag)
4674                 {
4675                         LineTag res = tag;
4676
4677                         while (res.length == 0 && res.next != null && res.next.length == 0)
4678                                 res = res.next;
4679
4680                         return res;
4681                 }
4682
4683                 /// <summary>Combines 'this' tag with 'other' tag</summary>
4684                 internal bool Combine(LineTag other) {
4685                         if (!this.Equals(other)) {
4686                                 return false;
4687                         }
4688
4689                         this.next = other.next;
4690                         if (this.next != null) {
4691                                 this.next.previous = this;
4692                         }
4693
4694                         return true;
4695                 }
4696
4697
4698                 /// <summary>Remove 'this' tag ; to be called when formatting is to be removed</summary>
4699                 internal bool Remove() {
4700                         if ((this.start == 1) && (this.next == null)) {
4701                                 // We cannot remove the only tag
4702                                 return false;
4703                         }
4704                         if (this.start != 1) {
4705                                 this.previous.next = this.next;
4706                                 this.next.previous = this.previous;
4707                         } else {
4708                                 this.next.start = 1;
4709                                 this.line.tags = this.next;
4710                                 this.next.previous = null;
4711                         }
4712                         return true;
4713                 }
4714
4715
4716                 /// <summary>Checks if 'this' tag describes the same formatting options as 'obj'</summary>
4717                 public override bool Equals(object obj) {
4718                         LineTag other;
4719
4720                         if (obj == null) {
4721                                 return false;
4722                         }
4723
4724                         if (!(obj is LineTag)) {
4725                                 return false;
4726                         }
4727
4728                         if (obj == this) {
4729                                 return true;
4730                         }
4731
4732                         other = (LineTag)obj;
4733
4734                         if (other.IsTextTag != IsTextTag)
4735                                 return false;
4736
4737                         if (this.font.Equals(other.font) && this.color.Equals(other.color)) {   // FIXME add checking for things like link or type later
4738                                 return true;
4739                         }
4740
4741                         return false;
4742                 }
4743
4744                 public override int GetHashCode() {
4745                         return base.GetHashCode ();
4746                 }
4747
4748                 public override string ToString() {
4749                         if (length > 0)
4750                                 return GetType () + " Tag starts at index " + this.start + " length " + this.length + " text: " + Text () + "Font " + this.font.ToString();
4751                         return "Zero Lengthed tag at index " + this.start;
4752                 }
4753
4754                 #endregion      // Internal Methods
4755         }
4756
4757         internal class UndoManager {
4758
4759                 internal enum ActionType {
4760
4761                         Typing,
4762
4763                         // This is basically just cut & paste
4764                         InsertString,
4765                         DeleteString,
4766
4767                         UserActionBegin,
4768                         UserActionEnd
4769                 }
4770
4771                 internal class Action {
4772                         internal ActionType     type;
4773                         internal int            line_no;
4774                         internal int            pos;
4775                         internal object         data;
4776                 }
4777
4778                 #region Local Variables
4779                 private Document        document;
4780                 private Stack           undo_actions;
4781                 private Stack           redo_actions;
4782
4783                 private int             caret_line;
4784                 private int             caret_pos;
4785
4786                 // When performing an action, we lock the queue, so that the action can't be undone
4787                 private bool locked;
4788                 #endregion      // Local Variables
4789
4790                 #region Constructors
4791                 internal UndoManager (Document document)
4792                 {
4793                         this.document = document;
4794                         undo_actions = new Stack (50);
4795                         redo_actions = new Stack (50);
4796                 }
4797                 #endregion      // Constructors
4798
4799                 #region Properties
4800                 internal bool CanUndo {
4801                         get { return undo_actions.Count > 0; }
4802                 }
4803
4804                 internal bool CanRedo {
4805                         get { return redo_actions.Count > 0; }
4806                 }
4807
4808                 internal string UndoActionName {
4809                         get {
4810                                 foreach (Action action in undo_actions) {
4811                                         if (action.type == ActionType.UserActionBegin)
4812                                                 return (string) action.data;
4813                                         if (action.type == ActionType.Typing)
4814                                                 return Locale.GetText ("Typing");
4815                                 }
4816                                 return String.Empty;
4817                         }
4818                 }
4819
4820                 internal string RedoActionName {
4821                         get {
4822                                 foreach (Action action in redo_actions) {
4823                                         if (action.type == ActionType.UserActionBegin)
4824                                                 return (string) action.data;
4825                                         if (action.type == ActionType.Typing)
4826                                                 return Locale.GetText ("Typing");
4827                                 }
4828                                 return String.Empty;
4829                         }
4830                 }
4831                 #endregion      // Properties
4832
4833                 #region Internal Methods
4834                 internal void Clear ()
4835                 {
4836                         undo_actions.Clear();
4837                         redo_actions.Clear();
4838                 }
4839
4840                 internal void Undo ()
4841                 {
4842                         Action action;
4843                         bool user_action_finished = false;
4844
4845                         if (undo_actions.Count == 0)
4846                                 return;
4847
4848                         // Nuke the redo queue
4849                         redo_actions.Clear ();
4850
4851                         locked = true;
4852                         do {
4853                                 Line start;
4854                                 action = (Action) undo_actions.Pop ();
4855
4856                                 // Put onto redo stack
4857                                 redo_actions.Push(action);
4858
4859                                 // Do the thing
4860                                 switch(action.type) {
4861
4862                                 case ActionType.UserActionBegin:
4863                                         user_action_finished = true;
4864                                         break;
4865
4866                                 case ActionType.UserActionEnd:
4867                                         // noop
4868                                         break;
4869
4870                                 case ActionType.InsertString:
4871                                         start = document.GetLine (action.line_no);
4872                                         document.SuspendUpdate ();
4873                                         document.DeleteMultiline (start, action.pos, ((string) action.data).Length + 1);
4874                                         document.PositionCaret (start, action.pos);
4875                                         document.SetSelectionToCaret (true);
4876                                         document.ResumeUpdate (true);
4877                                         break;
4878
4879                                 case ActionType.Typing:
4880                                         start = document.GetLine (action.line_no);
4881                                         document.SuspendUpdate ();
4882                                         document.DeleteMultiline (start, action.pos, ((StringBuilder) action.data).Length);
4883                                         document.PositionCaret (start, action.pos);
4884                                         document.SetSelectionToCaret (true);
4885                                         document.ResumeUpdate (true);
4886
4887                                         // This is an open ended operation, so only a single typing operation can be undone at once
4888                                         user_action_finished = true;
4889                                         break;
4890
4891                                 case ActionType.DeleteString:
4892                                         start = document.GetLine (action.line_no);
4893                                         document.SuspendUpdate ();
4894                                         Insert (start, action.pos, (Line) action.data, true);
4895                                         document.ResumeUpdate (true);
4896                                         break;
4897                                 }
4898                         } while (!user_action_finished && undo_actions.Count > 0);
4899
4900                         locked = false;
4901                 }
4902
4903                 internal void Redo ()
4904                 {
4905                         Action action;
4906                         bool user_action_finished = false;
4907
4908                         if (redo_actions.Count == 0)
4909                                 return;
4910
4911                         // You can't undo anything after redoing
4912                         undo_actions.Clear ();
4913
4914                         locked = true;
4915                         do {
4916                                 Line start;
4917                                 int start_index;
4918
4919                                 action = (Action) redo_actions.Pop ();
4920
4921                                 switch (action.type) {
4922
4923                                 case ActionType.UserActionBegin:
4924                                         //  Noop
4925                                         break;
4926
4927                                 case ActionType.UserActionEnd:
4928                                         user_action_finished = true;
4929                                         break;
4930
4931                                 case ActionType.InsertString:
4932                                         start = document.GetLine (action.line_no);
4933                                         document.SuspendUpdate ();
4934                                         start_index = document.LineTagToCharIndex (start, action.pos);
4935                                         document.InsertString (start, action.pos, (string) action.data);
4936                                         document.CharIndexToLineTag (start_index + ((string) action.data).Length,
4937                                                         out document.caret.line, out document.caret.tag,
4938                                                         out document.caret.pos);
4939                                         document.UpdateCaret ();
4940                                         document.SetSelectionToCaret (true);
4941                                         document.ResumeUpdate (true);
4942                                         break;
4943
4944                                 case ActionType.Typing:
4945                                         start = document.GetLine (action.line_no);
4946                                         document.SuspendUpdate ();
4947                                         start_index = document.LineTagToCharIndex (start, action.pos);
4948                                         document.InsertString (start, action.pos, ((StringBuilder) action.data).ToString ());
4949                                         document.CharIndexToLineTag (start_index + ((StringBuilder) action.data).Length,
4950                                                         out document.caret.line, out document.caret.tag,
4951                                                         out document.caret.pos);
4952                                         document.UpdateCaret ();
4953                                         document.SetSelectionToCaret (true);
4954                                         document.ResumeUpdate (true);
4955
4956                                         // This is an open ended operation, so only a single typing operation can be undone at once
4957                                         user_action_finished = true;
4958                                         break;
4959
4960                                 case ActionType.DeleteString:
4961                                         start = document.GetLine (action.line_no);
4962                                         document.SuspendUpdate ();
4963                                         document.DeleteMultiline (start, action.pos, ((Line) action.data).text.Length);
4964                                         document.PositionCaret (start, action.pos);
4965                                         document.SetSelectionToCaret (true);
4966                                         document.ResumeUpdate (true);
4967
4968                                         break;
4969                                 }
4970                         } while (!user_action_finished && redo_actions.Count > 0);
4971
4972                         locked = false;
4973                 }
4974                 #endregion      // Internal Methods
4975
4976                 #region Private Methods
4977
4978                 public void BeginUserAction (string name)
4979                 {
4980                         if (locked)
4981                                 return;
4982
4983                         Action ua = new Action ();
4984                         ua.type = ActionType.UserActionBegin;
4985                         ua.data = name;
4986
4987                         undo_actions.Push (ua);
4988                 }
4989
4990                 public void EndUserAction ()
4991                 {
4992                         if (locked)
4993                                 return;
4994
4995                         Action ua = new Action ();
4996                         ua.type = ActionType.UserActionEnd;
4997
4998                         undo_actions.Push (ua);
4999                 }
5000
5001                 // start_pos, end_pos = 1 based
5002                 public void RecordDeleteString (Line start_line, int start_pos, Line end_line, int end_pos)
5003                 {
5004                         if (locked)
5005                                 return;
5006
5007                         Action  a = new Action ();
5008
5009                         // We cant simply store the string, because then formatting would be lost
5010                         a.type = ActionType.DeleteString;
5011                         a.line_no = start_line.line_no;
5012                         a.pos = start_pos;
5013                         a.data = Duplicate (start_line, start_pos, end_line, end_pos);
5014
5015                         undo_actions.Push(a);
5016                 }
5017
5018                 public void RecordInsertString (Line line, int pos, string str)
5019                 {
5020                         if (locked || str.Length == 0)
5021                                 return;
5022
5023                         Action a = new Action ();
5024
5025                         a.type = ActionType.InsertString;
5026                         a.data = str;
5027                         a.line_no = line.line_no;
5028                         a.pos = pos;
5029
5030                         undo_actions.Push (a);
5031                 }
5032
5033                 public void RecordTyping (Line line, int pos, char ch)
5034                 {
5035                         if (locked)
5036                                 return;
5037
5038                         Action a = null;
5039
5040                         if (undo_actions.Count > 0)
5041                                 a = (Action) undo_actions.Peek ();
5042
5043                         if (a == null || a.type != ActionType.Typing) {
5044                                 a = new Action ();
5045                                 a.type = ActionType.Typing;
5046                                 a.data = new StringBuilder ();
5047                                 a.line_no = line.line_no;
5048                                 a.pos = pos;
5049
5050                                 undo_actions.Push (a);
5051                         }
5052
5053                         StringBuilder data = (StringBuilder) a.data;
5054                         data.Append (ch);
5055                 }
5056
5057                 // start_pos = 1-based
5058                 // end_pos = 1-based
5059                 public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos)
5060                 {
5061                         Line    ret;
5062                         Line    line;
5063                         Line    current;
5064                         LineTag tag;
5065                         LineTag current_tag;
5066                         int     start;
5067                         int     end;
5068                         int     tag_start;
5069
5070                         line = new Line (start_line.document);
5071                         ret = line;
5072
5073                         for (int i = start_line.line_no; i <= end_line.line_no; i++) {
5074                                 current = document.GetLine(i);
5075
5076                                 if (start_line.line_no == i) {
5077                                         start = start_pos;
5078                                 } else {
5079                                         start = 1;
5080                                 }
5081
5082                                 if (end_line.line_no == i) {
5083                                         end = end_pos;
5084                                 } else {
5085                                         end = current.text.Length;
5086                                 }
5087
5088                                 if (end_pos == 0)
5089                                         continue;
5090
5091                                 // Text for the tag
5092                                 line.text = new StringBuilder (current.text.ToString (start, end - start));
5093                                 
5094                                 // Copy tags from start to start+length onto new line
5095                                 current_tag = current.FindTag (start);
5096                                 while ((current_tag != null) && (current_tag.start < end)) {
5097                                         if ((current_tag.start <= start) && (start < (current_tag.start + current_tag.length))) {
5098                                                 // start tag is within this tag
5099                                                 tag_start = start;
5100                                         } else {
5101                                                 tag_start = current_tag.start;
5102                                         }
5103
5104                                         tag = new LineTag(line, tag_start - start + 1);
5105                                         tag.CopyFormattingFrom (current_tag);
5106
5107                                         current_tag = current_tag.next;
5108
5109                                         // Add the new tag to the line
5110                                         if (line.tags == null) {
5111                                                 line.tags = tag;
5112                                         } else {
5113                                                 LineTag tail;
5114                                                 tail = line.tags;
5115
5116                                                 while (tail.next != null) {
5117                                                         tail = tail.next;
5118                                                 }
5119                                                 tail.next = tag;
5120                                                 tag.previous = tail;
5121                                         }
5122                                 }
5123
5124                                 if ((i + 1) <= end_line.line_no) {
5125                                         line.soft_break = current.soft_break;
5126
5127                                         // Chain them (we use right/left as next/previous)
5128                                         line.right = new Line (start_line.document);
5129                                         line.right.left = line;
5130                                         line = line.right;
5131                                 }
5132                         }
5133
5134                         return ret;
5135                 }
5136
5137                 // Insert multi-line text at the given position; use formatting at insertion point for inserted text
5138                 internal void Insert(Line line, int pos, Line insert, bool select)
5139                 {
5140                         Line    current;
5141                         LineTag tag;
5142                         int     offset;
5143                         int     lines;
5144                         Line    first;
5145
5146                         // Handle special case first
5147                         if (insert.right == null) {
5148
5149                                 // Single line insert
5150                                 document.Split(line, pos);
5151
5152                                 if (insert.tags == null) {
5153                                         return; // Blank line
5154                                 }
5155
5156                                 //Insert our tags at the end
5157                                 tag = line.tags;
5158
5159                                 while (tag.next != null) {
5160                                         tag = tag.next;
5161                                 }
5162
5163                                 offset = tag.start + tag.length - 1;
5164
5165                                 tag.next = insert.tags;
5166                                 line.text.Insert(offset, insert.text.ToString());
5167
5168                                 // Adjust start locations
5169                                 tag = tag.next;
5170                                 while (tag != null) {
5171                                         tag.start += offset;
5172                                         tag.line = line;
5173                                         tag = tag.next;
5174                                 }
5175                                 // Put it back together
5176                                 document.Combine(line.line_no, line.line_no + 1);
5177
5178                                 if (select) {
5179                                         document.SetSelectionStart (line, pos);
5180                                         document.SetSelectionEnd (line, pos + insert.text.Length);
5181                                 }
5182
5183                                 document.UpdateView(line, pos);
5184                                 return;
5185                         }
5186
5187                         first = line;
5188                         lines = 1;
5189                         current = insert;
5190
5191                         while (current != null) {
5192                                 if (current == insert) {
5193                                         // Inserting the first line we split the line (and make space)
5194                                         document.Split(line, pos);
5195                                         //Insert our tags at the end of the line
5196                                         tag = line.tags;
5197
5198                                         if (tag != null) {
5199                                                 while (tag.next != null) {
5200                                                         tag = tag.next;
5201                                                 }
5202                                                 offset = tag.start + tag.length - 1;
5203                                                 tag.next = current.tags;
5204                                                 tag.next.previous = tag;
5205
5206                                                 tag = tag.next;
5207
5208                                         } else {
5209                                                 offset = 0;
5210                                                 line.tags = current.tags;
5211                                                 line.tags.previous = null;
5212                                                 tag = line.tags;
5213                                         }
5214                                 } else {
5215                                         document.Split(line.line_no, 0);
5216                                         offset = 0;
5217                                         line.tags = current.tags;
5218                                         line.tags.previous = null;
5219                                         tag = line.tags;
5220                                 }
5221                                 // Adjust start locations and line pointers
5222                                 while (tag != null) {
5223                                         tag.start += offset;
5224                                         tag.line = line;
5225                                         tag = tag.next;
5226                                 }
5227
5228                                 line.text.Insert(offset, current.text.ToString());
5229                                 line.Grow(line.text.Length);
5230
5231                                 line.recalc = true;
5232                                 line = document.GetLine(line.line_no + 1);
5233
5234                                 // FIXME? Test undo of line-boundaries
5235                                 if ((current.right == null) && (current.tags.length != 0)) {
5236                                         document.Combine(line.line_no - 1, line.line_no);
5237                                 }
5238                                 current = current.right;
5239                                 lines++;
5240
5241                         }
5242
5243                         // Recalculate our document
5244                         document.UpdateView(first, lines, pos);
5245                         return;
5246                 }               
5247                 #endregion      // Private Methods
5248         }
5249 }