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