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