2007-11-16 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextControl.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 // NOT COMPLETE
28
29 // There's still plenty of things missing, I've got most of it planned, just hadn't had
30 // the time to write it all yet.
31 // Stuff missing (in no particular order):
32 // - Align text after RecalculateLine
33 // - Implement tag types for hotlinks, etc.
34 // - Implement CaretPgUp/PgDown
35
36 // NOTE:
37 // selection_start.pos and selection_end.pos are 0-based
38 // selection_start.pos = first selected char
39 // selection_end.pos = first NOT-selected char
40 //
41 // FormatText methods are 1-based (as are all tags, LineTag.Start is 1 for 
42 // the first character on a line; the reason is that 0 is the position 
43 // *before* the first character on a line
44
45
46 #undef Debug
47
48 using System;
49 using System.Collections;
50 using System.Drawing;
51 using System.Drawing.Text;
52 using System.Text;
53 using RTF=System.Windows.Forms.RTF;
54
55 namespace System.Windows.Forms {
56         internal enum LineColor {
57                 Red     = 0,
58                 Black   = 1
59         }
60
61         internal enum CaretSelection {
62                 Position,       // Selection=Caret
63                 Word,           // Selection=Word under caret
64                 Line            // Selection=Line under caret
65         }
66
67         [Flags]
68         internal enum FormatSpecified {
69                 None,
70
71                 BackColor = 2,
72                 Font = 4,
73                 Color = 8,
74         }
75
76         internal enum CaretDirection {
77                 CharForward,    // Move a char to the right
78                 CharBack,       // Move a char to the left
79                 LineUp,         // Move a line up
80                 LineDown,       // Move a line down
81                 Home,           // Move to the beginning of the line
82                 End,            // Move to the end of the line
83                 PgUp,           // Move one page up
84                 PgDn,           // Move one page down
85                 CtrlPgUp,       // Move caret to the first visible char in the viewport
86                 CtrlPgDn,       // Move caret to the last visible char in the viewport
87                 CtrlHome,       // Move to the beginning of the document
88                 CtrlEnd,        // Move to the end of the document
89                 WordBack,       // Move to the beginning of the previous word (or beginning of line)
90                 WordForward,    // Move to the beginning of the next word (or end of line)
91                 SelectionStart, // Move to the beginning of the current selection
92                 SelectionEnd,   // Move to the end of the current selection
93                 CharForwardNoWrap,   // Move a char forward, but don't wrap onto the next line
94                 CharBackNoWrap      // Move a char backward, but don't wrap onto the previous line
95         }
96
97         internal enum LineEnding {
98                 Wrap = 1,    // line wraps to the next line
99                 Limp = 2,    // \r
100                 Hard = 4,    // \r\n
101                 Soft = 8,    // \r\r\n
102                 Rich = 16,    // \n
103
104                 None = 0
105         }
106         
107         internal class Document : ICloneable, IEnumerable {
108                 #region Structures
109                 // FIXME - go through code and check for places where
110                 // we do explicit comparisons instead of using the compare overloads
111                 internal struct Marker {
112                         internal Line           line;
113                         internal LineTag        tag;
114                         internal int            pos;
115                         internal int            height;
116
117                         public static bool operator<(Marker lhs, Marker rhs) {
118                                 if (lhs.line.line_no < rhs.line.line_no) {
119                                         return true;
120                                 }
121
122                                 if (lhs.line.line_no == rhs.line.line_no) {
123                                         if (lhs.pos < rhs.pos) {
124                                                 return true;
125                                         }
126                                 }
127                                 return false;
128                         }
129
130                         public static bool operator>(Marker lhs, Marker rhs) {
131                                 if (lhs.line.line_no > rhs.line.line_no) {
132                                         return true;
133                                 }
134
135                                 if (lhs.line.line_no == rhs.line.line_no) {
136                                         if (lhs.pos > rhs.pos) {
137                                                 return true;
138                                         }
139                                 }
140                                 return false;
141                         }
142
143                         public static bool operator==(Marker lhs, Marker rhs) {
144                                 if ((lhs.line.line_no == rhs.line.line_no) && (lhs.pos == rhs.pos)) {
145                                         return true;
146                                 }
147                                 return false;
148                         }
149
150                         public static bool operator!=(Marker lhs, Marker rhs) {
151                                 if ((lhs.line.line_no != rhs.line.line_no) || (lhs.pos != rhs.pos)) {
152                                         return true;
153                                 }
154                                 return false;
155                         }
156
157                         public void Combine(Line move_to_line, int move_to_line_length) {
158                                 line = move_to_line;
159                                 pos += move_to_line_length;
160                                 tag = LineTag.FindTag(line, pos);
161                         }
162
163                         // This is for future use, right now Document.Split does it by hand, with some added shortcut logic
164                         public void Split(Line move_to_line, int split_at) {
165                                 line = move_to_line;
166                                 pos -= split_at;
167                                 tag = LineTag.FindTag(line, pos);
168                         }
169
170                         public override bool Equals(object obj) {
171                                    return this==(Marker)obj;
172                         }
173
174                         public override int GetHashCode() {
175                                 return base.GetHashCode ();
176                         }
177
178                         public override string ToString() {
179                                 return "Marker Line " + line + ", Position " + pos;
180                         }
181
182                 }
183                 #endregion Structures
184
185                 #region Local Variables
186                 private Line            document;
187                 private int             lines;
188                 private Line            sentinel;
189                 private int             document_id;
190                 private Random          random = new Random();
191                 internal string         password_char;
192                 private StringBuilder   password_cache;
193                 private bool            calc_pass;
194                 private int             char_count;
195
196                 // For calculating widths/heights
197                 public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic);
198
199                 private int             recalc_suspended;
200                 private bool            recalc_pending;
201                 private int             recalc_start = 1;   // This starts at one, since lines are 1 based
202                 private int             recalc_end;
203                 private bool            recalc_optimize;
204
205                 private int             update_suspended;
206                 private bool update_pending;
207                 private int update_start = 1;
208
209                 internal bool           multiline;
210                 internal HorizontalAlignment alignment;
211                 internal bool           wrap;
212
213                 internal UndoManager    undo;
214
215                 internal Marker         caret;
216                 internal Marker         selection_start;
217                 internal Marker         selection_end;
218                 internal bool           selection_visible;
219                 internal Marker         selection_anchor;
220                 internal Marker         selection_prev;
221                 internal bool           selection_end_anchor;
222
223                 internal int            viewport_x;
224                 internal int            viewport_y;             // The visible area of the document
225                 internal int            viewport_width;
226                 internal int            viewport_height;
227
228                 internal int            document_x;             // Width of the document
229                 internal int            document_y;             // Height of the document
230
231                 internal Rectangle      invalid;
232
233                 internal int            crlf_size;              // 1 or 2, depending on whether we use \r\n or just \n
234
235                 internal TextBoxBase    owner;                  // Who's owning us?
236                 static internal int     caret_width = 1;
237                 static internal int     caret_shift = 1;
238
239                 internal int left_margin = 2;  // A left margin for all lines
240                 internal int top_margin = 2;
241                 internal int right_margin = 2;
242                 #endregion      // Local Variables
243
244                 #region Constructors
245                 internal Document (TextBoxBase owner)
246                 {
247                         lines = 0;
248
249                         this.owner = owner;
250
251                         multiline = true;
252                         password_char = "";
253                         calc_pass = false;
254                         recalc_pending = false;
255
256                         // Tree related stuff
257                         sentinel = new Line (this, LineEnding.None);
258                         sentinel.color = LineColor.Black;
259
260                         document = sentinel;
261
262                         // We always have a blank line
263                         owner.HandleCreated += new EventHandler(owner_HandleCreated);
264                         owner.VisibleChanged += new EventHandler(owner_VisibleChanged);
265
266                         Add (1, String.Empty, owner.Font, owner.ForeColor, LineEnding.None);
267
268                         undo = new UndoManager (this);
269
270                         selection_visible = false;
271                         selection_start.line = this.document;
272                         selection_start.pos = 0;
273                         selection_start.tag = selection_start.line.tags;
274                         selection_end.line = this.document;
275                         selection_end.pos = 0;
276                         selection_end.tag = selection_end.line.tags;
277                         selection_anchor.line = this.document;
278                         selection_anchor.pos = 0;
279                         selection_anchor.tag = selection_anchor.line.tags;
280                         caret.line = this.document;
281                         caret.pos = 0;
282                         caret.tag = caret.line.tags;
283
284                         viewport_x = 0;
285                         viewport_y = 0;
286
287                         crlf_size = 2;
288
289                         // Default selection is empty
290
291                         document_id = random.Next();
292
293                         string_format.Trimming = StringTrimming.None;
294                         string_format.FormatFlags = StringFormatFlags.DisplayFormatControl;
295
296                         UpdateMargins ();
297                 }
298                 #endregion
299
300                 #region Internal Properties
301                 internal Line Root {
302                         get {
303                                 return document;
304                         }
305
306                         set {
307                                 document = value;
308                         }
309                 }
310
311                 internal int Lines {
312                         get {
313                                 return lines;
314                         }
315                 }
316
317                 internal Line CaretLine {
318                         get {
319                                 return caret.line;
320                         }
321                 }
322
323                 internal int CaretPosition {
324                         get {
325                                 return caret.pos;
326                         }
327                 }
328
329                 internal Point Caret {
330                         get {
331                                 return new Point((int)caret.tag.Line.widths[caret.pos] + caret.line.X, caret.line.Y);
332                         }
333                 }
334
335                 internal LineTag CaretTag {
336                         get {
337                                 return caret.tag;
338                         }
339
340                         set {
341                                 caret.tag = value;
342                         }
343                 }
344
345                 internal int CRLFSize {
346                         get {
347                                 return crlf_size;
348                         }
349
350                         set {
351                                 crlf_size = value;
352                         }
353                 }
354
355                 internal string PasswordChar {
356                         get {
357                                 return password_char;
358                         }
359
360                         set {
361                                 password_char = value;
362                                 PasswordCache.Length = 0;
363                                 if ((password_char.Length != 0) && (password_char[0] != '\0')) {
364                                         calc_pass = true;
365                                 } else {
366                                         calc_pass = false;
367                                 }
368                         }
369                 }
370
371                 private StringBuilder PasswordCache {
372                         get { 
373                                 if (password_cache == null) 
374                                           password_cache = new StringBuilder(); 
375                                 return password_cache;
376                         }
377                 }
378
379                 internal int ViewPortX {
380                         get {
381                                 return viewport_x;
382                         }
383
384                         set {
385                                 viewport_x = value;
386                         }
387                 }
388
389                 internal int Length {
390                         get {
391                                 return char_count + lines - 1;  // Add \n for each line but the last
392                         }
393                 }
394
395                 private int CharCount {
396                         get {
397                                 return char_count;
398                         }
399
400                         set {
401                                 char_count = value;
402
403                                 if (LengthChanged != null) {
404                                         LengthChanged(this, EventArgs.Empty);
405                                 }
406                         }
407                 }
408
409                 internal int ViewPortY {
410                         get {
411                                 return viewport_y;
412                         }
413
414                         set {
415                                 viewport_y = value;
416                         }
417                 }
418
419                 internal int ViewPortWidth {
420                         get {
421                                 return viewport_width;
422                         }
423
424                         set {
425                                 viewport_width = value;
426                         }
427                 }
428
429                 internal int ViewPortHeight {
430                         get {
431                                 return viewport_height;
432                         }
433
434                         set {
435                                 viewport_height = value;
436                         }
437                 }
438
439
440                 internal int Width {
441                         get {
442                                 return this.document_x;
443                         }
444                 }
445
446                 internal int Height {
447                         get {
448                                 return this.document_y;
449                         }
450                 }
451
452                 internal bool SelectionVisible {
453                         get {
454                                 return selection_visible;
455                         }
456                 }
457
458                 internal bool Wrap {
459                         get {
460                                 return wrap;
461                         }
462
463                         set {
464                                 wrap = value;
465                         }
466                 }
467
468                 #endregion      // Internal Properties
469
470                 #region Private Methods
471
472                 internal void UpdateMargins ()
473                 {
474                         switch (owner.actual_border_style) {
475                                 case BorderStyle.None:
476                                         left_margin = 0;
477                                         top_margin = 0;
478                                         right_margin = 1;
479                                         break;
480                                 case BorderStyle.FixedSingle:
481                                         left_margin = 2;
482                                         top_margin = 2;
483                                         right_margin = 3;
484                                         break;
485                                 case BorderStyle.Fixed3D:
486                                         left_margin = 1;
487                                         top_margin = 1;
488                                         right_margin = 2;
489                                         break;
490                         }
491                 }
492
493                 internal void SuspendRecalc ()
494                 {
495                         if (recalc_suspended == 0) {
496                                 recalc_start = int.MaxValue;
497                                 recalc_end = int.MinValue;
498                         }
499                         
500                         recalc_suspended++;
501                 }
502
503                 internal void ResumeRecalc (bool immediate_update)
504                 {
505                         if (recalc_suspended > 0)
506                                 recalc_suspended--;
507
508                         if (recalc_suspended == 0 && (immediate_update || recalc_pending)) {
509                                 RecalculateDocument (owner.CreateGraphicsInternal (), recalc_start, recalc_end, recalc_optimize);
510                                 recalc_pending = false;
511                         }
512                 }
513
514                 internal void SuspendUpdate ()
515                 {
516                         update_suspended++;
517                 }
518
519                 internal void ResumeUpdate (bool immediate_update)
520                 {
521                         if (update_suspended > 0)
522                                 update_suspended--;
523
524                         if (immediate_update && update_suspended == 0 && update_pending) {
525                                 UpdateView (GetLine (update_start), 0);
526                                 update_pending = false;
527                         }
528                 }
529
530                 // For debugging
531                 internal int DumpTree(Line line, bool with_tags) {
532                         int     total;
533
534                         total = 1;
535
536                         Console.Write("Line {0} [# {1}], Y: {2}, ending style: {3},  Text: '{4}'",
537                                         line.line_no, line.GetHashCode(), line.Y, line.ending,
538                                         line.text != null ? line.text.ToString() : "undefined");
539
540                         if (line.left == sentinel) {
541                                 Console.Write(", left = sentinel");
542                         } else if (line.left == null) {
543                                 Console.Write(", left = NULL");
544                         }
545
546                         if (line.right == sentinel) {
547                                 Console.Write(", right = sentinel");
548                         } else if (line.right == null) {
549                                 Console.Write(", right = NULL");
550                         }
551
552                         Console.WriteLine("");
553
554                         if (with_tags) {
555                                 LineTag tag;
556                                 int     count;
557                                 int     length;
558
559                                 tag = line.tags;
560                                 count = 1;
561                                 length = 0;
562                                 Console.Write("   Tags: ");
563                                 while (tag != null) {
564                                         Console.Write("{0} <{1}>-<{2}>", count++, tag.Start, tag.End
565                                                         /*line.text.ToString (tag.start - 1, tag.length)*/);
566                                         length += tag.Length;
567
568                                         if (tag.Line != line) {
569                                                 Console.Write("BAD line link");
570                                                 throw new Exception("Bad line link in tree");
571                                         }
572                                         tag = tag.Next;
573                                         if (tag != null) {
574                                                 Console.Write(", ");
575                                         }
576                                 }
577                                 if (length > line.text.Length) {
578                                         throw new Exception(String.Format("Length of tags more than length of text on line (expected {0} calculated {1})", line.text.Length, length));
579                                 } else if (length < line.text.Length) {
580                                         throw new Exception(String.Format("Length of tags less than length of text on line (expected {0} calculated {1})", line.text.Length, length));
581                                 }
582                                 Console.WriteLine("");
583                         }
584                         if (line.left != null) {
585                                 if (line.left != sentinel) {
586                                         total += DumpTree(line.left, with_tags);
587                                 }
588                         } else {
589                                 if (line != sentinel) {
590                                         throw new Exception("Left should not be NULL");
591                                 }
592                         }
593
594                         if (line.right != null) {
595                                 if (line.right != sentinel) {
596                                         total += DumpTree(line.right, with_tags);
597                                 }
598                         } else {
599                                 if (line != sentinel) {
600                                         throw new Exception("Right should not be NULL");
601                                 }
602                         }
603
604                         for (int i = 1; i <= this.lines; i++) {
605                                 if (GetLine(i) == null) {
606                                         throw new Exception(String.Format("Hole in line order, missing {0}", i));
607                                 }
608                         }
609
610                         if (line == this.Root) {
611                                 if (total < this.lines) {
612                                         throw new Exception(String.Format("Not enough nodes in tree, found {0}, expected {1}", total, this.lines));
613                                 } else if (total > this.lines) {
614                                         throw new Exception(String.Format("Too many nodes in tree, found {0}, expected {1}", total, this.lines));
615                                 }
616                         }
617
618                         return total;
619                 }
620
621                 private void SetSelectionVisible (bool value)
622                 {
623                         selection_visible = value;
624
625                         // cursor and selection are enemies, we can't have both in the same room at the same time
626                         if (owner.IsHandleCreated && !owner.show_caret_w_selection)
627                                 XplatUI.CaretVisible (owner.Handle, !selection_visible);
628                 }
629
630                 private void DecrementLines(int line_no) {
631                         int     current;
632
633                         current = line_no;
634                         while (current <= lines) {
635                                 GetLine(current).line_no--;
636                                 current++;
637                         }
638                         return;
639                 }
640
641                 private void IncrementLines(int line_no) {
642                         int     current;
643
644                         current = this.lines;
645                         while (current >= line_no) {
646                                 GetLine(current).line_no++;
647                                 current--;
648                         }
649                         return;
650                 }
651
652                 private void RebalanceAfterAdd(Line line1) {
653                         Line    line2;
654
655                         while ((line1 != document) && (line1.parent.color == LineColor.Red)) {
656                                 if (line1.parent == line1.parent.parent.left) {
657                                         line2 = line1.parent.parent.right;
658
659                                         if ((line2 != null) && (line2.color == LineColor.Red)) {
660                                                 line1.parent.color = LineColor.Black;
661                                                 line2.color = LineColor.Black;
662                                                 line1.parent.parent.color = LineColor.Red;
663                                                 line1 = line1.parent.parent;
664                                         } else {
665                                                 if (line1 == line1.parent.right) {
666                                                         line1 = line1.parent;
667                                                         RotateLeft(line1);
668                                                 }
669
670                                                 line1.parent.color = LineColor.Black;
671                                                 line1.parent.parent.color = LineColor.Red;
672
673                                                 RotateRight(line1.parent.parent);
674                                         }
675                                 } else {
676                                         line2 = line1.parent.parent.left;
677
678                                         if ((line2 != null) && (line2.color == LineColor.Red)) {
679                                                 line1.parent.color = LineColor.Black;
680                                                 line2.color = LineColor.Black;
681                                                 line1.parent.parent.color = LineColor.Red;
682                                                 line1 = line1.parent.parent;
683                                         } else {
684                                                 if (line1 == line1.parent.left) {
685                                                         line1 = line1.parent;
686                                                         RotateRight(line1);
687                                                 }
688
689                                                 line1.parent.color = LineColor.Black;
690                                                 line1.parent.parent.color = LineColor.Red;
691                                                 RotateLeft(line1.parent.parent);
692                                         }
693                                 }
694                         }
695                         document.color = LineColor.Black;
696                 }
697
698                 private void RebalanceAfterDelete(Line line1) {
699                         Line line2;
700
701                         while ((line1 != document) && (line1.color == LineColor.Black)) {
702                                 if (line1 == line1.parent.left) {
703                                         line2 = line1.parent.right;
704                                         if (line2.color == LineColor.Red) { 
705                                                 line2.color = LineColor.Black;
706                                                 line1.parent.color = LineColor.Red;
707                                                 RotateLeft(line1.parent);
708                                                 line2 = line1.parent.right;
709                                         }
710                                         if ((line2.left.color == LineColor.Black) && (line2.right.color == LineColor.Black)) { 
711                                                 line2.color = LineColor.Red;
712                                                 line1 = line1.parent;
713                                         } else {
714                                                 if (line2.right.color == LineColor.Black) {
715                                                         line2.left.color = LineColor.Black;
716                                                         line2.color = LineColor.Red;
717                                                         RotateRight(line2);
718                                                         line2 = line1.parent.right;
719                                                 }
720                                                 line2.color = line1.parent.color;
721                                                 line1.parent.color = LineColor.Black;
722                                                 line2.right.color = LineColor.Black;
723                                                 RotateLeft(line1.parent);
724                                                 line1 = document;
725                                         }
726                                 } else { 
727                                         line2 = line1.parent.left;
728                                         if (line2.color == LineColor.Red) {
729                                                 line2.color = LineColor.Black;
730                                                 line1.parent.color = LineColor.Red;
731                                                 RotateRight(line1.parent);
732                                                 line2 = line1.parent.left;
733                                         }
734                                         if ((line2.right.color == LineColor.Black) && (line2.left.color == LineColor.Black)) {
735                                                 line2.color = LineColor.Red;
736                                                 line1 = line1.parent;
737                                         } else {
738                                                 if (line2.left.color == LineColor.Black) {
739                                                         line2.right.color = LineColor.Black;
740                                                         line2.color = LineColor.Red;
741                                                         RotateLeft(line2);
742                                                         line2 = line1.parent.left;
743                                                 }
744                                                 line2.color = line1.parent.color;
745                                                 line1.parent.color = LineColor.Black;
746                                                 line2.left.color = LineColor.Black;
747                                                 RotateRight(line1.parent);
748                                                 line1 = document;
749                                         }
750                                 }
751                         }
752                         line1.color = LineColor.Black;
753                 }
754
755                 private void RotateLeft(Line line1) {
756                         Line    line2 = line1.right;
757
758                         line1.right = line2.left;
759
760                         if (line2.left != sentinel) {
761                                 line2.left.parent = line1;
762                         }
763
764                         if (line2 != sentinel) {
765                                 line2.parent = line1.parent;
766                         }
767
768                         if (line1.parent != null) {
769                                 if (line1 == line1.parent.left) {
770                                         line1.parent.left = line2;
771                                 } else {
772                                         line1.parent.right = line2;
773                                 }
774                         } else {
775                                 document = line2;
776                         }
777
778                         line2.left = line1;
779                         if (line1 != sentinel) {
780                                 line1.parent = line2;
781                         }
782                 }
783
784                 private void RotateRight(Line line1) {
785                         Line line2 = line1.left;
786
787                         line1.left = line2.right;
788
789                         if (line2.right != sentinel) {
790                                 line2.right.parent = line1;
791                         }
792
793                         if (line2 != sentinel) {
794                                 line2.parent = line1.parent;
795                         }
796
797                         if (line1.parent != null) {
798                                 if (line1 == line1.parent.right) {
799                                         line1.parent.right = line2;
800                                 } else {
801                                         line1.parent.left = line2;
802                                 }
803                         } else {
804                                 document = line2;
805                         }
806
807                         line2.right = line1;
808                         if (line1 != sentinel) {
809                                 line1.parent = line2;
810                         }
811                 }        
812
813
814                 internal void UpdateView(Line line, int pos) {
815                         if (!owner.IsHandleCreated) {
816                                 return;
817                         }
818
819                         if (update_suspended > 0) {
820                                 update_start = Math.Min (update_start, line.line_no);
821                                 // update_end = Math.Max (update_end, line.line_no);
822                                 // recalc_optimize = true;
823                                 update_pending = true;
824                                 return;
825                         }
826
827                         // Optimize invalidation based on Line alignment
828                         if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no, true)) {
829                                 // Lineheight changed, invalidate the rest of the document
830                                 if ((line.Y - viewport_y) >=0 ) {
831                                         // We formatted something that's in view, only draw parts of the screen
832                                         owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
833                                 } else {
834                                         // The tag was above the visible area, draw everything
835                                         owner.Invalidate();
836                                 }
837                         } else {
838                                 switch(line.alignment) {
839                                         case HorizontalAlignment.Left: {
840                                                 owner.Invalidate(new Rectangle(line.X + (int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, viewport_width, line.height + 1));
841                                                 break;
842                                         }
843
844                                         case HorizontalAlignment.Center: {
845                                                 owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, viewport_width, line.height + 1));
846                                                 break;
847                                         }
848
849                                         case HorizontalAlignment.Right: {
850                                                 owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, (int)line.widths[pos + 1] - viewport_x + line.X, line.height + 1));
851                                                 break;
852                                         }
853                                 }
854                         }
855                 }
856
857
858                 // Update display from line, down line_count lines; pos is unused, but required for the signature
859                 internal void UpdateView(Line line, int line_count, int pos) {
860                         if (!owner.IsHandleCreated) {
861                                 return;
862                         }
863
864                         if (recalc_suspended > 0) {
865                                 recalc_start = Math.Min (recalc_start, line.line_no);
866                                 recalc_end = Math.Max (recalc_end, line.line_no + line_count);
867                                 recalc_optimize = true;
868                                 recalc_pending = true;
869                                 return;
870                         }
871
872                         int start_line_top = line.Y;                    
873
874                         int end_line_bottom;
875                         Line end_line;
876
877                         end_line = GetLine (line.line_no + line_count);
878                         if (end_line == null)
879                                 end_line = GetLine (lines);
880
881
882                         end_line_bottom = end_line.Y + end_line.height;
883                         
884                         if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count, true)) {
885                                 // Lineheight changed, invalidate the rest of the document
886                                 if ((line.Y - viewport_y) >=0 ) {
887                                         // We formatted something that's in view, only draw parts of the screen
888                                         owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
889                                 } else {
890                                         // The tag was above the visible area, draw everything
891                                         owner.Invalidate();
892                                 }
893                         } else {
894                                 int x = 0 - viewport_x;
895                                 int w = viewport_width;
896                                 int y = Math.Min (start_line_top - viewport_y, line.Y - viewport_y);
897                                 int h = Math.Max (end_line_bottom - y, end_line.Y + end_line.height - y);
898
899                                 owner.Invalidate (new Rectangle (x, y, w, h));
900                         }
901                 }
902                 #endregion      // Private Methods
903
904                 #region Internal Methods
905                 // Clear the document and reset state
906                 internal void Empty() {
907
908                         document = sentinel;
909                         lines = 0;
910
911                         // We always have a blank line
912                         Add (1, String.Empty, owner.Font, owner.ForeColor, LineEnding.None);
913                         
914                         this.RecalculateDocument(owner.CreateGraphicsInternal());
915                         PositionCaret(0, 0);
916
917                         SetSelectionVisible (false);
918
919                         selection_start.line = this.document;
920                         selection_start.pos = 0;
921                         selection_start.tag = selection_start.line.tags;
922                         selection_end.line = this.document;
923                         selection_end.pos = 0;
924                         selection_end.tag = selection_end.line.tags;
925                         char_count = 0;
926
927                         viewport_x = 0;
928                         viewport_y = 0;
929
930                         document_x = 0;
931                         document_y = 0;
932
933                         if (owner.IsHandleCreated)
934                                 owner.Invalidate ();
935                 }
936
937                 internal void PositionCaret(Line line, int pos) {
938                         caret.tag = line.FindTag (pos);
939
940                         MoveCaretToTextTag ();
941
942                         caret.line = line;
943                         caret.pos = pos;
944
945                         if (owner.IsHandleCreated) {
946                                 if (owner.Focused) {
947                                         if (caret.height != caret.tag.Height)
948                                                 XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
949                                         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);
950                                 }
951
952                                 if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
953                         }
954
955                         // We set this at the end because we use the heights to determine whether or
956                         // not we need to recreate the caret
957                         caret.height = caret.tag.Height;
958
959                 }
960
961                 internal void PositionCaret(int x, int y) {
962                         if (!owner.IsHandleCreated) {
963                                 return;
964                         }
965
966                         caret.tag = FindCursor(x, y, out caret.pos);
967                         
968                         MoveCaretToTextTag ();
969                         
970                         caret.line = caret.tag.Line;
971                         caret.height = caret.tag.Height;
972
973                         if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
974                                 XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
975                                 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);
976                         }
977
978                         if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
979                 }
980
981                 internal void CaretHasFocus() {
982                         if ((caret.tag != null) && owner.IsHandleCreated) {
983                                 XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
984                                 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);
985
986                                 DisplayCaret ();
987                         }
988
989                         if (owner.IsHandleCreated && SelectionLength () > 0) {
990                                 InvalidateSelectionArea ();
991                         }
992                 }
993
994                 internal void CaretLostFocus() {
995                         if (!owner.IsHandleCreated) {
996                                 return;
997                         }
998                         XplatUI.DestroyCaret(owner.Handle);
999                 }
1000
1001                 internal void AlignCaret() {
1002                         if (!owner.IsHandleCreated) {
1003                                 return;
1004                         }
1005
1006                         caret.tag = LineTag.FindTag (caret.line, caret.pos);
1007
1008                         MoveCaretToTextTag ();
1009
1010                         caret.height = caret.tag.Height;
1011
1012                         if (owner.Focused) {
1013                                 XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
1014                                 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);
1015                                 DisplayCaret ();
1016                         }
1017
1018                         if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
1019                 }
1020
1021                 internal void UpdateCaret() {
1022                         if (!owner.IsHandleCreated || caret.tag == null) {
1023                                 return;
1024                         }
1025
1026                         MoveCaretToTextTag ();
1027
1028                         if (caret.tag.Height != caret.height) {
1029                                 caret.height = caret.tag.Height;
1030                                 if (owner.Focused) {
1031                                         XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
1032                                 }
1033                         }
1034
1035                         if (owner.Focused) {
1036                                 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);
1037                                 DisplayCaret ();
1038                         }
1039                         
1040                         if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
1041                 }
1042
1043                 internal void DisplayCaret() {
1044                         if (!owner.IsHandleCreated) {
1045                                 return;
1046                         }
1047
1048                         if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
1049                                 XplatUI.CaretVisible(owner.Handle, true);
1050                         }
1051                 }
1052
1053                 internal void HideCaret() {
1054                         if (!owner.IsHandleCreated) {
1055                                 return;
1056                         }
1057
1058                         if (owner.Focused) {
1059                                 XplatUI.CaretVisible(owner.Handle, false);
1060                         }
1061                 }
1062
1063                 
1064                 internal void MoveCaretToTextTag ()
1065                 {
1066                         if (caret.tag == null || caret.tag.IsTextTag)
1067                                 return;
1068
1069                         
1070
1071                         if (caret.pos < caret.tag.Start) {
1072                                 caret.tag = caret.tag.Previous;
1073                         } else {
1074                                 caret.tag = caret.tag.Next;
1075                         }
1076                 }
1077
1078                 internal void MoveCaret(CaretDirection direction) {
1079                         // FIXME should we use IsWordSeparator to detect whitespace, instead 
1080                         // of looking for actual spaces in the Word move cases?
1081
1082                         bool nowrap = false;
1083                         switch(direction) {
1084                                 case CaretDirection.CharForwardNoWrap:
1085                                         nowrap = true;
1086                                         goto case CaretDirection.CharForward;
1087                                 case CaretDirection.CharForward: {
1088                                         caret.pos++;
1089                                         if (caret.pos > caret.line.TextLengthWithoutEnding ()) {
1090                                                 if (!nowrap) {
1091                                                         // Go into next line
1092                                                         if (caret.line.line_no < this.lines) {
1093                                                                 caret.line = GetLine(caret.line.line_no+1);
1094                                                                 caret.pos = 0;
1095                                                                 caret.tag = caret.line.tags;
1096                                                         } else {
1097                                                                 caret.pos--;
1098                                                         }
1099                                                 } else {
1100                                                         // Single line; we stay where we are
1101                                                         caret.pos--;
1102                                                 }
1103                                         } else {
1104                                                 if ((caret.tag.Start - 1 + caret.tag.Length) < caret.pos) {
1105                                                         caret.tag = caret.tag.Next;
1106                                                 }
1107                                         }
1108                                         UpdateCaret();
1109                                         return;
1110                                 }
1111
1112                                 case CaretDirection.CharBackNoWrap:
1113                                         nowrap = true;
1114                                         goto case CaretDirection.CharBack;
1115                                 case CaretDirection.CharBack: {
1116                                         if (caret.pos > 0) {
1117                                                 // caret.pos--; // folded into the if below
1118                                                 
1119                                                 if (--caret.pos > 0) {
1120                                                         if (caret.tag.Start > caret.pos) {
1121                                                                 caret.tag = caret.tag.Previous;
1122                                                         }
1123                                                 }
1124                                         } else {
1125                                                 if (caret.line.line_no > 1 && !nowrap) {
1126                                                         caret.line = GetLine(caret.line.line_no - 1);
1127                                                         caret.pos = caret.line.TextLengthWithoutEnding ();
1128                                                         caret.tag = LineTag.FindTag(caret.line, caret.pos);
1129                                                 }
1130                                         }
1131                                         UpdateCaret();
1132                                         return;
1133                                 }
1134
1135                                 case CaretDirection.WordForward: {
1136                                         int len;
1137
1138                                         len = caret.line.text.Length;
1139                                         if (caret.pos < len) {
1140                                                 while ((caret.pos < len) && (caret.line.text[caret.pos] != ' ')) {
1141                                                         caret.pos++;
1142                                                 }
1143                                                 if (caret.pos < len) {
1144                                                         // Skip any whitespace
1145                                                         while ((caret.pos < len) && (caret.line.text[caret.pos] == ' ')) {
1146                                                                 caret.pos++;
1147                                                         }
1148                                                 }
1149                                                 caret.tag = LineTag.FindTag(caret.line, caret.pos);
1150                                         } else {
1151                                                 if (caret.line.line_no < this.lines) {
1152                                                         caret.line = GetLine(caret.line.line_no + 1);
1153                                                         caret.pos = 0;
1154                                                         caret.tag = caret.line.tags;
1155                                                 }
1156                                         }
1157                                         UpdateCaret();
1158                                         return;
1159                                 }
1160
1161                                 case CaretDirection.WordBack: {
1162                                         if (caret.pos > 0) {
1163                                                 caret.pos--;
1164
1165                                                 while ((caret.pos > 0) && (caret.line.text[caret.pos] == ' ')) {
1166                                                         caret.pos--;
1167                                                 }
1168
1169                                                 while ((caret.pos > 0) && (caret.line.text[caret.pos] != ' ')) {
1170                                                         caret.pos--;
1171                                                 }
1172
1173                                                 if (caret.line.text.ToString(caret.pos, 1) == " ") {
1174                                                         if (caret.pos != 0) {
1175                                                                 caret.pos++;
1176                                                         } else {
1177                                                                 caret.line = GetLine(caret.line.line_no - 1);
1178                                                                 caret.pos = caret.line.text.Length;
1179                                                         }
1180                                                 }
1181                                                 caret.tag = LineTag.FindTag(caret.line, caret.pos);
1182                                         } else {
1183                                                 if (caret.line.line_no > 1) {
1184                                                         caret.line = GetLine(caret.line.line_no - 1);
1185                                                         caret.pos = caret.line.text.Length;
1186                                                         caret.tag = LineTag.FindTag(caret.line, caret.pos);
1187                                                 }
1188                                         }
1189                                         UpdateCaret();
1190                                         return;
1191                                 }
1192
1193                                 case CaretDirection.LineUp: {
1194                                         if (caret.line.line_no > 1) {
1195                                                 int     pixel;
1196
1197                                                 pixel = (int)caret.line.widths[caret.pos];
1198                                                 PositionCaret(pixel, GetLine(caret.line.line_no - 1).Y);
1199
1200                                                 DisplayCaret ();
1201                                         }
1202                                         return;
1203                                 }
1204
1205                                 case CaretDirection.LineDown: {
1206                                         if (caret.line.line_no < lines) {
1207                                                 int     pixel;
1208
1209                                                 pixel = (int)caret.line.widths[caret.pos];
1210                                                 PositionCaret(pixel, GetLine(caret.line.line_no + 1).Y);
1211
1212                                                 DisplayCaret ();
1213                                         }
1214                                         return;
1215                                 }
1216
1217                                 case CaretDirection.Home: {
1218                                         if (caret.pos > 0) {
1219                                                 caret.pos = 0;
1220                                                 caret.tag = caret.line.tags;
1221                                                 UpdateCaret();
1222                                         }
1223                                         return;
1224                                 }
1225
1226                                 case CaretDirection.End: {
1227                                         if (caret.pos < caret.line.TextLengthWithoutEnding ()) {
1228                                                 caret.pos = caret.line.TextLengthWithoutEnding ();
1229                                                 caret.tag = LineTag.FindTag(caret.line, caret.pos);
1230                                                 UpdateCaret();
1231                                         }
1232                                         return;
1233                                 }
1234
1235                                 case CaretDirection.PgUp: {
1236
1237                                         if (viewport_y == 0 && owner.richtext) {
1238                                                 owner.vscroll.Value = 0;
1239                                                 Line line = GetLine (1);
1240                                                 PositionCaret (line, 0);
1241                                         }
1242
1243                                         int y_offset = caret.line.Y + caret.line.height - 1 - viewport_y;
1244                                         int index;
1245                                         LineTag top = FindCursor ((int) caret.line.widths [caret.pos],
1246                                                         viewport_y - viewport_height, out index);
1247
1248                                         owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height);
1249                                         PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
1250
1251                                         return;
1252                                 }
1253
1254                                 case CaretDirection.PgDn: {
1255
1256                                         if (viewport_y + viewport_height >= document_y && owner.richtext) {
1257                                                 owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1;
1258                                                 Line line = GetLine (lines);
1259                                                 PositionCaret (line, line.Text.Length);
1260                                         }
1261
1262                                         int y_offset = caret.line.Y - viewport_y;
1263                                         int index;
1264                                         LineTag top = FindCursor ((int) caret.line.widths [caret.pos],
1265                                                         viewport_y + viewport_height, out index);
1266
1267                                         owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height);
1268                                         PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
1269                                         
1270                                         return;
1271                                 }
1272
1273                                 case CaretDirection.CtrlPgUp: {
1274                                         PositionCaret(0, viewport_y);
1275                                         DisplayCaret ();
1276                                         return;
1277                                 }
1278
1279                                 case CaretDirection.CtrlPgDn: {
1280                                         Line    line;
1281                                         LineTag tag;
1282                                         int     index;
1283
1284                                         tag = FindCursor (0, viewport_y + viewport_height, out index);
1285                                         if (tag.Line.line_no > 1) {
1286                                                 line = GetLine(tag.Line.line_no - 1);
1287                                         } else {
1288                                                 line = tag.Line;
1289                                         }
1290                                         PositionCaret(line, line.Text.Length);
1291                                         DisplayCaret ();
1292                                         return;
1293                                 }
1294
1295                                 case CaretDirection.CtrlHome: {
1296                                         caret.line = GetLine(1);
1297                                         caret.pos = 0;
1298                                         caret.tag = caret.line.tags;
1299
1300                                         UpdateCaret();
1301                                         return;
1302                                 }
1303
1304                                 case CaretDirection.CtrlEnd: {
1305                                         caret.line = GetLine(lines);
1306                                         caret.pos = caret.line.TextLengthWithoutEnding ();
1307                                         caret.tag = LineTag.FindTag(caret.line, caret.pos);
1308
1309                                         UpdateCaret();
1310                                         return;
1311                                 }
1312
1313                                 case CaretDirection.SelectionStart: {
1314                                         caret.line = selection_start.line;
1315                                         caret.pos = selection_start.pos;
1316                                         caret.tag = selection_start.tag;
1317
1318                                         UpdateCaret();
1319                                         return;
1320                                 }
1321
1322                                 case CaretDirection.SelectionEnd: {
1323                                         caret.line = selection_end.line;
1324                                         caret.pos = selection_end.pos;
1325                                         caret.tag = selection_end.tag;
1326
1327                                         UpdateCaret();
1328                                         return;
1329                                 }
1330                         }
1331                 }
1332
1333                 internal void DumpDoc ()
1334                 {
1335                         Console.WriteLine ("<doc lines='{0}'>", lines);
1336                         for (int i = 1; i <= lines ; i++) {
1337                                 Line line = GetLine (i);
1338                                 Console.WriteLine ("<line no='{0}' ending='{1}'>", line.line_no, line.ending);
1339
1340                                 LineTag tag = line.tags;
1341                                 while (tag != null) {
1342                                         Console.Write ("\t<tag type='{0}' span='{1}->{2}' font='{3}' color='{4}'>",
1343                                                         tag.GetType (), tag.Start, tag.Length, tag.Font, tag.Color);
1344                                         Console.Write (tag.Text ());
1345                                         Console.WriteLine ("</tag>");
1346                                         tag = tag.Next;
1347                                 }
1348                                 Console.WriteLine ("</line>");
1349                         }
1350                         Console.WriteLine ("</doc>");
1351                 }
1352
1353                 internal void Draw (Graphics g, Rectangle clip)
1354                 {
1355                         Line line;              // Current line being drawn
1356                         LineTag tag;            // Current tag being drawn
1357                         int start;              // First line to draw
1358                         int end;                // Last line to draw
1359                         StringBuilder text;     // String representing the current line
1360                         int line_no;
1361                         Color tag_color;
1362                         Color current_color;
1363
1364                         // First, figure out from what line to what line we need to draw
1365
1366                         if (multiline) {
1367                                 start = GetLineByPixel(clip.Top + viewport_y, false).line_no;
1368                                 end = GetLineByPixel(clip.Bottom + viewport_y, false).line_no;
1369                         } else {
1370                                 start = GetLineByPixel(clip.Left + viewport_x, false).line_no;
1371                                 end = GetLineByPixel(clip.Right + viewport_x, false).line_no;
1372                         }
1373
1374                         ///
1375                         /// We draw the single border ourself
1376                         ///
1377                         if (owner.actual_border_style == BorderStyle.FixedSingle) {
1378                                 ControlPaint.DrawBorder (g, owner.ClientRectangle, Color.Black, ButtonBorderStyle.Solid);
1379                         }
1380
1381                         /// Make sure that we aren't drawing one more line then we need to
1382                         line = GetLine (end - 1);
1383                         if (line != null && clip.Bottom == line.Y + line.height + viewport_y)
1384                                 end--;                  
1385
1386                         line_no = start;
1387
1388                         #if Debug
1389                                 DateTime        n = DateTime.Now;
1390                                 Console.WriteLine ("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
1391                                 Console.WriteLine ("CLIP:  {0}", clip);
1392                                 Console.WriteLine ("S: {0}", GetLine (start).text);
1393                                 Console.WriteLine ("E: {0}", GetLine (end).text);
1394                         #endif
1395
1396                         // Non multiline selection can be handled outside of the loop
1397                         if (!multiline && selection_visible && owner.ShowSelection) {
1398                                 g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight),
1399                                                 selection_start.line.widths [selection_start.pos] +
1400                                                 selection_start.line.X - viewport_x, 
1401                                                 selection_start.line.Y,
1402                                                 (selection_end.line.X + selection_end.line.widths [selection_end.pos]) -
1403                                                 (selection_start.line.X + selection_start.line.widths [selection_start.pos]), 
1404                                                 selection_start.line.height);
1405                         }
1406
1407                         while (line_no <= end) {
1408                                 line = GetLine (line_no);
1409                                 float line_y = line.Y - viewport_y;
1410                                 
1411                                 tag = line.tags;
1412                                 if (!calc_pass) {
1413                                         text = line.text;
1414                                 } else {
1415                                         if (PasswordCache.Length < line.text.Length)
1416                                                 PasswordCache.Append(Char.Parse(password_char), line.text.Length - PasswordCache.Length);
1417                                         else if (PasswordCache.Length > line.text.Length)
1418                                                 PasswordCache.Remove(line.text.Length, PasswordCache.Length - line.text.Length);
1419                                         text = PasswordCache;
1420                                 }
1421
1422                                 int line_selection_start = text.Length + 1;
1423                                 int line_selection_end = text.Length + 1;
1424                                 if (selection_visible && owner.ShowSelection &&
1425                                                 (line_no >= selection_start.line.line_no) &&
1426                                                 (line_no <= selection_end.line.line_no)) {
1427
1428                                         if (line_no == selection_start.line.line_no)
1429                                                 line_selection_start = selection_start.pos + 1;
1430                                         else
1431                                                 line_selection_start = 1;
1432
1433                                         if (line_no == selection_end.line.line_no)
1434                                                 line_selection_end = selection_end.pos + 1;
1435                                         else
1436                                                 line_selection_end = text.Length + 1;
1437
1438                                         if (line_selection_end == line_selection_start) {
1439                                                 // There isn't really selection
1440                                                 line_selection_start = text.Length + 1;
1441                                                 line_selection_end = line_selection_start;
1442                                         } else if (multiline) {
1443                                                 // lets draw some selection baby!!  (non multiline selection is drawn outside the loop)
1444                                                 g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight),
1445                                                                 line.widths [line_selection_start - 1] + line.X - viewport_x, 
1446                                                                 line_y, line.widths [line_selection_end - 1] - line.widths [line_selection_start - 1], 
1447                                                                 line.height);
1448                                         }
1449                                 }
1450
1451                                 current_color = line.tags.Color;
1452                                 while (tag != null) {
1453
1454                                         // Skip empty tags
1455                                         if (tag.Length == 0) {
1456                                                 tag = tag.Next;
1457                                                 continue;
1458                                         }
1459
1460                                         if (((tag.X + tag.Width) < (clip.Left - viewport_x)) && (tag.X > (clip.Right - viewport_x))) {
1461                                                 tag = tag.Next;
1462                                                 continue;
1463                                         }
1464
1465                                         if (tag.BackColor != Color.Empty) {
1466                                                 g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (tag.BackColor), tag.X + line.X - viewport_x,
1467                                                                 line_y + tag.Shift, tag.Width, line.height);
1468                                         }
1469
1470                                         tag_color = tag.Color;
1471                                         current_color = tag_color;
1472
1473                                         if (!owner.Enabled) {
1474                                                 Color a = tag.Color;
1475                                                 Color b = ThemeEngine.Current.ColorWindowText;
1476
1477                                                 if ((a.R == b.R) && (a.G == b.G) && (a.B == b.B)) {
1478                                                         tag_color = ThemeEngine.Current.ColorGrayText;
1479                                                 }
1480                                         } else if (owner.read_only && !owner.backcolor_set) {
1481                                                 tag_color = ThemeEngine.Current.ColorControlText;
1482                                         }
1483
1484                                         int tag_pos = tag.Start;
1485                                         current_color = tag_color;
1486                                         while (tag_pos < tag.Start + tag.Length) {
1487                                                 int old_tag_pos = tag_pos;
1488
1489                                                 if (tag_pos >= line_selection_start && tag_pos < line_selection_end) {
1490                                                         current_color = ThemeEngine.Current.ColorHighlightText;
1491                                                         tag_pos = Math.Min (tag.End, line_selection_end);
1492                                                 } else if (tag_pos < line_selection_start) {
1493                                                         current_color = tag_color;
1494                                                         tag_pos = Math.Min (tag.End, line_selection_start);
1495                                                 } else {
1496                                                         current_color = tag_color;
1497                                                         tag_pos = tag.End;
1498                                                 }
1499
1500                                                 tag.Draw (g, current_color,
1501                                                                 line.X - viewport_x,
1502                                                                 line_y + tag.Shift,
1503                                                                 old_tag_pos - 1, Math.Min (tag.Start + tag.Length, tag_pos) - 1,
1504                                                                 text.ToString() );
1505                                         }
1506                                         tag = tag.Next;
1507                                 }
1508
1509                                 line.DrawEnding (g, line_y);
1510                                 line_no++;
1511                         }
1512                 }
1513
1514                 private int GetLineEnding (string line, int start, out LineEnding ending)
1515                 {
1516                         int res;
1517                         int rich_index;
1518
1519                         if (start >= line.Length) {
1520                                 ending = LineEnding.Wrap;
1521                                 return -1;
1522                         }
1523                         
1524                         res = line.IndexOf ('\r', start);
1525                         rich_index = line.IndexOf ('\n', start);
1526                         
1527                         // Handle the case where we find both of them, and the \n is before the \r
1528                         if (res != -1 && rich_index != -1)
1529                                 if (rich_index < res) {
1530                                         ending = LineEnding.Rich;
1531                                         return rich_index;                              
1532                                 }
1533                         
1534                         if (res != -1) {
1535                                 if (res + 2 < line.Length && line [res + 1] == '\r' && line [res + 2] == '\n') {
1536                                         ending = LineEnding.Soft;
1537                                         return res;
1538                                 }
1539                                 if (res + 1 < line.Length && line [res + 1] == '\n') {
1540                                         ending = LineEnding.Hard;
1541                                         return res;
1542                                 }
1543                                 ending = LineEnding.Limp;
1544                                 return res;
1545                         }
1546
1547                         if (rich_index != -1) {
1548                                 ending = LineEnding.Rich;
1549                                 return rich_index;
1550                         }
1551
1552                         ending = LineEnding.Wrap;
1553                         return line.Length;
1554                 }
1555
1556                 // Get the line ending, but only of the types specified
1557                 private int GetLineEnding (string line, int start, out LineEnding ending, LineEnding type)
1558                 {
1559                         int index = start;
1560                         int last_length = 0;
1561
1562                         do {
1563                                 index = GetLineEnding (line, index + last_length, out ending);
1564                                 last_length = LineEndingLength (ending);
1565                         } while 
1566                                 ((ending & type) != ending && index != -1);
1567                         
1568                         return index == -1 ? line.Length : index;
1569                 }
1570                 
1571                 internal int LineEndingLength (LineEnding ending)
1572                 {
1573                         switch (ending) {
1574                                 case LineEnding.Limp:
1575                                 case LineEnding.Rich:
1576                                         return 1;
1577                                 case LineEnding.Hard:
1578                                         return 2;
1579                                 case LineEnding.Soft:
1580                                         return 3;
1581                         }
1582
1583                         return 0;
1584                 }
1585
1586                 internal string LineEndingToString (LineEnding ending)
1587                 {
1588                         switch (ending) {
1589                                 case LineEnding.Limp:
1590                                         return "\r";
1591                                 case LineEnding.Hard:
1592                                         return "\r\n";
1593                                 case LineEnding.Soft:
1594                                         return "\r\r\n";
1595                                 case LineEnding.Rich:
1596                                         return "\n";
1597                         }
1598                         
1599                         return string.Empty;
1600                 }
1601
1602                 // Insert text at the given position; use formatting at insertion point for inserted text
1603                 internal void Insert (Line line, int pos, bool update_caret, string s)
1604                 {
1605                         int break_index;
1606                         int base_line;
1607                         int old_line_count;
1608                         int count = 1;
1609                         LineEnding ending;
1610                         Line split_line;
1611                         
1612                         // Find the LineTag to add to
1613                         LineTag tag = line.FindTag (pos);
1614                         
1615                         // Don't recalculate while we mess around
1616                         SuspendRecalc ();
1617                         
1618                         base_line = line.line_no;
1619                         old_line_count = lines;
1620
1621                         break_index = GetLineEnding (s, 0, out ending, LineEnding.Hard | LineEnding.Rich);
1622
1623                         // There are no line feeds in our text to be pasted
1624                         if (break_index == s.Length) {
1625                                 line.InsertString (pos, s);
1626                         } else {
1627                                 // Add up to the first line feed to our current position
1628                                 line.InsertString (pos, s.Substring (0, break_index + LineEndingLength (ending)));
1629                                 
1630                                 // Split the rest of the original line to a new line
1631                                 Split (line, pos + (break_index + LineEndingLength (ending)));
1632                                 line.ending = ending;
1633                                 break_index += LineEndingLength (ending);
1634                                 split_line = GetLine (line.line_no + 1);
1635                                 
1636                                 // Insert brand new lines for any more line feeds in the inserted string
1637                                 while (true) {
1638                                         int next_break = GetLineEnding (s, break_index, out ending, LineEnding.Hard | LineEnding.Rich);
1639                                         
1640                                         if (next_break == s.Length)
1641                                                 break;
1642                                                 
1643                                         string line_text = s.Substring (break_index, next_break - break_index +
1644                                                         LineEndingLength (ending));
1645
1646                                         Add (base_line + count, line_text, line.alignment, tag.Font, tag.Color, ending);
1647
1648                                         Line last = GetLine (base_line + count);
1649                                         last.ending = ending;
1650
1651                                         count++;
1652                                         break_index = next_break + LineEndingLength (ending);
1653                                 }
1654
1655                                 // Add the remainder of the insert text to the split
1656                                 // part of the original line
1657                                 split_line.InsertString (0, s.Substring (break_index));
1658                         }
1659                         
1660                         // Allow the document to recalculate things
1661                         ResumeRecalc (false);
1662
1663                         UpdateView (line, lines - old_line_count + 1, pos);
1664
1665                         // Move the caret to the end of the inserted text if requested
1666                         if (update_caret) {
1667                                 Line l = GetLine (line.line_no + lines - old_line_count);
1668                                 PositionCaret (l, l.text.Length);
1669                                 DisplayCaret ();
1670                         }
1671                 }
1672
1673                 // Inserts a string at the given position
1674                 internal void InsertString (Line line, int pos, string s)
1675                 {
1676                         // Update our character count
1677                         CharCount += s.Length;
1678
1679                         // Insert the text into the Line
1680                         line.InsertString (pos, s);
1681                 }
1682
1683                 // Inserts a character at the current caret position
1684                 internal void InsertCharAtCaret (char ch, bool move_caret)
1685                 {
1686                         caret.line.InsertString (caret.pos, ch.ToString ());
1687
1688                         undo.RecordTyping (caret.line, caret.pos, ch);
1689
1690                         UpdateView (caret.line, caret.pos);
1691                         
1692                         if (move_caret) {
1693                                 caret.pos++;
1694                                 UpdateCaret ();
1695                                 SetSelectionToCaret (true);
1696                         }
1697                 }
1698                 
1699                 internal void InsertPicture (Line line, int pos, RTF.Picture picture)
1700                 {
1701                         //LineTag next_tag;
1702                         LineTag tag;
1703                         int len;
1704
1705                         len = 1;
1706
1707                         // Just a place holder basically
1708                         line.text.Insert (pos, "I");
1709
1710                         PictureTag picture_tag = new PictureTag (line, pos + 1, picture);
1711
1712                         tag = LineTag.FindTag (line, pos);
1713                         picture_tag.CopyFormattingFrom (tag);
1714                         /*next_tag = */tag.Break (pos + 1);
1715                         picture_tag.Previous = tag;
1716                         picture_tag.Next = tag.Next;
1717                         tag.Next = picture_tag;
1718
1719                         //
1720                         // Picture tags need to be surrounded by text tags
1721                         //
1722                         if (picture_tag.Next == null) {
1723                                 picture_tag.Next = new LineTag (line, pos + 1);
1724                                 picture_tag.Next.CopyFormattingFrom (tag);
1725                                 picture_tag.Next.Previous = picture_tag;
1726                         }
1727
1728                         tag = picture_tag.Next;
1729                         while (tag != null) {
1730                                 tag.Start += len;
1731                                 tag = tag.Next;
1732                         }
1733
1734                         line.Grow (len);
1735                         line.recalc = true;
1736
1737                         UpdateView (line, pos);
1738                 }
1739
1740                 internal void DeleteMultiline (Line start_line, int pos, int length)
1741                 {
1742                         Marker start = new Marker ();
1743                         Marker end = new Marker ();
1744                         int start_index = LineTagToCharIndex (start_line, pos);
1745
1746                         start.line = start_line;
1747                         start.pos = pos;
1748                         start.tag = LineTag.FindTag (start_line, pos);
1749
1750                         CharIndexToLineTag (start_index + length, out end.line,
1751                                         out end.tag, out end.pos);
1752
1753                         SuspendUpdate ();
1754
1755                         if (start.line == end.line) {
1756                                 DeleteChars (start.line, pos, end.pos - pos);
1757                         } else {
1758
1759                                 // Delete first and last lines
1760                                 DeleteChars (start.line, start.pos, start.line.text.Length - start.pos);
1761                                 DeleteChars (end.line, 0, end.pos);
1762
1763                                 int current = start.line.line_no + 1;
1764                                 if (current < end.line.line_no) {
1765                                         for (int i = end.line.line_no - 1; i >= current; i--) {
1766                                                 Delete (i);
1767                                         }
1768                                 }
1769
1770                                 // BIG FAT WARNING - selection_end.line might be stale due 
1771                                 // to the above Delete() call. DONT USE IT before hitting the end of this method!
1772
1773                                 // Join start and end
1774                                 Combine (start.line.line_no, current);
1775                         }
1776
1777                         ResumeUpdate (true);
1778                 }
1779
1780                 
1781                 // Deletes n characters at the given position; it will not delete past line limits
1782                 // pos is 0-based
1783                 public void DeleteChars (Line line, int pos, int count)
1784                 {
1785                         // Reduce our character count
1786                         CharCount -= count;
1787                         
1788                         line.DeleteCharacters (pos, count);
1789
1790                         if (pos >= line.TextLengthWithoutEnding ()) {
1791                                 LineEnding ending = line.ending;
1792                                 GetLineEnding (line.text.ToString (), 0, out ending);
1793                                 
1794                                 if (ending != line.ending) {
1795                                         line.ending = ending;
1796
1797                                         if (!multiline) {
1798                                                 UpdateView (line, lines, pos);
1799                                                 owner.Invalidate ();
1800                                                 return;
1801                                         }
1802                                 }
1803                         }
1804                         if (!multiline) {
1805                                 UpdateView (line, lines, pos);
1806                                 owner.Invalidate ();
1807                         } else 
1808                                 UpdateView (line, pos);
1809                 }
1810
1811                 // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
1812                 public void DeleteChar (Line line, int pos, bool forward)
1813                 {
1814                         if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true))
1815                                 return;
1816                         
1817                         if (forward)
1818                                 DeleteChars (line, pos, 1);
1819                         else
1820                                 DeleteChars (line, pos - 1, 1);
1821                 }
1822
1823                 // Combine two lines
1824                 internal void Combine(int FirstLine, int SecondLine) {
1825                         Combine(GetLine(FirstLine), GetLine(SecondLine));
1826                 }
1827
1828                 internal void Combine(Line first, Line second) {
1829                         LineTag last;
1830                         int     shift;
1831
1832                         // strip the ending off of the first lines text
1833                         first.text.Length = first.text.Length - LineEndingLength (first.ending);
1834
1835                         // Combine the two tag chains into one
1836                         last = first.tags;
1837
1838                         // Maintain the line ending style
1839                         first.ending = second.ending;
1840
1841                         while (last.Next != null) {
1842                                 last = last.Next;
1843                         }
1844
1845                         // need to get the shift before setting the next tag since that effects length
1846                         shift = last.Start + last.Length - 1;
1847                         last.Next = second.tags;
1848                         last.Next.Previous = last;
1849
1850                         // Fix up references within the chain
1851                         last = last.Next;
1852                         while (last != null) {
1853                                 last.Line = first;
1854                                 last.Start += shift;
1855                                 last = last.Next;
1856                         }
1857
1858                         // Combine both lines' strings
1859                         first.text.Insert(first.text.Length, second.text.ToString());
1860                         first.Grow(first.text.Length);
1861
1862                         // Remove the reference to our (now combined) tags from the doomed line
1863                         second.tags = null;
1864
1865                         // Renumber lines
1866                         DecrementLines(first.line_no + 2);      // first.line_no + 1 will be deleted, so we need to start renumbering one later
1867
1868                         // Mop up
1869                         first.recalc = true;
1870                         first.height = 0;       // This forces RecalcDocument/UpdateView to redraw from this line on
1871                         first.Streamline(lines);
1872
1873                         // Update Caret, Selection, etc
1874                         if (caret.line == second) {
1875                                 caret.Combine(first, shift);
1876                         }
1877                         if (selection_anchor.line == second) {
1878                                 selection_anchor.Combine(first, shift);
1879                         }
1880                         if (selection_start.line == second) {
1881                                 selection_start.Combine(first, shift);
1882                         }
1883                         if (selection_end.line == second) {
1884                                 selection_end.Combine(first, shift);
1885                         }
1886
1887                         #if Debug
1888                                 Line    check_first;
1889                                 Line    check_second;
1890
1891                                 check_first = GetLine(first.line_no);
1892                                 check_second = GetLine(check_first.line_no + 1);
1893
1894                                 Console.WriteLine("Pre-delete: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1895                         #endif
1896
1897                         this.Delete(second);
1898
1899                         #if Debug
1900                                 check_first = GetLine(first.line_no);
1901                                 check_second = GetLine(check_first.line_no + 1);
1902
1903                                 Console.WriteLine("Post-delete Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1904                         #endif
1905                 }
1906
1907                 // Split the line at the position into two
1908                 internal void Split(int LineNo, int pos) {
1909                         Line    line;
1910                         LineTag tag;
1911
1912                         line = GetLine(LineNo);
1913                         tag = LineTag.FindTag(line, pos);
1914                         Split(line, tag, pos);
1915                 }
1916
1917                 internal void Split(Line line, int pos) {
1918                         LineTag tag;
1919
1920                         tag = LineTag.FindTag(line, pos);
1921                         Split(line, tag, pos);
1922                 }
1923
1924                 ///<summary>Split line at given tag and position into two lines</summary>
1925                 ///if more space becomes available on previous line</param>
1926                 internal void Split(Line line, LineTag tag, int pos) {
1927                         LineTag new_tag;
1928                         Line    new_line;
1929                         bool    move_caret;
1930                         bool    move_sel_start;
1931                         bool    move_sel_end;
1932
1933                         move_caret = false;
1934                         move_sel_start = false;
1935                         move_sel_end = false;
1936
1937                         // Adjust selection and cursors
1938                         if (caret.line == line && caret.pos >= pos) {
1939                                 move_caret = true;
1940                         }
1941                         if (selection_start.line == line && selection_start.pos > pos) {
1942                                 move_sel_start = true;
1943                         }
1944
1945                         if (selection_end.line == line && selection_end.pos > pos) {
1946                                 move_sel_end = true;
1947                         }
1948
1949                         // cover the easy case first
1950                         if (pos == line.text.Length) {
1951                                 Add (line.line_no + 1, String.Empty, line.alignment, tag.Font, tag.Color, line.ending);
1952
1953                                 new_line = GetLine (line.line_no + 1);
1954                                 
1955                                 if (move_caret) {
1956                                         caret.line = new_line;
1957                                         caret.tag = new_line.tags;
1958                                         caret.pos = 0;
1959                                 }
1960
1961                                 if (move_sel_start) {
1962                                         selection_start.line = new_line;
1963                                         selection_start.pos = 0;
1964                                         selection_start.tag = new_line.tags;
1965                                 }
1966
1967                                 if (move_sel_end) {
1968                                         selection_end.line = new_line;
1969                                         selection_end.pos = 0;
1970                                         selection_end.tag = new_line.tags;
1971                                 }
1972                                 return;
1973                         }
1974
1975                         // We need to move the rest of the text into the new line
1976                         Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.Font, tag.Color, line.ending);
1977
1978                         // Now transfer our tags from this line to the next
1979                         new_line = GetLine(line.line_no + 1);
1980
1981                         line.recalc = true;
1982                         new_line.recalc = true;
1983
1984                         if ((tag.Start - 1) == pos) {
1985                                 int     shift;
1986
1987                                 // We can simply break the chain and move the tag into the next line
1988                                 if (tag == line.tags) {
1989                                         new_tag = new LineTag(line, 1);
1990                                         new_tag.CopyFormattingFrom (tag);
1991                                         line.tags = new_tag;
1992                                 }
1993
1994                                 if (tag.Previous != null) {
1995                                         tag.Previous.Next = null;
1996                                 }
1997                                 new_line.tags = tag;
1998                                 tag.Previous = null;
1999                                 tag.Line = new_line;
2000
2001                                 // Walk the list and correct the start location of the tags we just bumped into the next line
2002                                 shift = tag.Start - 1;
2003
2004                                 new_tag = tag;
2005                                 while (new_tag != null) {
2006                                         new_tag.Start -= shift;
2007                                         new_tag.Line = new_line;
2008                                         new_tag = new_tag.Next;
2009                                 }
2010                         } else {
2011                                 int     shift;
2012
2013                                 new_tag = new LineTag (new_line, 1);                    
2014                                 new_tag.Next = tag.Next;
2015                                 new_tag.CopyFormattingFrom (tag);
2016                                 new_line.tags = new_tag;
2017                                 if (new_tag.Next != null) {
2018                                         new_tag.Next.Previous = new_tag;
2019                                 }
2020                                 tag.Next = null;
2021
2022                                 shift = pos;
2023                                 new_tag = new_tag.Next;
2024                                 while (new_tag != null) {
2025                                         new_tag.Start -= shift;
2026                                         new_tag.Line = new_line;
2027                                         new_tag = new_tag.Next;
2028
2029                                 }
2030                         }
2031
2032                         if (move_caret) {
2033                                 caret.line = new_line;
2034                                 caret.pos = caret.pos - pos;
2035                                 caret.tag = caret.line.FindTag(caret.pos);
2036                         }
2037
2038                         if (move_sel_start) {
2039                                 selection_start.line = new_line;
2040                                 selection_start.pos = selection_start.pos - pos;
2041                                 selection_start.tag = new_line.FindTag(selection_start.pos);
2042                         }
2043
2044                         if (move_sel_end) {
2045                                 selection_end.line = new_line;
2046                                 selection_end.pos = selection_end.pos - pos;
2047                                 selection_end.tag = new_line.FindTag(selection_end.pos);
2048                         }
2049
2050                         CharCount -= line.text.Length - pos;
2051                         line.text.Remove(pos, line.text.Length - pos);
2052                 }
2053
2054                 // Adds a line of text, with given font.
2055                 // Bumps any line at that line number that already exists down
2056                 internal void Add (int LineNo, string Text, Font font, Color color, LineEnding ending)
2057                 {
2058                         Add (LineNo, Text, alignment, font, color, ending);
2059                 }
2060
2061                 internal void Add (int LineNo, string Text, HorizontalAlignment align, Font font, Color color, LineEnding ending)
2062                 {
2063                         Line    add;
2064                         Line    line;
2065                         int     line_no;
2066
2067                         CharCount += Text.Length;
2068
2069                         if (LineNo<1 || Text == null) {
2070                                 if (LineNo<1) {
2071                                         throw new ArgumentNullException("LineNo", "Line numbers must be positive");
2072                                 } else {
2073                                         throw new ArgumentNullException("Text", "Cannot insert NULL line");
2074                                 }
2075                         }
2076
2077                         add = new Line (this, LineNo, Text, align, font, color, ending);
2078
2079                         line = document;
2080                         while (line != sentinel) {
2081                                 add.parent = line;
2082                                 line_no = line.line_no;
2083
2084                                 if (LineNo > line_no) {
2085                                         line = line.right;
2086                                 } else if (LineNo < line_no) {
2087                                         line = line.left;
2088                                 } else {
2089                                         // Bump existing line numbers; walk all nodes to the right of this one and increment line_no
2090                                         IncrementLines(line.line_no);
2091                                         line = line.left;
2092                                 }
2093                         }
2094
2095                         add.left = sentinel;
2096                         add.right = sentinel;
2097
2098                         if (add.parent != null) {
2099                                 if (LineNo > add.parent.line_no) {
2100                                         add.parent.right = add;
2101                                 } else {
2102                                         add.parent.left = add;
2103                                 }
2104                         } else {
2105                                 // Root node
2106                                 document = add;
2107                         }
2108
2109                         RebalanceAfterAdd(add);
2110
2111                         lines++;
2112                 }
2113
2114                 internal virtual void Clear() {
2115                         lines = 0;
2116                         CharCount = 0;
2117                         document = sentinel;
2118                 }
2119
2120                 public virtual object Clone() {
2121                         Document clone;
2122
2123                         clone = new Document(null);
2124
2125                         clone.lines = this.lines;
2126                         clone.document = (Line)document.Clone();
2127
2128                         return clone;
2129                 }
2130
2131                 private void Delete (int LineNo)
2132                 {
2133                         Line    line;
2134
2135                         if (LineNo > lines)
2136                                 return;
2137
2138                         line = GetLine (LineNo);
2139
2140                         CharCount -= line.text.Length;
2141
2142                         DecrementLines (LineNo + 1);
2143                         Delete (line);
2144                 }
2145
2146                 private void Delete(Line line1) {
2147                         Line    line2;// = new Line();
2148                         Line    line3;
2149
2150                         if ((line1.left == sentinel) || (line1.right == sentinel)) {
2151                                 line3 = line1;
2152                         } else {
2153                                 line3 = line1.right;
2154                                 while (line3.left != sentinel) {
2155                                         line3 = line3.left;
2156                                 }
2157                         }
2158
2159                         if (line3.left != sentinel) {
2160                                 line2 = line3.left;
2161                         } else {
2162                                 line2 = line3.right;
2163                         }
2164
2165                         line2.parent = line3.parent;
2166                         if (line3.parent != null) {
2167                                 if(line3 == line3.parent.left) {
2168                                         line3.parent.left = line2;
2169                                 } else {
2170                                         line3.parent.right = line2;
2171                                 }
2172                         } else {
2173                                 document = line2;
2174                         }
2175
2176                         if (line3 != line1) {
2177                                 LineTag tag;
2178
2179                                 if (selection_start.line == line3) {
2180                                         selection_start.line = line1;
2181                                 }
2182
2183                                 if (selection_end.line == line3) {
2184                                         selection_end.line = line1;
2185                                 }
2186
2187                                 if (selection_anchor.line == line3) {
2188                                         selection_anchor.line = line1;
2189                                 }
2190
2191                                 if (caret.line == line3) {
2192                                         caret.line = line1;
2193                                 }
2194
2195
2196                                 line1.alignment = line3.alignment;
2197                                 line1.ascent = line3.ascent;
2198                                 line1.hanging_indent = line3.hanging_indent;
2199                                 line1.height = line3.height;
2200                                 line1.indent = line3.indent;
2201                                 line1.line_no = line3.line_no;
2202                                 line1.recalc = line3.recalc;
2203                                 line1.right_indent = line3.right_indent;
2204                                 line1.ending = line3.ending;
2205                                 line1.space = line3.space;
2206                                 line1.tags = line3.tags;
2207                                 line1.text = line3.text;
2208                                 line1.widths = line3.widths;
2209                                 line1.offset = line3.offset;
2210
2211                                 tag = line1.tags;
2212                                 while (tag != null) {
2213                                         tag.Line = line1;
2214                                         tag = tag.Next;
2215                                 }
2216                         }
2217
2218                         if (line3.color == LineColor.Black)
2219                                 RebalanceAfterDelete(line2);
2220
2221                         this.lines--;
2222                 }
2223
2224                 // Invalidate a section of the document to trigger redraw
2225                 internal void Invalidate(Line start, int start_pos, Line end, int end_pos) {
2226                         Line    l1;
2227                         Line    l2;
2228                         int     p1;
2229                         int     p2;
2230
2231                         if ((start == end) && (start_pos == end_pos)) {
2232                                 return;
2233                         }
2234
2235                         if (end_pos == -1) {
2236                                 end_pos = end.text.Length;
2237                         }
2238         
2239                         // figure out what's before what so the logic below is straightforward
2240                         if (start.line_no < end.line_no) {
2241                                 l1 = start;
2242                                 p1 = start_pos;
2243
2244                                 l2 = end;
2245                                 p2 = end_pos;
2246                         } else if (start.line_no > end.line_no) {
2247                                 l1 = end;
2248                                 p1 = end_pos;
2249
2250                                 l2 = start;
2251                                 p2 = start_pos;
2252                         } else {
2253                                 if (start_pos < end_pos) {
2254                                         l1 = start;
2255                                         p1 = start_pos;
2256
2257                                         l2 = end;
2258                                         p2 = end_pos;
2259                                 } else {
2260                                         l1 = end;
2261                                         p1 = end_pos;
2262
2263                                         l2 = start;
2264                                         p2 = start_pos;
2265                                 }
2266
2267                                 int endpoint = (int) l1.widths [p2];
2268                                 if (p2 == l1.text.Length + 1) {
2269                                         endpoint = (int) viewport_width;
2270                                 }
2271
2272                                 #if Debug
2273                                         Console.WriteLine("Invaliding backwards from {0}:{1} to {2}:{3}   {4}",
2274                                                         l1.line_no, p1, l2.line_no, p2,
2275                                                         new Rectangle(
2276                                                                 (int)l1.widths[p1] + l1.X - viewport_x, 
2277                                                                 l1.Y - viewport_y, 
2278                                                                 (int)l1.widths[p2], 
2279                                                                 l1.height
2280                                                                 )
2281                                                 );
2282                                 #endif
2283
2284                                 owner.Invalidate(new Rectangle (
2285                                         (int)l1.widths[p1] + l1.X - viewport_x, 
2286                                         l1.Y - viewport_y, 
2287                                         endpoint - (int)l1.widths[p1] + 1, 
2288                                         l1.height));
2289                                 return;
2290                         }
2291
2292                         #if Debug
2293                                 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);
2294                                 Console.WriteLine ("invalidate start line:  {0}  position:  {1}", l1.text, p1);
2295                         #endif
2296
2297                         // Three invalidates:
2298                         // First line from start
2299                         owner.Invalidate(new Rectangle((int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
2300
2301                         
2302                         // lines inbetween
2303                         if ((l1.line_no + 1) < l2.line_no) {
2304                                 int     y;
2305
2306                                 y = GetLine(l1.line_no + 1).Y;
2307                                 owner.Invalidate(new Rectangle(0, y - viewport_y, viewport_width, l2.Y - y));
2308
2309                                 #if Debug
2310                                         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);
2311                                 #endif
2312                         }
2313                         
2314
2315                         // Last line to end
2316                         owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height));
2317                         #if Debug
2318                                 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);
2319
2320                         #endif
2321                 }
2322
2323                 /// <summary>Select text around caret</summary>
2324                 internal void ExpandSelection(CaretSelection mode, bool to_caret) {
2325                         if (to_caret) {
2326                                 // We're expanding the selection to the caret position
2327                                 switch(mode) {
2328                                         case CaretSelection.Line: {
2329                                                 // Invalidate the selection delta
2330                                                 if (caret > selection_prev) {
2331                                                         Invalidate(selection_prev.line, 0, caret.line, caret.line.text.Length);
2332                                                 } else {
2333                                                         Invalidate(selection_prev.line, selection_prev.line.text.Length, caret.line, 0);
2334                                                 }
2335
2336                                                 if (caret.line.line_no <= selection_anchor.line.line_no) {
2337                                                         selection_start.line = caret.line;
2338                                                         selection_start.tag = caret.line.tags;
2339                                                         selection_start.pos = 0;
2340
2341                                                         selection_end.line = selection_anchor.line;
2342                                                         selection_end.tag = selection_anchor.tag;
2343                                                         selection_end.pos = selection_anchor.pos;
2344
2345                                                         selection_end_anchor = true;
2346                                                 } else {
2347                                                         selection_start.line = selection_anchor.line;
2348                                                         selection_start.pos = selection_anchor.height;
2349                                                         selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
2350
2351                                                         selection_end.line = caret.line;
2352                                                         selection_end.tag = caret.line.tags;
2353                                                         selection_end.pos = caret.line.text.Length;
2354
2355                                                         selection_end_anchor = false;
2356                                                 }
2357                                                 selection_prev.line = caret.line;
2358                                                 selection_prev.tag = caret.tag;
2359                                                 selection_prev.pos = caret.pos;
2360
2361                                                 break;
2362                                         }
2363
2364                                         case CaretSelection.Word: {
2365                                                 int     start_pos;
2366                                                 int     end_pos;
2367
2368                                                 start_pos = FindWordSeparator(caret.line, caret.pos, false);
2369                                                 end_pos = FindWordSeparator(caret.line, caret.pos, true);
2370
2371                                                 
2372                                                 // Invalidate the selection delta
2373                                                 if (caret > selection_prev) {
2374                                                         Invalidate(selection_prev.line, selection_prev.pos, caret.line, end_pos);
2375                                                 } else {
2376                                                         Invalidate(selection_prev.line, selection_prev.pos, caret.line, start_pos);
2377                                                 }
2378                                                 if (caret < selection_anchor) {
2379                                                         selection_start.line = caret.line;
2380                                                         selection_start.tag = caret.line.FindTag(start_pos);
2381                                                         selection_start.pos = start_pos;
2382
2383                                                         selection_end.line = selection_anchor.line;
2384                                                         selection_end.tag = selection_anchor.tag;
2385                                                         selection_end.pos = selection_anchor.pos;
2386
2387                                                         selection_prev.line = caret.line;
2388                                                         selection_prev.tag = caret.tag;
2389                                                         selection_prev.pos = start_pos;
2390
2391                                                         selection_end_anchor = true;
2392                                                 } else {
2393                                                         selection_start.line = selection_anchor.line;
2394                                                         selection_start.pos = selection_anchor.height;
2395                                                         selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
2396
2397                                                         selection_end.line = caret.line;
2398                                                         selection_end.tag = caret.line.FindTag(end_pos);
2399                                                         selection_end.pos = end_pos;
2400
2401                                                         selection_prev.line = caret.line;
2402                                                         selection_prev.tag = caret.tag;
2403                                                         selection_prev.pos = end_pos;
2404
2405                                                         selection_end_anchor = false;
2406                                                 }
2407                                                 break;
2408                                         }
2409
2410                                         case CaretSelection.Position: {
2411                                                 SetSelectionToCaret(false);
2412                                                 return;
2413                                         }
2414                                 }
2415                         } else {
2416                                 // We're setting the selection 'around' the caret position
2417                                 switch(mode) {
2418                                         case CaretSelection.Line: {
2419                                                 this.Invalidate(caret.line, 0, caret.line, caret.line.text.Length);
2420
2421                                                 selection_start.line = caret.line;
2422                                                 selection_start.tag = caret.line.tags;
2423                                                 selection_start.pos = 0;
2424
2425                                                 selection_end.line = caret.line;
2426                                                 selection_end.pos = caret.line.text.Length;
2427                                                 selection_end.tag = caret.line.FindTag(selection_end.pos);
2428
2429                                                 selection_anchor.line = selection_end.line;
2430                                                 selection_anchor.tag = selection_end.tag;
2431                                                 selection_anchor.pos = selection_end.pos;
2432                                                 selection_anchor.height = 0;
2433
2434                                                 selection_prev.line = caret.line;
2435                                                 selection_prev.tag = caret.tag;
2436                                                 selection_prev.pos = caret.pos;
2437
2438                                                 this.selection_end_anchor = true;
2439
2440                                                 break;
2441                                         }
2442
2443                                         case CaretSelection.Word: {
2444                                                 int     start_pos;
2445                                                 int     end_pos;
2446
2447                                                 start_pos = FindWordSeparator(caret.line, caret.pos, false);
2448                                                 end_pos = FindWordSeparator(caret.line, caret.pos, true);
2449
2450                                                 this.Invalidate(selection_start.line, start_pos, caret.line, end_pos);
2451
2452                                                 selection_start.line = caret.line;
2453                                                 selection_start.tag = caret.line.FindTag(start_pos);
2454                                                 selection_start.pos = start_pos;
2455
2456                                                 selection_end.line = caret.line;
2457                                                 selection_end.tag = caret.line.FindTag(end_pos);
2458                                                 selection_end.pos = end_pos;
2459
2460                                                 selection_anchor.line = selection_end.line;
2461                                                 selection_anchor.tag = selection_end.tag;
2462                                                 selection_anchor.pos = selection_end.pos;
2463                                                 selection_anchor.height = start_pos;
2464
2465                                                 selection_prev.line = caret.line;
2466                                                 selection_prev.tag = caret.tag;
2467                                                 selection_prev.pos = caret.pos;
2468
2469                                                 this.selection_end_anchor = true;
2470
2471                                                 break;
2472                                         }
2473                                 }
2474                         }
2475
2476                         SetSelectionVisible (!(selection_start == selection_end));
2477                 }
2478
2479                 internal void SetSelectionToCaret(bool start) {
2480                         if (start) {
2481                                 // Invalidate old selection; selection is being reset to empty
2482                                 this.Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2483
2484                                 selection_start.line = caret.line;
2485                                 selection_start.tag = caret.tag;
2486                                 selection_start.pos = caret.pos;
2487
2488                                 // start always also selects end
2489                                 selection_end.line = caret.line;
2490                                 selection_end.tag = caret.tag;
2491                                 selection_end.pos = caret.pos;
2492
2493                                 selection_anchor.line = caret.line;
2494                                 selection_anchor.tag = caret.tag;
2495                                 selection_anchor.pos = caret.pos;
2496                         } else {
2497                                 // Invalidate from previous end to caret (aka new end)
2498                                 if (selection_end_anchor) {
2499                                         if (selection_start != caret) {
2500                                                 this.Invalidate(selection_start.line, selection_start.pos, caret.line, caret.pos);
2501                                         }
2502                                 } else {
2503                                         if (selection_end != caret) {
2504                                                 this.Invalidate(selection_end.line, selection_end.pos, caret.line, caret.pos);
2505                                         }
2506                                 }
2507
2508                                 if (caret < selection_anchor) {
2509                                         selection_start.line = caret.line;
2510                                         selection_start.tag = caret.tag;
2511                                         selection_start.pos = caret.pos;
2512
2513                                         selection_end.line = selection_anchor.line;
2514                                         selection_end.tag = selection_anchor.tag;
2515                                         selection_end.pos = selection_anchor.pos;
2516
2517                                         selection_end_anchor = true;
2518                                 } else {
2519                                         selection_start.line = selection_anchor.line;
2520                                         selection_start.tag = selection_anchor.tag;
2521                                         selection_start.pos = selection_anchor.pos;
2522
2523                                         selection_end.line = caret.line;
2524                                         selection_end.tag = caret.tag;
2525                                         selection_end.pos = caret.pos;
2526
2527                                         selection_end_anchor = false;
2528                                 }
2529                         }
2530
2531                         SetSelectionVisible (!(selection_start == selection_end));
2532                 }
2533
2534                 internal void SetSelection(Line start, int start_pos, Line end, int end_pos) {
2535                         if (selection_visible) {
2536                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2537                         }
2538
2539                         if ((end.line_no < start.line_no) || ((end == start) && (end_pos <= start_pos))) {
2540                                 selection_start.line = end;
2541                                 selection_start.tag = LineTag.FindTag(end, end_pos);
2542                                 selection_start.pos = end_pos;
2543
2544                                 selection_end.line = start;
2545                                 selection_end.tag = LineTag.FindTag(start, start_pos);
2546                                 selection_end.pos = start_pos;
2547
2548                                 selection_end_anchor = true;
2549                         } else {
2550                                 selection_start.line = start;
2551                                 selection_start.tag = LineTag.FindTag(start, start_pos);
2552                                 selection_start.pos = start_pos;
2553
2554                                 selection_end.line = end;
2555                                 selection_end.tag = LineTag.FindTag(end, end_pos);
2556                                 selection_end.pos = end_pos;
2557
2558                                 selection_end_anchor = false;
2559                         }
2560
2561                         selection_anchor.line = start;
2562                         selection_anchor.tag = selection_start.tag;
2563                         selection_anchor.pos = start_pos;
2564
2565                         if (((start == end) && (start_pos == end_pos)) || start == null || end == null) {
2566                                 SetSelectionVisible (false);
2567                         } else {
2568                                 SetSelectionVisible (true);
2569                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2570                         }
2571                 }
2572
2573                 internal void SetSelectionStart(Line start, int start_pos, bool invalidate) {
2574                         // Invalidate from the previous to the new start pos
2575                         if (invalidate)
2576                                 Invalidate(selection_start.line, selection_start.pos, start, start_pos);
2577
2578                         selection_start.line = start;
2579                         selection_start.pos = start_pos;
2580                         selection_start.tag = LineTag.FindTag(start, start_pos);
2581
2582                         selection_anchor.line = start;
2583                         selection_anchor.pos = start_pos;
2584                         selection_anchor.tag = selection_start.tag;
2585
2586                         selection_end_anchor = false;
2587
2588                         
2589                         if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
2590                                 SetSelectionVisible (true);
2591                         } else {
2592                                 SetSelectionVisible (false);
2593                         }
2594
2595                         if (invalidate)
2596                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2597                 }
2598
2599                 internal void SetSelectionStart(int character_index, bool invalidate) {
2600                         Line    line;
2601                         LineTag tag;
2602                         int     pos;
2603
2604                         if (character_index < 0) {
2605                                 return;
2606                         }
2607
2608                         CharIndexToLineTag(character_index, out line, out tag, out pos);
2609                         SetSelectionStart(line, pos, invalidate);
2610                 }
2611
2612                 internal void SetSelectionEnd(Line end, int end_pos, bool invalidate) {
2613
2614                         if (end == selection_end.line && end_pos == selection_start.pos) {
2615                                 selection_anchor.line = selection_start.line;
2616                                 selection_anchor.tag = selection_start.tag;
2617                                 selection_anchor.pos = selection_start.pos;
2618
2619                                 selection_end.line = selection_start.line;
2620                                 selection_end.tag = selection_start.tag;
2621                                 selection_end.pos = selection_start.pos;
2622
2623                                 selection_end_anchor = false;
2624                         } else if ((end.line_no < selection_anchor.line.line_no) || ((end == selection_anchor.line) && (end_pos <= selection_anchor.pos))) {
2625                                 selection_start.line = end;
2626                                 selection_start.tag = LineTag.FindTag(end, end_pos);
2627                                 selection_start.pos = end_pos;
2628
2629                                 selection_end.line = selection_anchor.line;
2630                                 selection_end.tag = selection_anchor.tag;
2631                                 selection_end.pos = selection_anchor.pos;
2632
2633                                 selection_end_anchor = true;
2634                         } else {
2635                                 selection_start.line = selection_anchor.line;
2636                                 selection_start.tag = selection_anchor.tag;
2637                                 selection_start.pos = selection_anchor.pos;
2638
2639                                 selection_end.line = end;
2640                                 selection_end.tag = LineTag.FindTag(end, end_pos);
2641                                 selection_end.pos = end_pos;
2642
2643                                 selection_end_anchor = false;
2644                         }
2645
2646                         if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
2647                                 SetSelectionVisible (true);
2648                                 if (invalidate)
2649                                         Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2650                         } else {
2651                                 SetSelectionVisible (false);
2652                                 // ?? Do I need to invalidate here, tests seem to work without it, but I don't think they should :-s
2653                         }
2654                 }
2655
2656                 internal void SetSelectionEnd(int character_index, bool invalidate) {
2657                         Line    line;
2658                         LineTag tag;
2659                         int     pos;
2660
2661                         if (character_index < 0) {
2662                                 return;
2663                         }
2664
2665                         CharIndexToLineTag(character_index, out line, out tag, out pos);
2666                         SetSelectionEnd(line, pos, invalidate);
2667                 }
2668
2669                 internal void SetSelection(Line start, int start_pos) {
2670                         if (selection_visible) {
2671                                 Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2672                         }
2673
2674                         selection_start.line = start;
2675                         selection_start.pos = start_pos;
2676                         selection_start.tag = LineTag.FindTag(start, start_pos);
2677
2678                         selection_end.line = start;
2679                         selection_end.tag = selection_start.tag;
2680                         selection_end.pos = start_pos;
2681
2682                         selection_anchor.line = start;
2683                         selection_anchor.tag = selection_start.tag;
2684                         selection_anchor.pos = start_pos;
2685
2686                         selection_end_anchor = false;
2687                         SetSelectionVisible (false);
2688                 }
2689
2690                 internal void InvalidateSelectionArea() {
2691                         Invalidate (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2692                 }
2693
2694                 // Return the current selection, as string
2695                 internal string GetSelection() {
2696                         // We return String.Empty if there is no selection
2697                         if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
2698                                 return string.Empty;
2699                         }
2700
2701                         if (selection_start.line == selection_end.line) {
2702                                 return selection_start.line.text.ToString (selection_start.pos, selection_end.pos - selection_start.pos);
2703                         } else {
2704                                 StringBuilder   sb;
2705                                 int             i;
2706                                 int             start;
2707                                 int             end;
2708
2709                                 sb = new StringBuilder();
2710                                 start = selection_start.line.line_no;
2711                                 end = selection_end.line.line_no;
2712
2713                                 sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos) + Environment.NewLine);
2714
2715                                 if ((start + 1) < end) {
2716                                         for (i = start + 1; i < end; i++) {
2717                                                 sb.Append(GetLine(i).text.ToString() + Environment.NewLine);
2718                                         }
2719                                 }
2720
2721                                 sb.Append(selection_end.line.text.ToString(0, selection_end.pos));
2722
2723                                 return sb.ToString();
2724                         }
2725                 }
2726
2727                 internal void ReplaceSelection(string s, bool select_new) {
2728                         int             i;
2729
2730                         int selection_pos_on_line = selection_start.pos;
2731                         int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos);
2732                         SuspendRecalc ();
2733
2734                         // First, delete any selected text
2735                         if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) {
2736                                 if (selection_start.line == selection_end.line) {
2737                                         undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2738
2739                                         DeleteChars (selection_start.line, selection_start.pos, selection_end.pos - selection_start.pos);
2740
2741                                         // The tag might have been removed, we need to recalc it
2742                                         selection_start.tag = selection_start.line.FindTag(selection_start.pos);
2743                                 } else {
2744                                         int             start;
2745                                         int             end;
2746
2747                                         start = selection_start.line.line_no;
2748                                         end = selection_end.line.line_no;
2749
2750                                         undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
2751
2752                                         InvalidateSelectionArea ();
2753
2754                                         // Delete first line
2755                                         DeleteChars (selection_start.line, selection_start.pos, selection_start.line.text.Length - selection_start.pos);
2756                                         selection_start.line.recalc = true;
2757
2758                                         // Delete last line
2759                                         DeleteChars(selection_end.line, 0, selection_end.pos);
2760
2761                                         start++;
2762                                         if (start < end) {
2763                                                 for (i = end - 1; i >= start; i--) {
2764                                                         Delete(i);
2765                                                 }
2766                                         }
2767
2768                                         // BIG FAT WARNING - selection_end.line might be stale due 
2769                                         // to the above Delete() call. DONT USE IT before hitting the end of this method!
2770
2771                                         // Join start and end
2772                                         Combine(selection_start.line.line_no, start);
2773                                 }
2774                         }
2775
2776
2777                         Insert(selection_start.line, selection_start.pos, false, s);
2778                         undo.RecordInsertString (selection_start.line, selection_start.pos, s);
2779                         ResumeRecalc (false);
2780
2781                         Line begin_update_line = selection_start.line;
2782                         int begin_update_pos = selection_start.pos;
2783                         
2784                         if (!select_new) {
2785                                 CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line,
2786                                                 out selection_start.tag, out selection_start.pos);
2787
2788                                 selection_end.line = selection_start.line;
2789                                 selection_end.pos = selection_start.pos;
2790                                 selection_end.tag = selection_start.tag;
2791                                 selection_anchor.line = selection_start.line;
2792                                 selection_anchor.pos = selection_start.pos;
2793                                 selection_anchor.tag = selection_start.tag;
2794
2795                                 SetSelectionVisible (false);
2796                         } else {
2797                                 CharIndexToLineTag(selection_start_pos, out selection_start.line,
2798                                                 out selection_start.tag, out selection_start.pos);
2799
2800                                 CharIndexToLineTag(selection_start_pos + s.Length, out selection_end.line,
2801                                                 out selection_end.tag, out selection_end.pos);
2802
2803                                 selection_anchor.line = selection_start.line;
2804                                 selection_anchor.pos = selection_start.pos;
2805                                 selection_anchor.tag = selection_start.tag;
2806
2807                                 SetSelectionVisible (true);
2808                         }
2809
2810                         PositionCaret (selection_start.line, selection_start.pos);
2811                         UpdateView (begin_update_line, selection_end.line.line_no - begin_update_line.line_no, begin_update_pos);
2812                 }
2813
2814                 internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
2815                         Line    line;
2816                         LineTag tag;
2817                         int     i;
2818                         int     chars;
2819                         int     start;
2820
2821                         chars = 0;
2822
2823                         for (i = 1; i <= lines; i++) {
2824                                 line = GetLine(i);
2825
2826                                 start = chars;
2827                                 chars += line.text.Length;
2828
2829                                 if (index <= chars) {
2830                                         // we found the line
2831                                         tag = line.tags;
2832
2833                                         while (tag != null) {
2834                                                 if (index < (start + tag.Start + tag.Length - 1)) {
2835                                                         line_out = line;
2836                                                         tag_out = LineTag.GetFinalTag (tag);
2837                                                         pos = index - start;
2838                                                         return;
2839                                                 }
2840                                                 if (tag.Next == null) {
2841                                                         Line    next_line;
2842
2843                                                         next_line = GetLine(line.line_no + 1);
2844
2845                                                         if (next_line != null) {
2846                                                                 line_out = next_line;
2847                                                                 tag_out = LineTag.GetFinalTag (next_line.tags);
2848                                                                 pos = 0;
2849                                                                 return;
2850                                                         } else {
2851                                                                 line_out = line;
2852                                                                 tag_out = LineTag.GetFinalTag (tag);
2853                                                                 pos = line_out.text.Length;
2854                                                                 return;
2855                                                         }
2856                                                 }
2857                                                 tag = tag.Next;
2858                                         }
2859                                 }
2860                         }
2861
2862                         line_out = GetLine(lines);
2863                         tag = line_out.tags;
2864                         while (tag.Next != null) {
2865                                 tag = tag.Next;
2866                         }
2867                         tag_out = tag;
2868                         pos = line_out.text.Length;
2869                 }
2870
2871                 internal int LineTagToCharIndex(Line line, int pos) {
2872                         int     i;
2873                         int     length;
2874
2875                         // Count first and last line
2876                         length = 0;
2877
2878                         // Count the lines in the middle
2879
2880                         for (i = 1; i < line.line_no; i++) {
2881                                 length += GetLine(i).text.Length;
2882                         }
2883
2884                         length += pos;
2885
2886                         return length;
2887                 }
2888
2889                 internal int SelectionLength() {
2890                         if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
2891                                 return 0;
2892                         }
2893
2894                         if (selection_start.line == selection_end.line) {
2895                                 return selection_end.pos - selection_start.pos;
2896                         } else {
2897                                 int     i;
2898                                 int     start;
2899                                 int     end;
2900                                 int     length;
2901
2902                                 // Count first and last line
2903                                 length = selection_start.line.text.Length - selection_start.pos + selection_end.pos + crlf_size;
2904
2905                                 // Count the lines in the middle
2906                                 start = selection_start.line.line_no + 1;
2907                                 end = selection_end.line.line_no;
2908
2909                                 if (start < end) {
2910                                         for (i = start; i < end; i++) {
2911                                                 Line line = GetLine (i);
2912                                                 length += line.text.Length + LineEndingLength (line.ending);
2913                                         }
2914                                 }
2915
2916                                 return length;
2917                         }
2918
2919                         
2920                 }
2921
2922
2923                 /// <summary>Give it a Line number and it returns the Line object at with that line number</summary>
2924                 internal Line GetLine(int LineNo) {
2925                         Line    line = document;
2926
2927                         while (line != sentinel) {
2928                                 if (LineNo == line.line_no) {
2929                                         return line;
2930                                 } else if (LineNo < line.line_no) {
2931                                         line = line.left;
2932                                 } else {
2933                                         line = line.right;
2934                                 }
2935                         }
2936
2937                         return null;
2938                 }
2939
2940                 /// <summary>Retrieve the previous tag; walks line boundaries</summary>
2941                 internal LineTag PreviousTag(LineTag tag) {
2942                         Line l; 
2943
2944                         if (tag.Previous != null) {
2945                                 return tag.Previous;
2946                         }
2947
2948                         // Next line 
2949                         if (tag.Line.line_no == 1) {
2950                                 return null;
2951                         }
2952
2953                         l = GetLine(tag.Line.line_no - 1);
2954                         if (l != null) {
2955                                 LineTag t;
2956
2957                                 t = l.tags;
2958                                 while (t.Next != null) {
2959                                         t = t.Next;
2960                                 }
2961                                 return t;
2962                         }
2963
2964                         return null;
2965                 }
2966
2967                 /// <summary>Retrieve the next tag; walks line boundaries</summary>
2968                 internal LineTag NextTag(LineTag tag) {
2969                         Line l;
2970
2971                         if (tag.Next != null) {
2972                                 return tag.Next;
2973                         }
2974
2975                         // Next line
2976                         l = GetLine(tag.Line.line_no + 1);
2977                         if (l != null) {
2978                                 return l.tags;
2979                         }
2980
2981                         return null;
2982                 }
2983
2984                 internal Line ParagraphStart(Line line) {
2985                         while (line.ending == LineEnding.Wrap) {
2986                                 line = GetLine(line.line_no - 1);
2987                         }
2988                         return line;
2989                 }       
2990
2991                 internal Line ParagraphEnd(Line line) {
2992                         Line    l;
2993    
2994                         while (line.ending == LineEnding.Wrap) {
2995                                 l = GetLine(line.line_no + 1);
2996                                 if ((l == null) || (l.ending != LineEnding.Wrap)) {
2997                                         break;
2998                                 }
2999                                 line = l;
3000                         }
3001                         return line;
3002                 }
3003
3004                 /// <summary>Give it a pixel offset coordinate and it returns the Line covering that are (offset
3005                 /// is either X or Y depending on if we are multiline
3006                 /// </summary>
3007                 internal Line GetLineByPixel (int offset, bool exact)
3008                 {
3009                         Line    line = document;
3010                         Line    last = null;
3011
3012                         if (multiline) {
3013                                 while (line != sentinel) {
3014                                         last = line;
3015                                         if ((offset >= line.Y) && (offset < (line.Y+line.height))) {
3016                                                 return line;
3017                                         } else if (offset < line.Y) {
3018                                                 line = line.left;
3019                                         } else {
3020                                                 line = line.right;
3021                                         }
3022                                 }
3023                         } else {
3024                                 while (line != sentinel) {
3025                                         last = line;
3026                                         if ((offset >= line.X) && (offset < (line.X + line.Width)))
3027                                                 return line;
3028                                         else if (offset < line.X)
3029                                                 line = line.left;
3030                                         else
3031                                                 line = line.right;
3032                                 }
3033                         }
3034
3035                         if (exact) {
3036                                 return null;
3037                         }
3038                         return last;
3039                 }
3040
3041                 // Give it x/y pixel coordinates and it returns the Tag at that position
3042                 internal LineTag FindCursor (int x, int y, out int index)
3043                 {
3044                         Line line;
3045
3046                         line = GetLineByPixel (multiline ? y : x, false);
3047
3048                         LineTag tag = line.GetTag (x);
3049                                 
3050                         if (tag.Length == 0)
3051                                 index = 0;
3052                         else
3053                                 index = tag.GetCharIndex (x);
3054                         
3055                         return tag;
3056                 }
3057
3058                 /// <summary>Format area of document in specified font and color</summary>
3059                 /// <param name="start_pos">1-based start position on start_line</param>
3060                 /// <param name="end_pos">1-based end position on end_line </param>
3061                 internal void FormatText (Line start_line, int start_pos, Line end_line, int end_pos, Font font,
3062                                 Color color, Color back_color, FormatSpecified specified)
3063                 {
3064                         Line    l;
3065
3066                         // First, format the first line
3067                         if (start_line != end_line) {
3068                                 // First line
3069                                 LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, font, color, back_color, specified);
3070
3071                                 // Format last line
3072                                 LineTag.FormatText(end_line, 1, end_pos, font, color, back_color, specified);
3073
3074                                 // Now all the lines inbetween
3075                                 for (int i = start_line.line_no + 1; i < end_line.line_no; i++) {
3076                                         l = GetLine(i);
3077                                         LineTag.FormatText(l, 1, l.text.Length, font, color, back_color, specified);
3078                                 }
3079                         } else {
3080                                 // Special case, single line
3081                                 LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color, back_color, specified);
3082                         }
3083                 }
3084
3085                 internal void RecalculateAlignments ()
3086                 {
3087                         Line    line;
3088                         int     line_no;
3089
3090                         line_no = 1;
3091
3092
3093
3094                         while (line_no <= lines) {
3095                                 line = GetLine(line_no);
3096
3097                                 if (line != null) {
3098                                         switch (line.alignment) {
3099                                         case HorizontalAlignment.Left:
3100                                                 line.align_shift = 0;
3101                                                 break;
3102                                         case HorizontalAlignment.Center:
3103                                                 line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
3104                                                 break;
3105                                         case HorizontalAlignment.Right:
3106                                                 line.align_shift = viewport_width - (int)line.widths[line.text.Length] - right_margin;
3107                                                 break;
3108                                         }
3109                                 }
3110
3111                                 line_no++;
3112                         }
3113                         return;
3114                 }
3115
3116                 /// <summary>Calculate formatting for the whole document</summary>
3117                 internal bool RecalculateDocument(Graphics g) {
3118                         return RecalculateDocument(g, 1, this.lines, false);
3119                 }
3120
3121                 /// <summary>Calculate formatting starting at a certain line</summary>
3122                 internal bool RecalculateDocument(Graphics g, int start) {
3123                         return RecalculateDocument(g, start, this.lines, false);
3124                 }
3125
3126                 /// <summary>Calculate formatting within two given line numbers</summary>
3127                 internal bool RecalculateDocument(Graphics g, int start, int end) {
3128                         return RecalculateDocument(g, start, end, false);
3129                 }
3130
3131                 /// <summary>With optimize on, returns true if line heights changed</summary>
3132                 internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
3133                         Line    line;
3134                         int     line_no;
3135                         int     offset;
3136                         int     new_width;
3137                         bool    changed;
3138                         int     shift;
3139
3140                         if (recalc_suspended > 0) {
3141                                 recalc_pending = true;
3142                                 recalc_start = Math.Min (recalc_start, start);
3143                                 recalc_end = Math.Max (recalc_end, end);
3144                                 recalc_optimize = optimize;
3145                                 return false;
3146                         }
3147
3148                         // Fixup the positions, they can go kinda nuts
3149                         start = Math.Max (start, 1);
3150                         end = Math.Min (end, lines);
3151
3152                         offset = GetLine(start).offset;
3153                         line_no = start;
3154                         new_width = 0;
3155                         shift = this.lines;
3156                         if (!optimize) {
3157                                 changed = true;         // We always return true if we run non-optimized
3158                         } else {
3159                                 changed = false;
3160                         }
3161
3162                         while (line_no <= (end + this.lines - shift)) {
3163                                 line = GetLine(line_no++);
3164                                 line.offset = offset;
3165
3166                                 if (!calc_pass) {
3167                                         if (!optimize) {
3168                                                 line.RecalculateLine(g, this);
3169                                         } else {
3170                                                 if (line.recalc && line.RecalculateLine(g, this)) {
3171                                                         changed = true;
3172                                                         // If the height changed, all subsequent lines change
3173                                                         end = this.lines;
3174                                                         shift = this.lines;
3175                                                 }
3176                                         }
3177                                 } else {
3178                                         if (!optimize) {
3179                                                 line.RecalculatePasswordLine(g, this);
3180                                         } else {
3181                                                 if (line.recalc && line.RecalculatePasswordLine(g, this)) {
3182                                                         changed = true;
3183                                                         // If the height changed, all subsequent lines change
3184                                                         end = this.lines;
3185                                                         shift = this.lines;
3186                                                 }
3187                                         }
3188                                 }
3189
3190                                 if (line.widths[line.text.Length] > new_width) {
3191                                         new_width = (int)line.widths[line.text.Length];
3192                                 }
3193
3194                                 // Calculate alignment
3195                                 if (line.alignment != HorizontalAlignment.Left) {
3196                                         if (line.alignment == HorizontalAlignment.Center) {
3197                                                 line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
3198                                         } else {
3199                                                 line.align_shift = viewport_width - (int)line.widths[line.text.Length] - 1;
3200                                         }
3201                                 }
3202
3203                                 if (multiline)
3204                                         offset += line.height;
3205                                 else
3206                                         offset += (int) line.widths [line.text.Length];
3207
3208                                 if (line_no > lines) {
3209                                         break;
3210                                 }
3211                         }
3212
3213                         if (document_x != new_width) {
3214                                 document_x = new_width;
3215                                 if (WidthChanged != null) {
3216                                         WidthChanged(this, null);
3217                                 }
3218                         }
3219
3220                         RecalculateAlignments();
3221
3222                         line = GetLine(lines);
3223
3224                         if (document_y != line.Y + line.height) {
3225                                 document_y = line.Y + line.height;
3226                                 if (HeightChanged != null) {
3227                                         HeightChanged(this, null);
3228                                 }
3229                         }
3230                         UpdateCaret();
3231                         return changed;
3232                 }
3233
3234                 internal int Size() {
3235                         return lines;
3236                 }
3237
3238                 private void owner_HandleCreated(object sender, EventArgs e) {
3239                         RecalculateDocument(owner.CreateGraphicsInternal());
3240                         AlignCaret();
3241                 }
3242
3243                 private void owner_VisibleChanged(object sender, EventArgs e) {
3244                         if (owner.Visible) {
3245                                 RecalculateDocument(owner.CreateGraphicsInternal());
3246                         }
3247                 }
3248
3249                 internal static bool IsWordSeparator (char ch)
3250                 {
3251                         switch (ch) {
3252                         case ' ':
3253                         case '\t':
3254                         case '(':
3255                         case ')':
3256                         case '\r':
3257                         case '\n':
3258                                 return true;
3259                         default:
3260                                 return false;
3261                         }
3262                 }
3263
3264                 internal int FindWordSeparator(Line line, int pos, bool forward) {
3265                         int len;
3266
3267                         len = line.text.Length;
3268
3269                         if (forward) {
3270                                 for (int i = pos + 1; i < len; i++) {
3271                                         if (IsWordSeparator(line.Text[i])) {
3272                                                 return i + 1;
3273                                         }
3274                                 }
3275                                 return len;
3276                         } else {
3277                                 for (int i = pos - 1; i > 0; i--) {
3278                                         if (IsWordSeparator(line.Text[i - 1])) {
3279                                                 return i;
3280                                         }
3281                                 }
3282                                 return 0;
3283                         }
3284                 }
3285
3286                 /* Search document for text */
3287                 internal bool FindChars(char[] chars, Marker start, Marker end, out Marker result) {
3288                         Line    line;
3289                         int     line_no;
3290                         int     pos;
3291                         int     line_len;
3292
3293                         // Search for occurence of any char in the chars array
3294                         result = new Marker();
3295
3296                         line = start.line;
3297                         line_no = start.line.line_no;
3298                         pos = start.pos;
3299                         while (line_no <= end.line.line_no) {
3300                                 line_len = line.text.Length;
3301                                 while (pos < line_len) {
3302                                         for (int i = 0; i < chars.Length; i++) {
3303                                                 if (line.text[pos] == chars[i]) {
3304                                                         // Special case
3305                                                         if ((line.line_no == end.line.line_no) && (pos >= end.pos)) {
3306                                                                 return false;
3307                                                         }
3308
3309                                                         result.line = line;
3310                                                         result.pos = pos;
3311                                                         return true;
3312                                                 }
3313                                         }
3314                                         pos++;
3315                                 }
3316
3317                                 pos = 0;
3318                                 line_no++;
3319                                 line = GetLine(line_no);
3320                         }
3321
3322                         return false;
3323                 }
3324
3325                 // This version does not build one big string for searching, instead it handles 
3326                 // line-boundaries, which is faster and less memory intensive
3327                 // FIXME - Depending on culture stuff we might have to create a big string and use culturespecific 
3328                 // search stuff and change it to accept and return positions instead of Markers (which would match 
3329                 // RichTextBox behaviour better but would be inconsistent with the rest of TextControl)
3330                 internal bool Find(string search, Marker start, Marker end, out Marker result, RichTextBoxFinds options) {
3331                         Marker  last;
3332                         string  search_string;
3333                         Line    line;
3334                         int     line_no;
3335                         int     pos;
3336                         int     line_len;
3337                         int     current;
3338                         bool    word;
3339                         bool    word_option;
3340                         bool    ignore_case;
3341                         bool    reverse;
3342                         char    c;
3343
3344                         result = new Marker();
3345                         word_option = ((options & RichTextBoxFinds.WholeWord) != 0);
3346                         ignore_case = ((options & RichTextBoxFinds.MatchCase) == 0);
3347                         reverse = ((options & RichTextBoxFinds.Reverse) != 0);
3348
3349                         line = start.line;
3350                         line_no = start.line.line_no;
3351                         pos = start.pos;
3352                         current = 0;
3353
3354                         // Prep our search string, lowercasing it if we do case-independent matching
3355                         if (ignore_case) {
3356                                 StringBuilder   sb;
3357                                 sb = new StringBuilder(search);
3358                                 for (int i = 0; i < sb.Length; i++) {
3359                                         sb[i] = Char.ToLower(sb[i]);
3360                                 }
3361                                 search_string = sb.ToString();
3362                         } else {
3363                                 search_string = search;
3364                         }
3365
3366                         // We need to check if the character before our start position is a wordbreak
3367                         if (word_option) {
3368                                 if (line_no == 1) {
3369                                         if ((pos == 0) || (IsWordSeparator(line.text[pos - 1]))) {
3370                                                 word = true;
3371                                         } else {
3372                                                 word = false;
3373                                         }
3374                                 } else {
3375                                         if (pos > 0) {
3376                                                 if (IsWordSeparator(line.text[pos - 1])) {
3377                                                         word = true;
3378                                                 } else {
3379                                                         word = false;
3380                                                 }
3381                                         } else {
3382                                                 // Need to check the end of the previous line
3383                                                 Line    prev_line;
3384
3385                                                 prev_line = GetLine(line_no - 1);
3386                                                 if (prev_line.ending == LineEnding.Wrap) {
3387                                                         if (IsWordSeparator(prev_line.text[prev_line.text.Length - 1])) {
3388                                                                 word = true;
3389                                                         } else {
3390                                                                 word = false;
3391                                                         }
3392                                                 } else {
3393                                                         word = true;
3394                                                 }
3395                                         }
3396                                 }
3397                         } else {
3398                                 word = false;
3399                         }
3400
3401                         // To avoid duplication of this loop with reverse logic, we search
3402                         // through the document, remembering the last match and when returning
3403                         // report that last remembered match
3404
3405                         last = new Marker();
3406                         last.height = -1;       // Abused - we use it to track change
3407
3408                         while (line_no <= end.line.line_no) {
3409                                 if (line_no != end.line.line_no) {
3410                                         line_len = line.text.Length;
3411                                 } else {
3412                                         line_len = end.pos;
3413                                 }
3414
3415                                 while (pos < line_len) {
3416
3417                                         if (word_option && (current == search_string.Length)) {
3418                                                 if (IsWordSeparator(line.text[pos])) {
3419                                                         if (!reverse) {
3420                                                                 goto FindFound;
3421                                                         } else {
3422                                                                 last = result;
3423                                                                 current = 0;
3424                                                         }
3425                                                 } else {
3426                                                         current = 0;
3427                                                 }
3428                                         }
3429
3430                                         if (ignore_case) {
3431                                                 c = Char.ToLower(line.text[pos]);
3432                                         } else {
3433                                                 c = line.text[pos];
3434                                         }
3435
3436                                         if (c == search_string[current]) {
3437                                                 
3438                                                 if (current == 0) {
3439                                                         result.line = line;
3440                                                         result.pos = pos;
3441                                                 }
3442                                                 if (!word_option || (word_option && (word || (current > 0)))) {
3443                                                         current++;
3444                                                 }
3445
3446                                                 if (!word_option && (current == search_string.Length)) {
3447                                                         if (!reverse) {
3448                                                                 goto FindFound;
3449                                                         } else {
3450                                                                 last = result;
3451                                                                 current = 0;
3452                                                         }
3453                                                 }
3454                                         } else {
3455                                                 current = 0;
3456                                         }
3457                                         pos++;
3458
3459                                         if (!word_option) {
3460                                                 continue;
3461                                         }
3462
3463                                         if (IsWordSeparator(c)) {
3464                                                 word = true;
3465                                         } else {
3466                                                 word = false;
3467                                         }
3468                                 }
3469
3470                                 if (word_option) {
3471                                         // Mark that we just saw a word boundary
3472                                         if (line.ending != LineEnding.Wrap || line.line_no == lines - 1) {
3473                                                 word = true;
3474                                         }
3475
3476                                         if (current == search_string.Length) {
3477                                                 if (word) {
3478                                                         if (!reverse) {
3479                                                                 goto FindFound;
3480                                                         } else {
3481                                                                 last = result;
3482                                                                 current = 0;
3483                                                         }
3484                                                 } else {
3485                                                         current = 0;
3486                                                 }
3487                                         }
3488                                 }
3489
3490                                 pos = 0;
3491                                 line_no++;
3492                                 line = GetLine(line_no);
3493                         }
3494
3495                         if (reverse) {
3496                                 if (last.height != -1) {
3497                                         result = last;
3498                                         return true;
3499                                 }
3500                         }
3501
3502                         return false;
3503
3504                         FindFound:
3505                         if (!reverse) {
3506 //                              if ((line.line_no == end.line.line_no) && (pos >= end.pos)) {
3507 //                                      return false;
3508 //                              }
3509                                 return true;
3510                         }
3511
3512                         result = last;
3513                         return true;
3514
3515                 }
3516
3517                 /* Marker stuff */
3518                 internal void GetMarker(out Marker mark, bool start) {
3519                         mark = new Marker();
3520
3521                         if (start) {
3522                                 mark.line = GetLine(1);
3523                                 mark.tag = mark.line.tags;
3524                                 mark.pos = 0;
3525                         } else {
3526                                 mark.line = GetLine(lines);
3527                                 mark.tag = mark.line.tags;
3528                                 while (mark.tag.Next != null) {
3529                                         mark.tag = mark.tag.Next;
3530                                 }
3531                                 mark.pos = mark.line.text.Length;
3532                         }
3533                 }
3534                 #endregion      // Internal Methods
3535
3536                 #region Events
3537                 internal event EventHandler CaretMoved;
3538                 internal event EventHandler WidthChanged;
3539                 internal event EventHandler HeightChanged;
3540                 internal event EventHandler LengthChanged;
3541                 #endregion      // Events
3542
3543                 #region Administrative
3544                 public IEnumerator GetEnumerator() {
3545                         // FIXME
3546                         return null;
3547                 }
3548
3549                 public override bool Equals(object obj) {
3550                         if (obj == null) {
3551                                 return false;
3552                         }
3553
3554                         if (!(obj is Document)) {
3555                                 return false;
3556                         }
3557
3558                         if (obj == this) {
3559                                 return true;
3560                         }
3561
3562                         if (ToString().Equals(((Document)obj).ToString())) {
3563                                 return true;
3564                         }
3565
3566                         return false;
3567                 }
3568
3569                 public override int GetHashCode() {
3570                         return document_id;
3571                 }
3572
3573                 public override string ToString() {
3574                         return "document " + this.document_id;
3575                 }
3576                 #endregion      // Administrative
3577         }
3578
3579         internal class PictureTag : LineTag {
3580
3581                 internal RTF.Picture picture;
3582
3583                 internal PictureTag (Line line, int start, RTF.Picture picture) : base (line, start)
3584                 {
3585                         this.picture = picture;
3586                 }
3587
3588                 public override bool IsTextTag {
3589                         get { return false; }
3590                 }
3591
3592                 public override SizeF SizeOfPosition (Graphics dc, int pos)
3593                 {
3594                         return picture.Size;
3595                 }
3596
3597                 internal override int MaxHeight ()
3598                 {
3599                         return (int) (picture.Height + 0.5F);
3600                 }
3601
3602                 public override void Draw (Graphics dc, Color color, float xoff, float y, int start, int end)
3603                 {
3604                         picture.DrawImage (dc, xoff + Line.widths [start], y, false);
3605                 }
3606
3607                 public override void Draw (Graphics dc, Color color, float xoff, float y, int start, int end, string text)
3608                 {
3609                         picture.DrawImage (dc, xoff + + Line.widths [start], y, false);
3610                 }
3611
3612                 public override string Text ()
3613                 {
3614                         return "I";
3615                 }
3616         }
3617
3618         internal class UndoManager {
3619
3620                 internal enum ActionType {
3621
3622                         Typing,
3623
3624                         // This is basically just cut & paste
3625                         InsertString,
3626                         DeleteString,
3627
3628                         UserActionBegin,
3629                         UserActionEnd
3630                 }
3631
3632                 internal class Action {
3633                         internal ActionType     type;
3634                         internal int            line_no;
3635                         internal int            pos;
3636                         internal object         data;
3637                 }
3638
3639                 #region Local Variables
3640                 private Document        document;
3641                 private Stack           undo_actions;
3642                 private Stack           redo_actions;
3643
3644                 //private int           caret_line;
3645                 //private int           caret_pos;
3646
3647                 // When performing an action, we lock the queue, so that the action can't be undone
3648                 private bool locked;
3649                 #endregion      // Local Variables
3650
3651                 #region Constructors
3652                 internal UndoManager (Document document)
3653                 {
3654                         this.document = document;
3655                         undo_actions = new Stack (50);
3656                         redo_actions = new Stack (50);
3657                 }
3658                 #endregion      // Constructors
3659
3660                 #region Properties
3661                 internal bool CanUndo {
3662                         get { return undo_actions.Count > 0; }
3663                 }
3664
3665                 internal bool CanRedo {
3666                         get { return redo_actions.Count > 0; }
3667                 }
3668
3669                 internal string UndoActionName {
3670                         get {
3671                                 foreach (Action action in undo_actions) {
3672                                         if (action.type == ActionType.UserActionBegin)
3673                                                 return (string) action.data;
3674                                         if (action.type == ActionType.Typing)
3675                                                 return Locale.GetText ("Typing");
3676                                 }
3677                                 return String.Empty;
3678                         }
3679                 }
3680
3681                 internal string RedoActionName {
3682                         get {
3683                                 foreach (Action action in redo_actions) {
3684                                         if (action.type == ActionType.UserActionBegin)
3685                                                 return (string) action.data;
3686                                         if (action.type == ActionType.Typing)
3687                                                 return Locale.GetText ("Typing");
3688                                 }
3689                                 return String.Empty;
3690                         }
3691                 }
3692                 #endregion      // Properties
3693
3694                 #region Internal Methods
3695                 internal void Clear ()
3696                 {
3697                         undo_actions.Clear();
3698                         redo_actions.Clear();
3699                 }
3700
3701                 internal void Undo ()
3702                 {
3703                         Action action;
3704                         bool user_action_finished = false;
3705
3706                         if (undo_actions.Count == 0)
3707                                 return;
3708
3709                         // Nuke the redo queue
3710                         redo_actions.Clear ();
3711
3712                         locked = true;
3713                         do {
3714                                 Line start;
3715                                 action = (Action) undo_actions.Pop ();
3716
3717                                 // Put onto redo stack
3718                                 redo_actions.Push(action);
3719
3720                                 // Do the thing
3721                                 switch(action.type) {
3722
3723                                 case ActionType.UserActionBegin:
3724                                         user_action_finished = true;
3725                                         break;
3726
3727                                 case ActionType.UserActionEnd:
3728                                         // noop
3729                                         break;
3730
3731                                 case ActionType.InsertString:
3732                                         start = document.GetLine (action.line_no);
3733                                         document.SuspendUpdate ();
3734                                         document.DeleteMultiline (start, action.pos, ((string) action.data).Length + 1);
3735                                         document.PositionCaret (start, action.pos);
3736                                         document.SetSelectionToCaret (true);
3737                                         document.ResumeUpdate (true);
3738                                         break;
3739
3740                                 case ActionType.Typing:
3741                                         start = document.GetLine (action.line_no);
3742                                         document.SuspendUpdate ();
3743                                         document.DeleteMultiline (start, action.pos, ((StringBuilder) action.data).Length);
3744                                         document.PositionCaret (start, action.pos);
3745                                         document.SetSelectionToCaret (true);
3746                                         document.ResumeUpdate (true);
3747
3748                                         // This is an open ended operation, so only a single typing operation can be undone at once
3749                                         user_action_finished = true;
3750                                         break;
3751
3752                                 case ActionType.DeleteString:
3753                                         start = document.GetLine (action.line_no);
3754                                         document.SuspendUpdate ();
3755                                         Insert (start, action.pos, (Line) action.data, true);
3756                                         document.ResumeUpdate (true);
3757                                         break;
3758                                 }
3759                         } while (!user_action_finished && undo_actions.Count > 0);
3760
3761                         locked = false;
3762                 }
3763
3764                 internal void Redo ()
3765                 {
3766                         Action action;
3767                         bool user_action_finished = false;
3768
3769                         if (redo_actions.Count == 0)
3770                                 return;
3771
3772                         // You can't undo anything after redoing
3773                         undo_actions.Clear ();
3774
3775                         locked = true;
3776                         do {
3777                                 Line start;
3778                                 int start_index;
3779
3780                                 action = (Action) redo_actions.Pop ();
3781
3782                                 switch (action.type) {
3783
3784                                 case ActionType.UserActionBegin:
3785                                         //  Noop
3786                                         break;
3787
3788                                 case ActionType.UserActionEnd:
3789                                         user_action_finished = true;
3790                                         break;
3791
3792                                 case ActionType.InsertString:
3793                                         start = document.GetLine (action.line_no);
3794                                         document.SuspendUpdate ();
3795                                         start_index = document.LineTagToCharIndex (start, action.pos);
3796                                         document.InsertString (start, action.pos, (string) action.data);
3797                                         document.CharIndexToLineTag (start_index + ((string) action.data).Length,
3798                                                         out document.caret.line, out document.caret.tag,
3799                                                         out document.caret.pos);
3800                                         document.UpdateCaret ();
3801                                         document.SetSelectionToCaret (true);
3802                                         document.ResumeUpdate (true);
3803                                         break;
3804
3805                                 case ActionType.Typing:
3806                                         start = document.GetLine (action.line_no);
3807                                         document.SuspendUpdate ();
3808                                         start_index = document.LineTagToCharIndex (start, action.pos);
3809                                         document.InsertString (start, action.pos, ((StringBuilder) action.data).ToString ());
3810                                         document.CharIndexToLineTag (start_index + ((StringBuilder) action.data).Length,
3811                                                         out document.caret.line, out document.caret.tag,
3812                                                         out document.caret.pos);
3813                                         document.UpdateCaret ();
3814                                         document.SetSelectionToCaret (true);
3815                                         document.ResumeUpdate (true);
3816
3817                                         // This is an open ended operation, so only a single typing operation can be undone at once
3818                                         user_action_finished = true;
3819                                         break;
3820
3821                                 case ActionType.DeleteString:
3822                                         start = document.GetLine (action.line_no);
3823                                         document.SuspendUpdate ();
3824                                         document.DeleteMultiline (start, action.pos, ((Line) action.data).text.Length);
3825                                         document.PositionCaret (start, action.pos);
3826                                         document.SetSelectionToCaret (true);
3827                                         document.ResumeUpdate (true);
3828
3829                                         break;
3830                                 }
3831                         } while (!user_action_finished && redo_actions.Count > 0);
3832
3833                         locked = false;
3834                 }
3835                 #endregion      // Internal Methods
3836
3837                 #region Private Methods
3838
3839                 public void BeginUserAction (string name)
3840                 {
3841                         if (locked)
3842                                 return;
3843
3844                         Action ua = new Action ();
3845                         ua.type = ActionType.UserActionBegin;
3846                         ua.data = name;
3847
3848                         undo_actions.Push (ua);
3849                 }
3850
3851                 public void EndUserAction ()
3852                 {
3853                         if (locked)
3854                                 return;
3855
3856                         Action ua = new Action ();
3857                         ua.type = ActionType.UserActionEnd;
3858
3859                         undo_actions.Push (ua);
3860                 }
3861
3862                 // start_pos, end_pos = 1 based
3863                 public void RecordDeleteString (Line start_line, int start_pos, Line end_line, int end_pos)
3864                 {
3865                         if (locked)
3866                                 return;
3867
3868                         Action  a = new Action ();
3869
3870                         // We cant simply store the string, because then formatting would be lost
3871                         a.type = ActionType.DeleteString;
3872                         a.line_no = start_line.line_no;
3873                         a.pos = start_pos;
3874                         a.data = Duplicate (start_line, start_pos, end_line, end_pos);
3875
3876                         undo_actions.Push(a);
3877                 }
3878
3879                 public void RecordInsertString (Line line, int pos, string str)
3880                 {
3881                         if (locked || str.Length == 0)
3882                                 return;
3883
3884                         Action a = new Action ();
3885
3886                         a.type = ActionType.InsertString;
3887                         a.data = str;
3888                         a.line_no = line.line_no;
3889                         a.pos = pos;
3890
3891                         undo_actions.Push (a);
3892                 }
3893
3894                 public void RecordTyping (Line line, int pos, char ch)
3895                 {
3896                         if (locked)
3897                                 return;
3898
3899                         Action a = null;
3900
3901                         if (undo_actions.Count > 0)
3902                                 a = (Action) undo_actions.Peek ();
3903
3904                         if (a == null || a.type != ActionType.Typing) {
3905                                 a = new Action ();
3906                                 a.type = ActionType.Typing;
3907                                 a.data = new StringBuilder ();
3908                                 a.line_no = line.line_no;
3909                                 a.pos = pos;
3910
3911                                 undo_actions.Push (a);
3912                         }
3913
3914                         StringBuilder data = (StringBuilder) a.data;
3915                         data.Append (ch);
3916                 }
3917
3918                 // start_pos = 1-based
3919                 // end_pos = 1-based
3920                 public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos)
3921                 {
3922                         Line    ret;
3923                         Line    line;
3924                         Line    current;
3925                         LineTag tag;
3926                         LineTag current_tag;
3927                         int     start;
3928                         int     end;
3929                         int     tag_start;
3930
3931                         line = new Line (start_line.document, start_line.ending);
3932                         ret = line;
3933
3934                         for (int i = start_line.line_no; i <= end_line.line_no; i++) {
3935                                 current = document.GetLine(i);
3936
3937                                 if (start_line.line_no == i) {
3938                                         start = start_pos;
3939                                 } else {
3940                                         start = 0;
3941                                 }
3942
3943                                 if (end_line.line_no == i) {
3944                                         end = end_pos;
3945                                 } else {
3946                                         end = current.text.Length;
3947                                 }
3948
3949                                 if (end_pos == 0)
3950                                         continue;
3951
3952                                 // Text for the tag
3953                                 line.text = new StringBuilder (current.text.ToString (start, end - start));
3954
3955                                 // Copy tags from start to start+length onto new line
3956                                 current_tag = current.FindTag (start);
3957                                 while ((current_tag != null) && (current_tag.Start <= end)) {
3958                                         if ((current_tag.Start <= start) && (start < (current_tag.Start + current_tag.Length))) {
3959                                                 // start tag is within this tag
3960                                                 tag_start = start;
3961                                         } else {
3962                                                 tag_start = current_tag.Start;
3963                                         }
3964
3965                                         tag = new LineTag(line, tag_start - start + 1);
3966                                         tag.CopyFormattingFrom (current_tag);
3967
3968                                         current_tag = current_tag.Next;
3969
3970                                         // Add the new tag to the line
3971                                         if (line.tags == null) {
3972                                                 line.tags = tag;
3973                                         } else {
3974                                                 LineTag tail;
3975                                                 tail = line.tags;
3976
3977                                                 while (tail.Next != null) {
3978                                                         tail = tail.Next;
3979                                                 }
3980                                                 tail.Next = tag;
3981                                                 tag.Previous = tail;
3982                                         }
3983                                 }
3984
3985                                 if ((i + 1) <= end_line.line_no) {
3986                                         line.ending = current.ending;
3987
3988                                         // Chain them (we use right/left as next/previous)
3989                                         line.right = new Line (start_line.document, start_line.ending);
3990                                         line.right.left = line;
3991                                         line = line.right;
3992                                 }
3993                         }
3994
3995                         return ret;
3996                 }
3997
3998                 // Insert multi-line text at the given position; use formatting at insertion point for inserted text
3999                 internal void Insert(Line line, int pos, Line insert, bool select)
4000                 {
4001                         Line    current;
4002                         LineTag tag;
4003                         int     offset;
4004                         int     lines;
4005                         Line    first;
4006
4007                         // Handle special case first
4008                         if (insert.right == null) {
4009
4010                                 // Single line insert
4011                                 document.Split(line, pos);
4012
4013                                 if (insert.tags == null) {
4014                                         return; // Blank line
4015                                 }
4016
4017                                 //Insert our tags at the end
4018                                 tag = line.tags;
4019
4020                                 while (tag.Next != null) {
4021                                         tag = tag.Next;
4022                                 }
4023
4024                                 offset = tag.Start + tag.Length - 1;
4025
4026                                 tag.Next = insert.tags;
4027                                 line.text.Insert(offset, insert.text.ToString());
4028
4029                                 // Adjust start locations
4030                                 tag = tag.Next;
4031                                 while (tag != null) {
4032                                         tag.Start += offset;
4033                                         tag.Line = line;
4034                                         tag = tag.Next;
4035                                 }
4036                                 // Put it back together
4037                                 document.Combine(line.line_no, line.line_no + 1);
4038
4039                                 if (select) {
4040                                         document.SetSelectionStart (line, pos, false);
4041                                         document.SetSelectionEnd (line, pos + insert.text.Length, false);
4042                                 }
4043
4044                                 document.UpdateView(line, pos);
4045                                 return;
4046                         }
4047
4048                         first = line;
4049                         lines = 1;
4050                         current = insert;
4051
4052                         while (current != null) {
4053
4054                                 if (current == insert) {
4055                                         // Inserting the first line we split the line (and make space)
4056                                         document.Split(line.line_no, pos);
4057                                         //Insert our tags at the end of the line
4058                                         tag = line.tags;
4059
4060                                         
4061                                         if (tag != null && tag.Length != 0) {
4062                                                 while (tag.Next != null) {
4063                                                         tag = tag.Next;
4064                                                 }
4065                                                 offset = tag.Start + tag.Length - 1;
4066                                                 tag.Next = current.tags;
4067                                                 tag.Next.Previous = tag;
4068
4069                                                 tag = tag.Next;
4070
4071                                         } else {
4072                                                 offset = 0;
4073                                                 line.tags = current.tags;
4074                                                 line.tags.Previous = null;
4075                                                 tag = line.tags;
4076                                         }
4077
4078                                         line.ending = current.ending;
4079                                 } else {
4080                                         document.Split(line.line_no, 0);
4081                                         offset = 0;
4082                                         line.tags = current.tags;
4083                                         line.tags.Previous = null;
4084                                         line.ending = current.ending;
4085                                         tag = line.tags;
4086                                 }
4087
4088                                 // Adjust start locations and line pointers
4089                                 while (tag != null) {
4090                                         tag.Start += offset - 1;
4091                                         tag.Line = line;
4092                                         tag = tag.Next;
4093                                 }
4094
4095                                 line.text.Insert(offset, current.text.ToString());
4096                                 line.Grow(line.text.Length);
4097
4098                                 line.recalc = true;
4099                                 line = document.GetLine(line.line_no + 1);
4100
4101                                 // FIXME? Test undo of line-boundaries
4102                                 if ((current.right == null) && (current.tags.Length != 0)) {
4103                                         document.Combine(line.line_no - 1, line.line_no);
4104                                 }
4105                                 current = current.right;
4106                                 lines++;
4107
4108                         }
4109
4110                         // Recalculate our document
4111                         document.UpdateView(first, lines, pos);
4112                         return;
4113                 }               
4114                 #endregion      // Private Methods
4115         }
4116 }