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