Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextControl.cs
index 52880772ba46b79fb120691592937315affbbe7b..0452c2c6fa30f98c9ddc59221d008d5d545a805f 100644 (file)
@@ -95,626 +95,15 @@ namespace System.Windows.Forms {
        }
 
        internal enum LineEnding {
-               Wrap,    // line wraps to the next line
-               Limp,    // \r
-               Hard,    // \r\n
-               Soft,    // \r\r\n
-               Rich,    // \n
+               Wrap = 1,    // line wraps to the next line
+               Limp = 2,    // \r
+               Hard = 4,    // \r\n
+               Soft = 8,    // \r\r\n
+               Rich = 16,    // \n
 
-               None
+               None = 0
        }
        
-       // Being cloneable should allow for nice line and document copies...
-       internal class Line : ICloneable, IComparable {
-               #region Local Variables
-
-               internal Document document;
-
-               // Stuff that matters for our line
-               internal StringBuilder          text;                   // Characters for the line
-               internal float[]                widths;                 // Width of each character; always one larger than text.Length
-               internal int                    space;                  // Number of elements in text and widths
-               internal int                    line_no;                // Line number
-               internal LineTag                tags;                   // Tags describing the text
-               internal int                    offset;                 // Baseline can be on the X or Y axis depending if we are in multiline mode or not
-               internal int                    height;                 // Height of the line (height of tallest tag)
-               internal int                    ascent;                 // Ascent of the line (ascent of the tallest tag)
-               internal HorizontalAlignment    alignment;              // Alignment of the line
-               internal int                    align_shift;            // Pixel shift caused by the alignment
-               internal int                    indent;                 // Left indent for the first line
-               internal int                    hanging_indent;         // Hanging indent (left indent for all but the first line)
-               internal int                    right_indent;           // Right indent for all lines
-               internal LineEnding ending;
-
-
-               // Stuff that's important for the tree
-               internal Line                   parent;                 // Our parent line
-               internal Line                   left;                   // Line with smaller line number
-               internal Line                   right;                  // Line with higher line number
-               internal LineColor              color;                  // We're doing a black/red tree. this is the node color
-               internal int                    DEFAULT_TEXT_LEN;       // 
-               internal bool                   recalc;                 // Line changed
-               #endregion      // Local Variables
-
-               #region Constructors
-               internal Line (Document document, LineEnding ending)
-               {
-                       this.document = document; 
-                       color = LineColor.Red;
-                       left = null;
-                       right = null;
-                       parent = null;
-                       text = null;
-                       recalc = true;
-                       alignment = document.alignment;
-
-                       this.ending = ending;
-               }
-
-               internal Line(Document document, int LineNo, string Text, Font font, SolidBrush color, LineEnding ending) : this (document, ending)
-               {
-                       space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
-
-                       text = new StringBuilder(Text, space);
-                       line_no = LineNo;
-                       this.ending = ending;
-
-                       widths = new float[space + 1];
-
-                       
-                       tags = new LineTag(this, 1);
-                       tags.font = font;
-                       tags.color = color;                             
-               }
-
-               internal Line(Document document, int LineNo, string Text, HorizontalAlignment align, Font font, SolidBrush color, LineEnding ending) : this(document, ending)
-               {
-                       space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
-
-                       text = new StringBuilder(Text, space);
-                       line_no = LineNo;
-                       this.ending = ending;
-                       alignment = align;
-
-                       widths = new float[space + 1];
-
-                       
-                       tags = new LineTag(this, 1);
-                       tags.font = font;
-                       tags.color = color;
-               }
-
-               internal Line(Document document, int LineNo, string Text, LineTag tag, LineEnding ending) : this(document, ending)
-               {
-                       space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
-
-                       text = new StringBuilder(Text, space);
-                       this.ending = ending;
-                       line_no = LineNo;
-
-                       widths = new float[space + 1];
-                       tags = tag;
-               }
-
-               #endregion      // Constructors
-
-               #region Internal Properties
-
-               internal int Y {
-                       get {
-                               if (!document.multiline)
-                                       return document.top_margin;
-                               return document.top_margin + offset;
-                       }
-               }
-
-               internal int X {
-                       get {
-                               if (document.multiline)
-                                       return align_shift;
-                               return offset + align_shift;
-                       }
-               }
-
-               internal int Width {
-                       get {
-                               int res = (int) widths [text.Length];
-                               if (!document.multiline) {
-
-                               }
-                               return res;
-                       }
-               }
-
-               internal int Indent {
-                       get {
-                               return indent;
-                       }
-
-                       set {
-                               indent = value;
-                               recalc = true;
-                       }
-               }
-
-               internal int HangingIndent {
-                       get {
-                               return hanging_indent;
-                       }
-
-                       set {
-                               hanging_indent = value;
-                               recalc = true;
-                       }
-               }
-
-               internal int RightIndent {
-                       get {
-                               return right_indent;
-                       }
-
-                       set {
-                               right_indent = value;
-                               recalc = true;
-                       }
-               }
-                       
-
-               internal int Height {
-                       get {
-                               return height;
-                       }
-
-                       set {
-                               height = value;
-                       }
-               }
-
-               internal int LineNo {
-                       get {
-                               return line_no;
-                       }
-
-                       set {
-                               line_no = value;
-                       }
-               }
-
-               internal string Text {
-                       get {
-                               return text.ToString();
-                       }
-
-                       set {
-                               text = new StringBuilder(value, value.Length > DEFAULT_TEXT_LEN ? value.Length : DEFAULT_TEXT_LEN);
-                       }
-               }
-
-               internal HorizontalAlignment Alignment {
-                       get {
-                               return alignment;
-                       }
-
-                       set {
-                               if (alignment != value) {
-                                       alignment = value;
-                                       recalc = true;
-                               }
-                       }
-               }
-#if no
-               internal StringBuilder Text {
-                       get {
-                               return text;
-                       }
-
-                       set {
-                               text = value;
-                       }
-               }
-#endif
-               #endregion      // Internal Properties
-
-               #region Internal Methods
-
-               // This doesn't do exactly what you would think, it just pulls of the \n part of the ending
-               internal string TextWithoutEnding ()
-               {
-                       return text.ToString (0, text.Length - document.LineEndingLength (ending));
-               }
-
-               internal int TextLengthWithoutEnding ()
-               {
-                       return text.Length - document.LineEndingLength (ending);
-               }
-
-               internal void DrawEnding (Graphics dc, float y)
-               {
-                       if (document.multiline)
-                               return;
-                       LineTag last = tags;
-                       while (last.next != null)
-                               last = last.next;
-
-                       string end_str = null;
-                       switch (document.LineEndingLength (ending)) {
-                       case 0:
-                               return;
-                       case 1:
-                               end_str = "\u0013";
-                               break;
-                       case 2:
-                               end_str = "\u0013\u0013";
-                               break;
-                       case 3:
-                               end_str = "\u0013\u0013\u0013";
-                               break;
-                       }
-                       dc.DrawString (end_str, last.font, last.color,  X + widths [TextLengthWithoutEnding ()] - document.viewport_x,
-                                       y, Document.string_format);
-               }
-
-               
-               // Make sure we always have enoughs space in text and widths
-               internal void Grow(int minimum) {
-                       int     length;
-                       float[] new_widths;
-
-                       length = text.Length;
-
-                       if ((length + minimum) > space) {
-                               // We need to grow; double the size
-
-                               if ((length + minimum) > (space * 2)) {
-                                       new_widths = new float[length + minimum * 2 + 1];
-                                       space = length + minimum * 2;
-                               } else {                                
-                                       new_widths = new float[space * 2 + 1];
-                                       space *= 2;
-                               }
-                               widths.CopyTo(new_widths, 0);
-
-                               widths = new_widths;
-                       }
-               }
-
-               internal void Streamline(int lines) {
-                       LineTag current;
-                       LineTag next;
-
-                       current = this.tags;
-                       next = current.next;
-
-                       //
-                       // Catch what the loop below wont; eliminate 0 length 
-                       // tags, but only if there are other tags after us
-                       // We only eliminate text tags if there is another text tag
-                       // after it.  Otherwise we wind up trying to type on picture tags
-                       //
-                       while ((current.length == 0) && (next != null) && (next.IsTextTag)) {
-                               tags = next;
-                               tags.previous = null;
-                               current = next;
-                               next = current.next;
-                       }
-                       
-
-                       if (next == null) {
-                               return;
-                       }
-
-                       while (next != null) {
-                               // Take out 0 length tags unless it's the last tag in the document
-                               if (current.IsTextTag && next.length == 0 && next.IsTextTag) {
-                                       if ((next.next != null) || (line_no != lines)) {
-                                               current.next = next.next;
-                                               if (current.next != null) {
-                                                       current.next.previous = current;
-                                               }
-                                               next = current.next;
-                                               continue;
-                                       }
-                               }
-                               if (current.Combine(next)) {
-                                       next = current.next;
-                                       continue;
-                               }
-
-                               current = current.next;
-                               next = current.next;
-                       }
-               }
-
-               /// <summary> Find the tag on a line based on the character position, pos is 0-based</summary>
-               internal LineTag FindTag(int pos) {
-                       LineTag tag;
-
-                       if (pos == 0) {
-                               return tags;
-                       }
-
-                       tag = this.tags;
-
-                       if (pos >= text.Length) {
-                               pos = text.Length - 1;
-                       }
-
-                       while (tag != null) {
-                               if (((tag.start - 1) <= pos) && (pos < (tag.start + tag.length - 1))) {
-                                       return LineTag.GetFinalTag (tag);
-                               }
-                               tag = tag.next;
-                       }
-                       return null;
-               }
-
-               /// <summary>
-               /// Recalculate a single line using the same char for every character in the line
-               /// </summary>
-               
-               internal bool RecalculatePasswordLine(Graphics g, Document doc) {
-                       LineTag tag;
-                       int     pos;
-                       int     len;
-                       float   w;
-                       bool    ret;
-                       int     descent;
-
-                       pos = 0;
-                       len = this.text.Length;
-                       tag = this.tags;
-                       ascent = 0;
-                       tag.shift = 0;
-
-                       this.recalc = false;
-                       widths[0] = document.left_margin + indent;
-
-                       w = g.MeasureString(doc.password_char, tags.font, 10000, Document.string_format).Width;
-
-                       if (this.height != (int)tag.font.Height) {
-                               ret = true;
-                       } else {
-                               ret = false;
-                       }
-
-                       this.height = (int)tag.font.Height;
-                       tag.height = this.height;
-
-                       XplatUI.GetFontMetrics(g, tag.font, out tag.ascent, out descent);
-                       this.ascent = tag.ascent;
-
-                       while (pos < len) {
-                               pos++;
-                               widths[pos] = widths[pos-1] + w;
-                       }
-
-                       return ret;
-               }
-
-               /// <summary>
-               /// Go through all tags on a line and recalculate all size-related values;
-               /// returns true if lineheight changed
-               /// </summary>
-               internal bool RecalculateLine(Graphics g, Document doc) {
-                       LineTag tag;
-                       int     pos;
-                       int     len;
-                       SizeF   size;
-                       float   w;
-                       int     prev_offset;
-                       bool    retval;
-                       bool    wrapped;
-                       Line    line;
-                       int     wrap_pos;
-
-                       pos = 0;
-                       len = this.text.Length;
-                       tag = this.tags;
-                       prev_offset = this.offset;      // For drawing optimization calculations
-                       this.height = 0;                // Reset line height
-                       this.ascent = 0;                // Reset the ascent for the line
-                       tag.shift = 0;
-
-                       if (ending == LineEnding.Wrap) {
-                               widths[0] = document.left_margin + hanging_indent;
-                       } else {
-                               widths[0] = document.left_margin + indent;
-                       }
-
-                       this.recalc = false;
-                       retval = false;
-                       wrapped = false;
-
-                       wrap_pos = 0;
-
-                       while (pos < len) {
-
-                               while (tag.length == 0) {       // We should always have tags after a tag.length==0 unless len==0
-                                       tag.ascent = 0;
-                                       tag.shift = 0;
-                                       tag = tag.next;
-                               }
-
-                               size = tag.SizeOfPosition (g, pos);
-                               w = size.Width;
-
-                               if (Char.IsWhiteSpace(text[pos])) {
-                                       wrap_pos = pos + 1;
-                               }
-
-                               if (doc.wrap) {
-                                       if ((wrap_pos > 0) && (wrap_pos != len) && (widths[pos] + w) + 5 > (doc.viewport_width - this.right_indent)) {
-                                               // Make sure to set the last width of the line before wrapping
-                                               widths [pos + 1] = widths [pos] + w;
-
-                                               pos = wrap_pos;
-                                               len = text.Length;
-                                               doc.Split(this, tag, pos);
-                                               ending = LineEnding.Wrap;
-                                               len = this.text.Length;
-                                               
-                                               retval = true;
-                                               wrapped = true;
-                                       }  else if (pos > 1 && (widths[pos] + w) > (doc.viewport_width - this.right_indent)) {
-                                               // No suitable wrap position was found so break right in the middle of a word
-
-                                               // Make sure to set the last width of the line before wrapping
-                                               widths [pos + 1] = widths [pos] + w;
-
-                                               doc.Split(this, tag, pos);
-                                               ending = LineEnding.Wrap;
-                                               len = this.text.Length;
-                                               retval = true;
-                                               wrapped = true;
-                                       }
-                               }
-
-                               // Contract all wrapped lines that follow back into our line
-                               if (!wrapped) {
-                                       pos++;
-
-                                       widths[pos] = widths[pos-1] + w;
-
-                                       if (pos == len) {
-                                               line = doc.GetLine(this.line_no + 1);
-                                               if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None)) {
-                                                       // Pull the two lines together
-                                                       doc.Combine(this.line_no, this.line_no + 1);
-                                                       len = this.text.Length;
-                                                       retval = true;
-                                               }
-                                       }
-                               }
-
-                               if (pos == (tag.start-1 + tag.length)) {
-                                       // We just found the end of our current tag
-                                       tag.height = tag.MaxHeight ();
-
-                                       // Check if we're the tallest on the line (so far)
-                                       if (tag.height > this.height) {
-                                               this.height = tag.height;               // Yep; make sure the line knows
-                                       }
-
-                                       if (tag.ascent == 0) {
-                                               int     descent;
-
-                                               XplatUI.GetFontMetrics(g, tag.font, out tag.ascent, out descent);
-                                       }
-
-                                       if (tag.ascent > this.ascent) {
-                                               LineTag         t;
-
-                                               // We have a tag that has a taller ascent than the line;
-                                               t = tags;
-                                               while (t != null && t != tag) {
-                                                       t.shift = tag.ascent - t.ascent;
-                                                       t = t.next;
-                                               }
-
-                                               // Save on our line
-                                               this.ascent = tag.ascent;
-                                       } else {
-                                               tag.shift = this.ascent - tag.ascent;
-                                       }
-
-                                       tag = tag.next;
-                                       if (tag != null) {
-                                               tag.shift = 0;
-                                               wrap_pos = pos;
-                                       }
-                               }
-                       }
-
-                       if (this.height == 0) {
-                               this.height = tags.font.Height;
-                               tag.height = this.height;
-                       }
-
-                       if (prev_offset != offset) {
-                               retval = true;
-                       }
-                       return retval;
-               }
-               #endregion      // Internal Methods
-
-               #region Administrative
-               public int CompareTo(object obj) {
-                       if (obj == null) {
-                               return 1;
-                       }
-
-                       if (! (obj is Line)) {
-                               throw new ArgumentException("Object is not of type Line", "obj");
-                       }
-
-                       if (line_no < ((Line)obj).line_no) {
-                               return -1;
-                       } else if (line_no > ((Line)obj).line_no) {
-                               return 1;
-                       } else {
-                               return 0;
-                       }
-               }
-
-               public object Clone() {
-                       Line    clone;
-
-                       clone = new Line (document, ending);
-
-                       clone.text = text;
-
-                       if (left != null) {
-                               clone.left = (Line)left.Clone();
-                       }
-
-                       if (left != null) {
-                               clone.left = (Line)left.Clone();
-                       }
-
-                       return clone;
-               }
-
-               internal object CloneLine() {
-                       Line    clone;
-
-                       clone = new Line (document, ending);
-
-                       clone.text = text;
-
-                       return clone;
-               }
-
-               public override bool Equals(object obj) {
-                       if (obj == null) {
-                               return false;
-                       }
-
-                       if (!(obj is Line)) {
-                               return false;
-                       }
-
-                       if (obj == this) {
-                               return true;
-                       }
-
-                       if (line_no == ((Line)obj).line_no) {
-                               return true;
-                       }
-
-                       return false;
-               }
-
-               public override int GetHashCode() {
-                       return base.GetHashCode ();
-               }
-
-               public override string ToString() {
-                       return "Line " + line_no;
-               }
-
-               #endregion      // Administrative
-       }
-
        internal class Document : ICloneable, IEnumerable {
                #region Structures
                // FIXME - go through code and check for places where
@@ -803,6 +192,7 @@ namespace System.Windows.Forms {
                private StringBuilder   password_cache;
                private bool            calc_pass;
                private int             char_count;
+               private bool            enable_links;
 
                // For calculating widths/heights
                public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic);
@@ -833,14 +223,14 @@ namespace System.Windows.Forms {
 
                internal int            viewport_x;
                internal int            viewport_y;             // The visible area of the document
+               internal int            offset_x;
+               internal int            offset_y;
                internal int            viewport_width;
                internal int            viewport_height;
 
                internal int            document_x;             // Width of the document
                internal int            document_y;             // Height of the document
 
-               internal Rectangle      invalid;
-
                internal int            crlf_size;              // 1 or 2, depending on whether we use \r\n or just \n
 
                internal TextBoxBase    owner;                  // Who's owning us?
@@ -874,7 +264,7 @@ namespace System.Windows.Forms {
                        owner.HandleCreated += new EventHandler(owner_HandleCreated);
                        owner.VisibleChanged += new EventHandler(owner_VisibleChanged);
 
-                       Add (1, String.Empty, owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush (owner.ForeColor), LineEnding.None);
+                       Add (1, String.Empty, owner.Font, owner.ForeColor, LineEnding.None);
 
                        undo = new UndoManager (this);
 
@@ -895,6 +285,9 @@ namespace System.Windows.Forms {
                        viewport_x = 0;
                        viewport_y = 0;
 
+                       offset_x = 0;
+                       offset_y = 0;
+
                        crlf_size = 2;
 
                        // Default selection is empty
@@ -919,6 +312,7 @@ namespace System.Windows.Forms {
                        }
                }
 
+               // UIA: Method used via reflection in TextRangeProvider
                internal int Lines {
                        get {
                                return lines;
@@ -939,7 +333,7 @@ namespace System.Windows.Forms {
 
                internal Point Caret {
                        get {
-                               return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.X, caret.line.Y);
+                               return new Point((int)caret.tag.Line.widths[caret.pos] + caret.line.X, caret.line.Y);
                        }
                }
 
@@ -963,6 +357,14 @@ namespace System.Windows.Forms {
                        }
                }
 
+               /// <summary>
+               ///  Whether text is scanned for links
+               /// </summary>
+               internal bool EnableLinks {
+                       get { return enable_links; }
+                       set { enable_links = value; }
+               }
+
                internal string PasswordChar {
                        get {
                                return password_char;
@@ -1027,6 +429,32 @@ namespace System.Windows.Forms {
                        }
                }
 
+               internal int OffsetX
+               {
+                       get
+                       {
+                               return offset_x;
+                       }
+
+                       set
+                       {
+                               offset_x = value;
+                       }
+               }
+
+               internal int OffsetY
+               {
+                       get
+                       {
+                               return offset_y;
+                       }
+
+                       set
+                       {
+                               offset_y = value;
+                       }
+               }
+
                internal int ViewPortWidth {
                        get {
                                return viewport_width;
@@ -1082,19 +510,32 @@ namespace System.Windows.Forms {
 
                internal void UpdateMargins ()
                {
-                       if (owner.actual_border_style == BorderStyle.FixedSingle) {
-                               left_margin = 0;
-                               top_margin = 0;
-                               right_margin = 0;
-                       } else {
-                               left_margin = 2;
-                               top_margin = 2;
-                               right_margin = 2;
+                       switch (owner.actual_border_style) {
+                               case BorderStyle.None:
+                                       left_margin = 0;
+                                       top_margin = 0;
+                                       right_margin = 1;
+                                       break;
+                               case BorderStyle.FixedSingle:
+                                       left_margin = 2;
+                                       top_margin = 2;
+                                       right_margin = 3;
+                                       break;
+                               case BorderStyle.Fixed3D:
+                                       left_margin = 1;
+                                       top_margin = 1;
+                                       right_margin = 2;
+                                       break;
                        }
                }
 
                internal void SuspendRecalc ()
                {
+                       if (recalc_suspended == 0) {
+                               recalc_start = int.MaxValue;
+                               recalc_end = int.MinValue;
+                       }
+                       
                        recalc_suspended++;
                }
 
@@ -1103,8 +544,8 @@ namespace System.Windows.Forms {
                        if (recalc_suspended > 0)
                                recalc_suspended--;
 
-                       if (immediate_update && recalc_suspended == 0 && recalc_pending) {
-                               RecalculateDocument (owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
+                       if (recalc_suspended == 0 && (immediate_update || recalc_pending) && !(recalc_start == int.MaxValue && recalc_end == int.MinValue)) {
+                               RecalculateDocument (owner.CreateGraphicsInternal (), recalc_start, recalc_end, recalc_optimize);
                                recalc_pending = false;
                        }
                }
@@ -1159,15 +600,15 @@ namespace System.Windows.Forms {
                                length = 0;
                                Console.Write("   Tags: ");
                                while (tag != null) {
-                                       Console.Write("{0} <{1}>-<{2}>", count++, tag.start, tag.end
+                                       Console.Write("{0} <{1}>-<{2}>", count++, tag.Start, tag.End
                                                        /*line.text.ToString (tag.start - 1, tag.length)*/);
-                                       length += tag.length;
+                                       length += tag.Length;
 
-                                       if (tag.line != line) {
+                                       if (tag.Line != line) {
                                                Console.Write("BAD line link");
                                                throw new Exception("Bad line link in tree");
                                        }
-                                       tag = tag.next;
+                                       tag = tag.Next;
                                        if (tag != null) {
                                                Console.Write(", ");
                                        }
@@ -1218,11 +659,14 @@ namespace System.Windows.Forms {
 
                private void SetSelectionVisible (bool value)
                {
+                       bool old_selection_visible = selection_visible;
                        selection_visible = value;
 
                        // cursor and selection are enemies, we can't have both in the same room at the same time
                        if (owner.IsHandleCreated && !owner.show_caret_w_selection)
                                XplatUI.CaretVisible (owner.Handle, !selection_visible);
+                       if (UIASelectionChanged != null && (selection_visible || old_selection_visible))
+                               UIASelectionChanged (this, EventArgs.Empty);
                }
 
                private void DecrementLines(int line_no) {
@@ -1427,7 +871,11 @@ namespace System.Windows.Forms {
                                // Lineheight changed, invalidate the rest of the document
                                if ((line.Y - viewport_y) >=0 ) {
                                        // We formatted something that's in view, only draw parts of the screen
-                                       owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
+                                       owner.Invalidate(new Rectangle(
+                                               offset_x, 
+                                               line.Y - viewport_y + offset_y, 
+                                               viewport_width, 
+                                               owner.Height - (line.Y - viewport_y)));
                                } else {
                                        // The tag was above the visible area, draw everything
                                        owner.Invalidate();
@@ -1435,71 +883,327 @@ namespace System.Windows.Forms {
                        } else {
                                switch(line.alignment) {
                                        case HorizontalAlignment.Left: {
-                                               owner.Invalidate(new Rectangle(line.X + (int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, viewport_width, line.height + 1));
+                                               owner.Invalidate(new Rectangle(
+                                                       line.X + ((int)line.widths[pos] - viewport_x - 1) + offset_x, 
+                                                       line.Y - viewport_y + offset_y, 
+                                                       viewport_width, 
+                                                       line.height + 1));
+                                               break;
+                                       }
+
+                                       case HorizontalAlignment.Center: {
+                                               owner.Invalidate(new Rectangle(
+                                                       line.X + offset_x, 
+                                                       line.Y - viewport_y + offset_y, 
+                                                       viewport_width, 
+                                                       line.height + 1));
+                                               break;
+                                       }
+
+                                       case HorizontalAlignment.Right: {
+                                               owner.Invalidate(new Rectangle(
+                                                       line.X + offset_x, 
+                                                       line.Y - viewport_y + offset_y, 
+                                                       (int)line.widths[pos + 1] - viewport_x + line.X, 
+                                                       line.height + 1));
                                                break;
                                        }
+                               }
+                       }
+               }
+
+
+               // Update display from line, down line_count lines; pos is unused, but required for the signature
+               internal void UpdateView(Line line, int line_count, int pos) {
+                       if (!owner.IsHandleCreated) {
+                               return;
+                       }
+
+                       if (recalc_suspended > 0) {
+                               recalc_start = Math.Min (recalc_start, line.line_no);
+                               recalc_end = Math.Max (recalc_end, line.line_no + line_count);
+                               recalc_optimize = true;
+                               recalc_pending = true;
+                               return;
+                       }
+
+                       int start_line_top = line.Y;                    
+
+                       Line end_line = GetLine (line.line_no + line_count);
+                       if (end_line == null)
+                               end_line = GetLine (lines);
+
+                       if (end_line == null)
+                               return;
+                       
+                       int end_line_bottom = end_line.Y + end_line.height;
+                       
+                       if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count, true)) {
+                               // Lineheight changed, invalidate the rest of the document
+                               if ((line.Y - viewport_y) >=0 ) {
+                                       // We formatted something that's in view, only draw parts of the screen
+                                       owner.Invalidate(new Rectangle(
+                                               offset_x, 
+                                               line.Y - viewport_y + offset_y, 
+                                               viewport_width, 
+                                               owner.Height - (line.Y - viewport_y)));
+                               } else {
+                                       // The tag was above the visible area, draw everything
+                                       owner.Invalidate();
+                               }
+                       } else {
+                               int x = 0 - viewport_x + offset_x;
+                               int w = viewport_width;
+                               int y = Math.Min (start_line_top - viewport_y, line.Y - viewport_y) + offset_y;
+                               int h = Math.Max (end_line_bottom - y, end_line.Y + end_line.height - y);
+
+                               owner.Invalidate (new Rectangle (x, y, w, h));
+                       }
+               }
+
+               /// <summary>
+               ///  Scans the next paragraph for http:/ ftp:/ www. https:/ etc and marks the tags
+               ///  as links.
+               /// </summary>
+               /// <param name="start_line">The line to start on</param>
+               /// <param name="link_changed">marks as true if something is changed</param>
+               private void ScanForLinks (Line start_line, ref bool link_changed)
+               {
+                       Line current_line = start_line;
+                       StringBuilder line_no_breaks = new StringBuilder ();
+                       StringBuilder line_link_record = new StringBuilder ();
+                       ArrayList cumulative_length_list = new ArrayList ();
+                       bool update_caret_tag = false;
+
+                       cumulative_length_list.Add (0);
+
+                       while (current_line != null) {
+                               line_no_breaks.Append (current_line.text);
+
+                               if (link_changed == false)
+                                       current_line.LinkRecord (line_link_record);
+
+                               current_line.ClearLinks ();
+
+                               cumulative_length_list.Add (line_no_breaks.Length);
+
+                               if (current_line.ending == LineEnding.Wrap)
+                                       current_line = GetLine (current_line.LineNo + 1);
+                               else
+                                       break;
+                       }
+
+                       // search for protocols.. make sure www. is first!
+                       string [] search_terms = new string [] { "www.", "http:/", "ftp:/", "https:/" };
+                       int search_found = 0;
+                       int index_found = 0;
+                       string line_no_breaks_string = line_no_breaks.ToString ();
+                       int line_no_breaks_index = 0;
+                       int link_end = 0;
+
+                       while (true) {
+                               if (line_no_breaks_index >= line_no_breaks_string.Length)
+                                       break;
+
+                               index_found = FirstIndexOfAny (line_no_breaks_string, search_terms, line_no_breaks_index, out search_found);
+
+                               //no links found on this line
+                               if (index_found == -1)
+                                       break;
+
+                               if (search_found == 0) {
+                                       // if we are at the end of the line to analyse and the end of the line
+                                       // is "www." then there are no links here
+                                       if (line_no_breaks_string.Length == index_found + search_terms [0].Length)
+                                               break;
+
+                                       // if after www. we don't have a letter a digit or a @ or - or /
+                                       // then it is not a web address, we should continue searching
+                                       if (char.IsLetterOrDigit (line_no_breaks_string [index_found + search_terms [0].Length]) == false &&
+                                               "@/~".IndexOf (line_no_breaks_string [index_found + search_terms [0].Length].ToString ()) == -1) {
+                                               line_no_breaks_index = index_found + search_terms [0].Length;
+                                               continue;
+                                       }
+                               }
+
+                               link_end = line_no_breaks_string.Length - 1;
+                               line_no_breaks_index = line_no_breaks_string.Length;
+
+                               // we've found a link, we just need to find where it ends now
+                               for (int i = index_found + search_terms [search_found].Length; i < line_no_breaks_string.Length; i++) {
+                                       if (line_no_breaks_string [i - 1] == '.') {
+                                               if (char.IsLetterOrDigit (line_no_breaks_string [i]) == false &&
+                                                       "@/~".IndexOf (line_no_breaks_string [i].ToString ()) == -1) {
+                                                       link_end = i - 1;
+                                                       line_no_breaks_index = i;
+                                                       break;
+                                               }
+                                       } else {
+                                               if (char.IsLetterOrDigit (line_no_breaks_string [i]) == false &&
+                                                       "@-/:~.?=_&".IndexOf (line_no_breaks_string [i].ToString ()) == -1) {
+                                                       link_end = i - 1;
+                                                       line_no_breaks_index = i;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               string link_text = line_no_breaks_string.Substring (index_found, link_end - index_found + 1);
+                               int current_cumulative = 0;
+
+                               // we've found a link - index_found -> link_end
+                               // now we just make all the tags as containing link and
+                               // point them to the text for the whole link
+
+                               current_line = start_line;
+
+                               //find the line we start on
+                               for (current_cumulative = 1; current_cumulative < cumulative_length_list.Count; current_cumulative++)
+                                       if ((int)cumulative_length_list [current_cumulative] > index_found)
+                                               break;
+
+                               current_line = GetLine (start_line.LineNo + current_cumulative - 1);
+
+                               // find the tag we start on
+                               LineTag current_tag = current_line.FindTag (index_found - (int)cumulative_length_list [current_cumulative - 1] + 1);
+
+                               if (current_tag.Start != (index_found - (int)cumulative_length_list [current_cumulative - 1]) + 1) {
+                                       if (current_tag == CaretTag)
+                                               update_caret_tag = true;
+
+                                       current_tag = current_tag.Break ((index_found - (int)cumulative_length_list [current_cumulative - 1]) + 1);
+                               }
+
+                               // set the tag
+                               current_tag.IsLink = true;
+                               current_tag.LinkText = link_text;
+
+                               //go through each character
+                               // find the tag we are in
+                               // skip the number of characters in the tag
+                               for (int i = 1; i < link_text.Length; i++) {
+                                       // on to a new word-wrapped line
+                                       if ((int)cumulative_length_list [current_cumulative] <= index_found + i) {
+
+                                               current_line = GetLine (start_line.LineNo + current_cumulative++);
+                                               current_tag = current_line.FindTag (index_found + i - (int)cumulative_length_list [current_cumulative - 1] + 1);
+
+                                               current_tag.IsLink = true;
+                                               current_tag.LinkText = link_text;
+
+                                               continue;
+                                       }
 
-                                       case HorizontalAlignment.Center: {
-                                               owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, viewport_width, line.height + 1));
-                                               break;
+                                       if (current_tag.End < index_found + 1 + i - (int)cumulative_length_list [current_cumulative - 1]) {
+                                               // skip empty tags in the middle of the URL
+                                               do {
+                                                       current_tag = current_tag.Next;
+                                               } while (current_tag.Length == 0);
+
+                                               current_tag.IsLink = true;
+                                               current_tag.LinkText = link_text;
                                        }
+                               }
 
-                                       case HorizontalAlignment.Right: {
-                                               owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, (int)line.widths[pos + 1] - viewport_x + line.X, line.height + 1));
-                                               break;
+                               //if there are characters left in the tag after the link
+                               // split the tag
+                               // make the second part a non link
+                               if (current_tag.End > (index_found + link_text.Length + 1) - (int)cumulative_length_list [current_cumulative - 1]) {
+                                       if (current_tag == CaretTag)
+                                               update_caret_tag = true;
+
+                                       current_tag.Break ((index_found + link_text.Length + 1) - (int)cumulative_length_list [current_cumulative - 1]);
+                               }
+                       }
+
+                       if (update_caret_tag) {
+                               CaretTag = LineTag.FindTag (CaretLine, CaretPosition);
+                               link_changed = true;
+                       } else {
+                               if (link_changed == false) {
+                                       current_line = start_line;
+                                       StringBuilder new_link_record = new StringBuilder ();
+
+                                       while (current_line != null) {
+                                               current_line.LinkRecord (new_link_record);
+
+                                               if (current_line.ending == LineEnding.Wrap)
+                                                       current_line = GetLine (current_line.LineNo + 1);
+                                               else
+                                                       break;
                                        }
+
+                                       if (new_link_record.Equals (line_link_record) == false)
+                                               link_changed = true;
                                }
                        }
                }
 
+               private int FirstIndexOfAny (string haystack, string [] needles, int start_index, out int term_found)
+               {
+                       term_found = -1;
+                       int best_index = -1;
 
-               // Update display from line, down line_count lines; pos is unused, but required for the signature
-               internal void UpdateView(Line line, int line_count, int pos) {
-                       if (!owner.IsHandleCreated) {
-                               return;
-                       }
+                       for (int i = 0; i < needles.Length; i++) {
+                               int index = haystack.IndexOf (needles [i], start_index, StringComparison.InvariantCultureIgnoreCase);
 
-                       if (recalc_suspended > 0) {
-                               recalc_start = Math.Min (recalc_start, line.line_no);
-                               recalc_end = Math.Max (recalc_end, line.line_no + line_count);
-                               recalc_optimize = true;
-                               recalc_pending = true;
-                               return;
+                               if (index > -1) {
+                                       if (term_found > -1) {
+                                               if (index < best_index) {
+                                                       best_index = index;
+                                                       term_found = i;
+                                               }
+                                       } else {
+                                               best_index = index;
+                                               term_found = i;
+                                       }
+                               }
                        }
 
-                       int start_line_top = line.Y;                    
-
-                       int end_line_bottom;
-                       Line end_line;
+                       return best_index;
+               }
 
-                       end_line = GetLine (line.line_no + line_count);
-                       if (end_line == null)
-                               end_line = GetLine (lines);
 
 
-                       end_line_bottom = end_line.Y + end_line.height;
-                       
-                       if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count, true)) {
-                               // Lineheight changed, invalidate the rest of the document
-                               if ((line.Y - viewport_y) >=0 ) {
-                                       // We formatted something that's in view, only draw parts of the screen
-                                       owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
-                               } else {
-                                       // The tag was above the visible area, draw everything
-                                       owner.Invalidate();
-                               }
-                       } else {
-                               int x = 0 - viewport_x;
-                               int w = viewport_width;
-                               int y = Math.Min (start_line_top - viewport_y, line.Y - viewport_y);
-                               int h = Math.Max (end_line_bottom - y, end_line.Y + end_line.height - y);
+               private void InvalidateLinks (Rectangle clip)
+               {
+                       for (int i = (owner.list_links.Count - 1); i >= 0; i--) {
+                               TextBoxBase.LinkRectangle link = (TextBoxBase.LinkRectangle) owner.list_links [i];
 
-                               owner.Invalidate (new Rectangle (x, y, w, h));
+                               if (clip.IntersectsWith (link.LinkAreaRectangle))
+                                       owner.list_links.RemoveAt (i);
                        }
                }
                #endregion      // Private Methods
 
                #region Internal Methods
+
+               internal void ScanForLinks (int start, int end, ref bool link_changed)
+               {
+                       Line line = null;
+                       LineEnding lastending = LineEnding.Rich;
+
+                       // make sure we start scanning at the real begining of the line
+                       while (true) {
+                               if (start != 1 && GetLine (start - 1).ending == LineEnding.Wrap)
+                                       start--;
+                               else
+                                       break;
+                       }
+
+                       for (int i = start; i <= end && i <= lines; i++) {
+                               line = GetLine (i);
+
+                               if (lastending != LineEnding.Wrap)
+                                       ScanForLinks (line, ref link_changed);
+
+                               lastending = line.ending;
+
+                               if (lastending == LineEnding.Wrap && (i + 1) <= end)
+                                       end++;
+                       }
+               }
+
                // Clear the document and reset state
                internal void Empty() {
 
@@ -1507,7 +1211,7 @@ namespace System.Windows.Forms {
                        lines = 0;
 
                        // We always have a blank line
-                       Add (1, String.Empty, owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush (owner.ForeColor), LineEnding.None);
+                       Add (1, String.Empty, owner.Font, owner.ForeColor, LineEnding.None);
                        
                        this.RecalculateDocument(owner.CreateGraphicsInternal());
                        PositionCaret(0, 0);
@@ -1542,9 +1246,11 @@ namespace System.Windows.Forms {
 
                        if (owner.IsHandleCreated) {
                                if (owner.Focused) {
-                                       if (caret.height != caret.tag.height)
+                                       if (caret.height != caret.tag.Height)
                                                XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
-                                       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);
+                                       XplatUI.SetCaretPos(owner.Handle, 
+                                               offset_x + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x, 
+                                               offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift);
                                }
 
                                if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
@@ -1552,7 +1258,7 @@ namespace System.Windows.Forms {
 
                        // We set this at the end because we use the heights to determine whether or
                        // not we need to recreate the caret
-                       caret.height = caret.tag.height;
+                       caret.height = caret.tag.Height;
 
                }
 
@@ -1562,15 +1268,17 @@ namespace System.Windows.Forms {
                        }
 
                        caret.tag = FindCursor(x, y, out caret.pos);
-
+                       
                        MoveCaretToTextTag ();
                        
-                       caret.line = caret.tag.line;
-                       caret.height = caret.tag.height;
+                       caret.line = caret.tag.Line;
+                       caret.height = caret.tag.Height;
 
                        if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
                                XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
-                               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);
+                               XplatUI.SetCaretPos(owner.Handle, 
+                                       (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x + offset_x, 
+                                       offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift);
                        }
 
                        if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
@@ -1579,7 +1287,9 @@ namespace System.Windows.Forms {
                internal void CaretHasFocus() {
                        if ((caret.tag != null) && owner.IsHandleCreated) {
                                XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
-                               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);
+                               XplatUI.SetCaretPos(owner.Handle, 
+                                       offset_x + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x, 
+                                       offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift);
 
                                DisplayCaret ();
                        }
@@ -1596,20 +1306,38 @@ namespace System.Windows.Forms {
                        XplatUI.DestroyCaret(owner.Handle);
                }
 
-               internal void AlignCaret() {
+               internal void AlignCaret ()
+               {
+                       AlignCaret (true);
+               }
+
+               internal void AlignCaret(bool changeCaretTag) {
                        if (!owner.IsHandleCreated) {
                                return;
                        }
 
-                       caret.tag = LineTag.FindTag (caret.line, caret.pos);
+                       if (changeCaretTag) {
+                               caret.tag = LineTag.FindTag (caret.line, caret.pos);
 
-                       MoveCaretToTextTag ();
+                               MoveCaretToTextTag ();
+                       }
 
-                       caret.height = caret.tag.height;
+                       // if the caret has had SelectionFont changed to a
+                       // different height, we reflect changes unless the new
+                       // font is larger than the line (line recalculations
+                       // ignore empty tags) in which case we make it equal
+                       // the line height and then when text is entered
+                       if (caret.tag.Height > caret.tag.Line.Height) {
+                               caret.height = caret.line.height;
+                       } else {
+                               caret.height = caret.tag.Height;
+                       }
 
                        if (owner.Focused) {
                                XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
-                               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);
+                               XplatUI.SetCaretPos (owner.Handle, 
+                                       offset_x + (int) caret.tag.Line.widths [caret.pos] + caret.line.X - viewport_x, 
+                                       offset_y + caret.line.Y + viewport_y + caret_shift);
                                DisplayCaret ();
                        }
 
@@ -1623,15 +1351,17 @@ namespace System.Windows.Forms {
 
                        MoveCaretToTextTag ();
 
-                       if (caret.tag.height != caret.height) {
-                               caret.height = caret.tag.height;
+                       if (caret.tag.Height != caret.height) {
+                               caret.height = caret.tag.Height;
                                if (owner.Focused) {
                                        XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
                                }
                        }
 
                        if (owner.Focused) {
-                               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);
+                               XplatUI.SetCaretPos(owner.Handle, 
+                                       offset_x + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x, 
+                                       offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift);
                                DisplayCaret ();
                        }
                        
@@ -1666,10 +1396,10 @@ namespace System.Windows.Forms {
 
                        
 
-                       if (caret.pos < caret.tag.start) {
-                               caret.tag = caret.tag.previous;
+                       if (caret.pos < caret.tag.Start) {
+                               caret.tag = caret.tag.Previous;
                        } else {
-                               caret.tag = caret.tag.next;
+                               caret.tag = caret.tag.Next;
                        }
                }
 
@@ -1699,8 +1429,8 @@ namespace System.Windows.Forms {
                                                        caret.pos--;
                                                }
                                        } else {
-                                               if ((caret.tag.start - 1 + caret.tag.length) < caret.pos) {
-                                                       caret.tag = caret.tag.next;
+                                               if ((caret.tag.Start - 1 + caret.tag.Length) < caret.pos) {
+                                                       caret.tag = caret.tag.Next;
                                                }
                                        }
                                        UpdateCaret();
@@ -1715,8 +1445,8 @@ namespace System.Windows.Forms {
                                                // caret.pos--; // folded into the if below
                                                
                                                if (--caret.pos > 0) {
-                                                       if (caret.tag.start > caret.pos) {
-                                                               caret.tag = caret.tag.previous;
+                                                       if (caret.tag.Start > caret.pos) {
+                                                               caret.tag = caret.tag.Previous;
                                                        }
                                                }
                                        } else {
@@ -1832,7 +1562,7 @@ namespace System.Windows.Forms {
 
                                case CaretDirection.PgUp: {
 
-                                       if (viewport_y == 0 && owner.richtext) {
+                                       if (caret.line.line_no == 1 && owner.richtext) {
                                                owner.vscroll.Value = 0;
                                                Line line = GetLine (1);
                                                PositionCaret (line, 0);
@@ -1843,7 +1573,7 @@ namespace System.Windows.Forms {
                                        LineTag top = FindCursor ((int) caret.line.widths [caret.pos],
                                                        viewport_y - viewport_height, out index);
 
-                                       owner.vscroll.Value = Math.Min (top.line.Y, owner.vscroll.Maximum - viewport_height);
+                                       owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height);
                                        PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
 
                                        return;
@@ -1851,10 +1581,10 @@ namespace System.Windows.Forms {
 
                                case CaretDirection.PgDn: {
 
-                                       if (viewport_y + viewport_height >= document_y && owner.richtext) {
+                                       if (caret.line.line_no == lines && owner.richtext) {
                                                owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1;
                                                Line line = GetLine (lines);
-                                               PositionCaret (line, line.Text.Length);
+                                               PositionCaret (line, line.TextLengthWithoutEnding());
                                        }
 
                                        int y_offset = caret.line.Y - viewport_y;
@@ -1862,7 +1592,7 @@ namespace System.Windows.Forms {
                                        LineTag top = FindCursor ((int) caret.line.widths [caret.pos],
                                                        viewport_y + viewport_height, out index);
 
-                                       owner.vscroll.Value = Math.Min (top.line.Y, owner.vscroll.Maximum - viewport_height);
+                                       owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height);
                                        PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
                                        
                                        return;
@@ -1879,11 +1609,11 @@ namespace System.Windows.Forms {
                                        LineTag tag;
                                        int     index;
 
-                                       tag = FindTag(0, viewport_y + viewport_height, out index, false);
-                                       if (tag.line.line_no > 1) {
-                                               line = GetLine(tag.line.line_no - 1);
+                                       tag = FindCursor (0, viewport_y + viewport_height, out index);
+                                       if (tag.Line.line_no > 1) {
+                                               line = GetLine(tag.Line.line_no - 1);
                                        } else {
-                                               line = tag.line;
+                                               line = tag.Line;
                                        }
                                        PositionCaret(line, line.Text.Length);
                                        DisplayCaret ();
@@ -1938,16 +1668,32 @@ namespace System.Windows.Forms {
                                LineTag tag = line.tags;
                                while (tag != null) {
                                        Console.Write ("\t<tag type='{0}' span='{1}->{2}' font='{3}' color='{4}'>",
-                                                       tag.GetType (), tag.start, tag.length, tag.font, tag.color.Color);
+                                                       tag.GetType (), tag.Start, tag.Length, tag.Font, tag.Color);
                                        Console.Write (tag.Text ());
                                        Console.WriteLine ("</tag>");
-                                       tag = tag.next;
+                                       tag = tag.Next;
                                }
                                Console.WriteLine ("</line>");
                        }
                        Console.WriteLine ("</doc>");
                }
 
+               // UIA: Used via reflection by TextProviderBehavior
+               internal void GetVisibleLineIndexes (Rectangle clip, out int start, out int end)
+               {
+                       if (multiline) {
+                               /* Expand the region slightly to be sure to
+                                * paint the full extent of the line of text.
+                                * See bug 464464.
+                                */
+                               start = GetLineByPixel(clip.Top + viewport_y - offset_y - 1, false).line_no;
+                               end = GetLineByPixel(clip.Bottom + viewport_y - offset_y + 1, false).line_no;
+                       } else {
+                               start = GetLineByPixel(clip.Left + viewport_x - offset_x, false).line_no;
+                               end = GetLineByPixel(clip.Right + viewport_x - offset_x, false).line_no;
+                       }
+               }
+
                internal void Draw (Graphics g, Rectangle clip)
                {
                        Line line;              // Current line being drawn
@@ -1956,22 +1702,14 @@ namespace System.Windows.Forms {
                        int end;                // Last line to draw
                        StringBuilder text;     // String representing the current line
                        int line_no;
-                       Brush tag_brush;
-                       Brush current_brush;
-                       Brush disabled_brush;
-                       Brush readonly_brush;
-                       Brush hilight;
-                       Brush hilight_text;
+                       Color tag_color;
+                       Color current_color;
 
                        // First, figure out from what line to what line we need to draw
+                       GetVisibleLineIndexes (clip, out start, out end);
 
-                       if (multiline) {
-                               start = GetLineByPixel(clip.Top + viewport_y, false).line_no;
-                               end = GetLineByPixel(clip.Bottom + viewport_y, false).line_no;
-                       } else {
-                               start = GetLineByPixel(clip.Left + viewport_x, false).line_no;
-                               end = GetLineByPixel(clip.Right + viewport_x, false).line_no;
-                       }
+                       // remove links in the list (used for mouse down events) that are within the clip area.
+                       InvalidateLinks (clip);
 
                        ///
                        /// We draw the single border ourself
@@ -1982,8 +1720,8 @@ namespace System.Windows.Forms {
 
                        /// Make sure that we aren't drawing one more line then we need to
                        line = GetLine (end - 1);
-                       if (line != null && clip.Bottom == line.Y + line.height + viewport_y)
-                               end--;                  
+                       if (line != null && clip.Bottom == offset_y + line.Y + line.height - viewport_y)
+                               end--;
 
                        line_no = start;
 
@@ -1995,17 +1733,12 @@ namespace System.Windows.Forms {
                                Console.WriteLine ("E: {0}", GetLine (end).text);
                        #endif
 
-                       disabled_brush = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorGrayText);
-                       readonly_brush = ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControlText);
-                       hilight = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHighlight);
-                       hilight_text = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHighlightText);
-
                        // Non multiline selection can be handled outside of the loop
                        if (!multiline && selection_visible && owner.ShowSelection) {
-                               g.FillRectangle (hilight,
-                                               selection_start.line.widths [selection_start.pos] +
+                               g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight),
+                                               offset_x + selection_start.line.widths [selection_start.pos] +
                                                selection_start.line.X - viewport_x, 
-                                               selection_start.line.Y,
+                                               offset_y + selection_start.line.Y,
                                                (selection_end.line.X + selection_end.line.widths [selection_end.pos]) -
                                                (selection_start.line.X + selection_start.line.widths [selection_start.pos]), 
                                                selection_start.line.height);
@@ -2013,7 +1746,7 @@ namespace System.Windows.Forms {
 
                        while (line_no <= end) {
                                line = GetLine (line_no);
-                               float line_y = line.Y - viewport_y;
+                               float line_y = line.Y - viewport_y + offset_y;
                                
                                tag = line.tags;
                                if (!calc_pass) {
@@ -2048,69 +1781,77 @@ namespace System.Windows.Forms {
                                                line_selection_end = line_selection_start;
                                        } else if (multiline) {
                                                // lets draw some selection baby!!  (non multiline selection is drawn outside the loop)
-                                               g.FillRectangle (hilight,
-                                                               line.widths [line_selection_start - 1] + line.X - viewport_x, 
+                                               g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight),
+                                                               offset_x + line.widths [line_selection_start - 1] + line.X - viewport_x, 
                                                                line_y, line.widths [line_selection_end - 1] - line.widths [line_selection_start - 1], 
                                                                line.height);
                                        }
                                }
 
-                               current_brush = line.tags.color;
+                               current_color = line.tags.ColorToDisplay;
                                while (tag != null) {
 
                                        // Skip empty tags
-                                       if (tag.length == 0) {
-                                               tag = tag.next;
+                                       if (tag.Length == 0) {
+                                               tag = tag.Next;
                                                continue;
                                        }
 
-                                       if (((tag.X + tag.width) < (clip.Left - viewport_x)) && (tag.X > (clip.Right - viewport_x))) {
-                                               tag = tag.next;
+                                       if (((tag.X + tag.Width) < (clip.Left - viewport_x - offset_x)) && 
+                                            (tag.X > (clip.Right - viewport_x - offset_x))) {
+                                               tag = tag.Next;
                                                continue;
                                        }
 
-                                       if (tag.back_color != null) {
-                                               g.FillRectangle (tag.back_color, tag.X + line.X - viewport_x,
-                                                               line_y + tag.shift, tag.width, line.height);
+                                       if (tag.BackColor != Color.Empty) {
+                                               g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (tag.BackColor), 
+                                                               offset_x + tag.X + line.X - viewport_x,
+                                                               line_y + tag.Shift, tag.Width, line.height);
                                        }
 
-                                       tag_brush = tag.color;
-                                       current_brush = tag_brush;
+                                       tag_color = tag.ColorToDisplay;
+                                       current_color = tag_color;
 
-                                       if (!owner.is_enabled) {
-                                               Color a = ((SolidBrush) tag.color).Color;
+                                       if (!owner.Enabled) {
+                                               Color a = tag.Color;
                                                Color b = ThemeEngine.Current.ColorWindowText;
 
-                                               if ((a.R == b.R) && (a.G == b.G) && (a.B == b.B)) {
-                                                       tag_brush = disabled_brush;
-                                               }
-                                       } else if (owner.read_only && !owner.backcolor_set) {
-                                               tag_brush = readonly_brush;
-                                       }
+                                               if ((a.R == b.R) && (a.G == b.G) && (a.B == b.B))
+                                                       tag_color = ThemeEngine.Current.ColorGrayText;
+
+                                       } 
 
-                                       int tag_pos = tag.start;
-                                       current_brush = tag_brush;
-                                       while (tag_pos < tag.start + tag.length) {
+                                       int tag_pos = tag.Start;
+                                       current_color = tag_color;
+                                       while (tag_pos < tag.Start + tag.Length) {
                                                int old_tag_pos = tag_pos;
 
                                                if (tag_pos >= line_selection_start && tag_pos < line_selection_end) {
-                                                       current_brush = hilight_text;
-                                                       tag_pos = Math.Min (tag.end, line_selection_end);
+                                                       current_color = ThemeEngine.Current.ColorHighlightText;
+                                                       tag_pos = Math.Min (tag.End, line_selection_end);
                                                } else if (tag_pos < line_selection_start) {
-                                                       current_brush = tag_brush;
-                                                       tag_pos = Math.Min (tag.end, line_selection_start);
+                                                       current_color = tag_color;
+                                                       tag_pos = Math.Min (tag.End, line_selection_start);
                                                } else {
-                                                       current_brush = tag_brush;
-                                                       tag_pos = tag.end;
+                                                       current_color = tag_color;
+                                                       tag_pos = tag.End;
                                                }
 
-                                               tag.Draw (g, current_brush,
-                                                               line.X - viewport_x,
-                                                               line_y + tag.shift,
-                                                               old_tag_pos - 1, Math.Max (tag.length, tag_pos - old_tag_pos),
-                                                               text.ToString() );
+                                               Rectangle text_size;
+
+                                               tag.Draw (g, current_color,
+                                                               offset_x + line.X - viewport_x,
+                                                               line_y + tag.Shift,
+                                                               old_tag_pos - 1, Math.Min (tag.Start + tag.Length, tag_pos) - 1,
+                                                               text.ToString (), out text_size, tag.IsLink);
+
+                                               if (tag.IsLink) {
+                                                       TextBoxBase.LinkRectangle link = new TextBoxBase.LinkRectangle (text_size);
+                                                       link.LinkTag = tag;
+                                                       owner.list_links.Add (link);
+                                               }
                                        }
-                                       tag = tag.next;
+                                       tag = tag.Next;
                                }
 
                                line.DrawEnding (g, line_y);
@@ -2118,11 +1859,26 @@ namespace System.Windows.Forms {
                        }
                }
 
-               internal int GetLineEnding (string line, int start, out LineEnding ending)
+               private int GetLineEnding (string line, int start, out LineEnding ending)
                {
                        int res;
+                       int rich_index;
 
+                       if (start >= line.Length) {
+                               ending = LineEnding.Wrap;
+                               return -1;
+                       }
+                       
                        res = line.IndexOf ('\r', start);
+                       rich_index = line.IndexOf ('\n', start);
+                       
+                       // Handle the case where we find both of them, and the \n is before the \r
+                       if (res != -1 && rich_index != -1)
+                               if (rich_index < res) {
+                                       ending = LineEnding.Rich;
+                                       return rich_index;                              
+                               }
+                       
                        if (res != -1) {
                                if (res + 2 < line.Length && line [res + 1] == '\r' && line [res + 2] == '\n') {
                                        ending = LineEnding.Soft;
@@ -2136,206 +1892,185 @@ namespace System.Windows.Forms {
                                return res;
                        }
 
-                       res = line.IndexOf ('\n', start);
-                       if (res != -1) {
+                       if (rich_index != -1) {
                                ending = LineEnding.Rich;
-                               return res;
+                               return rich_index;
                        }
 
                        ending = LineEnding.Wrap;
                        return line.Length;
                }
 
-               internal int LineEndingLength (LineEnding ending)
+               // Get the line ending, but only of the types specified
+               private int GetLineEnding (string line, int start, out LineEnding ending, LineEnding type)
                {
-                       int res = 0;
+                       int index = start;
+                       int last_length = 0;
 
+                       do {
+                               index = GetLineEnding (line, index + last_length, out ending);
+                               last_length = LineEndingLength (ending);
+                       } while 
+                               ((ending & type) != ending && index != -1);
+                       
+                       return index == -1 ? line.Length : index;
+               }
+               
+               internal int LineEndingLength (LineEnding ending)
+               {
                        switch (ending) {
-                       case LineEnding.Limp:
-                       case LineEnding.Rich:
-                               res = 1;
-                               break;
-                       case LineEnding.Hard:
-                               res = 2;
-                               break;
-                       case LineEnding.Soft:
-                               res = 3;
-                               break;
+                               case LineEnding.Limp:
+                               case LineEnding.Rich:
+                                       return 1;
+                               case LineEnding.Hard:
+                                       return 2;
+                               case LineEnding.Soft:
+                                       return 3;
                        }
 
-                       return res;
+                       return 0;
                }
 
                internal string LineEndingToString (LineEnding ending)
                {
-                       string res = String.Empty;
                        switch (ending) {
-                       case LineEnding.Limp:
-                               res = "\r";
-                               break;
-                       case LineEnding.Hard:
-                               res = "\r\n";
-                               break;
-                       case LineEnding.Soft:
-                               res = "\r\r\n";
-                               break;
-                       case LineEnding.Rich:
-                               res = "\n";
-                               break;
-                       }
-                       return res;
+                               case LineEnding.Limp:
+                                       return "\r";
+                               case LineEnding.Hard:
+                                       return "\r\n";
+                               case LineEnding.Soft:
+                                       return "\r\r\n";
+                               case LineEnding.Rich:
+                                       return "\n";
+                       }
+                       
+                       return string.Empty;
                }
 
+               internal LineEnding StringToLineEnding (string ending)
+               {
+                       switch (ending) {
+                               case "\r":
+                                       return LineEnding.Limp;
+                               case "\r\n":
+                                       return LineEnding.Hard;
+                               case "\r\r\n":
+                                       return LineEnding.Soft;
+                               case "\n":
+                                       return LineEnding.Rich;
+                               default:
+                                       return LineEnding.None;
+                       }
+               }
                
-               // Insert multi-line text at the given position; use formatting at insertion point for inserted text
-               internal void Insert(Line line, int pos, bool update_caret, string s) {
+               internal void Insert (Line line, int pos, bool update_caret, string s)
+               {
+                       Insert (line, pos, update_caret, s, line.FindTag (pos));
+               }
+
+               // Insert text at the given position; use formatting at insertion point for inserted text
+               internal void Insert (Line line, int pos, bool update_caret, string s, LineTag tag)
+               {
                        int break_index;
                        int base_line;
                        int old_line_count;
                        int count = 1;
                        LineEnding ending;
-                       LineTag tag = LineTag.FindTag (line, pos);
+                       Line split_line;
                        
+                       // Don't recalculate while we mess around
                        SuspendRecalc ();
                        
                        base_line = line.line_no;
                        old_line_count = lines;
 
-                       break_index = GetLineEnding (s, 0, out ending);
+                       // Discard chars after any possible -unlikely- end of file
+                       int eof_index = s.IndexOf ('\0');
+                       if (eof_index != -1)
+                               s = s.Substring (0, eof_index);
+
+                       break_index = GetLineEnding (s, 0, out ending, LineEnding.Hard | LineEnding.Rich);
 
-                       // Bump the text at insertion point a line down if we're inserting more than one line
-                       if (break_index != s.Length) {
-                               Split (line, pos);
+                       // There are no line feeds in our text to be pasted
+                       if (break_index == s.Length) {
+                               line.InsertString (pos, s, tag);
+                       } else {
+                               // Add up to the first line feed to our current position
+                               line.InsertString (pos, s.Substring (0, break_index + LineEndingLength (ending)), tag);
+                               
+                               // Split the rest of the original line to a new line
+                               Split (line, pos + (break_index + LineEndingLength (ending)));
                                line.ending = ending;
-                               // Remainder of start line is now in base_line + 1
-                       }
+                               break_index += LineEndingLength (ending);
+                               split_line = GetLine (line.line_no + 1);
+                               
+                               // Insert brand new lines for any more line feeds in the inserted string
+                               while (true) {
+                                       int next_break = GetLineEnding (s, break_index, out ending, LineEnding.Hard | LineEnding.Rich);
+                                       
+                                       if (next_break == s.Length)
+                                               break;
+                                               
+                                       string line_text = s.Substring (break_index, next_break - break_index +
+                                                       LineEndingLength (ending));
 
-                       InsertString (line, pos, s.Substring (0, break_index + LineEndingLength (ending)));
-                       
-                       break_index += LineEndingLength (ending);
-                       while (break_index < s.Length) {
-                               int next_break = GetLineEnding (s, break_index, out ending);
-                               string line_text = s.Substring (break_index, next_break - break_index +
-                                               LineEndingLength (ending));
+                                       Add (base_line + count, line_text, line.alignment, tag.Font, tag.Color, ending);
 
-                               Add (base_line + count, line_text, line.alignment, tag.font, tag.color, ending);
+                                       Line last = GetLine (base_line + count);
+                                       last.ending = ending;
 
-                               Line last = GetLine (base_line + count);
-                               last.ending = ending;
+                                       count++;
+                                       break_index = next_break + LineEndingLength (ending);
+                               }
 
-                               count++;
-                               break_index = next_break + LineEndingLength (ending);
+                               // Add the remainder of the insert text to the split
+                               // part of the original line
+                               split_line.InsertString (0, s.Substring (break_index));
                        }
+                       
+                       // Allow the document to recalculate things
+                       ResumeRecalc (false);
 
-                       ResumeRecalc (true);
+                       // Update our character count
+                       CharCount += s.Length;
 
-                       UpdateView(line, lines - old_line_count + 1, pos);
+                       UpdateView (line, lines - old_line_count + 1, pos);
 
+                       // Move the caret to the end of the inserted text if requested
                        if (update_caret) {
-                               // Move caret to the end of the inserted text
                                Line l = GetLine (line.line_no + lines - old_line_count);
-                               PositionCaret(l, l.text.Length);
+                               PositionCaret (l, l.text.Length);
                                DisplayCaret ();
                        }
                }
 
-               // Inserts a character at the given position
-               internal void InsertString(Line line, int pos, string s) {
-                       InsertString(line.FindTag(pos), pos, s);
-               }
-
                // Inserts a string at the given position
-               internal void InsertString(LineTag tag, int pos, string s) {
-                       Line    line;
-                       int     len;
-
-                       len = s.Length;
-
-                       CharCount += len;
-
-                       line = tag.line;
-                       line.text.Insert(pos, s);
-
-                       tag = tag.next;
-                       while (tag != null) {
-                               tag.start += len;
-                               tag = tag.next;
-                       }
-                       line.Grow(len);
-                       line.recalc = true;
-
-                       UpdateView(line, pos);
-               }
-
-               // Inserts a string at the caret position
-               internal void InsertStringAtCaret(string s, bool move_caret) {
-
-                       InsertString (caret.tag, caret.pos, s);
-
-                       UpdateView(caret.line, caret.pos);
-                       if (move_caret) {
-                               caret.pos += s.Length;
-                               UpdateCaret();
-                       }
-               }
-
-
-
-               // Inserts a character at the given position
-               internal void InsertChar(Line line, int pos, char ch) {
-                       InsertChar(line.FindTag(pos), pos, ch);
-               }
-
-               // Inserts a character at the given position
-               internal void InsertChar(LineTag tag, int pos, char ch) {
-                       Line    line;
-
-                       CharCount++;
-
-                       line = tag.line;
-                       line.text.Insert(pos, ch);
-
-                       tag = tag.next;
-                       while (tag != null) {
-                               tag.start++;
-                               tag = tag.next;
-                       }
-                       line.Grow(1);
-                       line.recalc = true;
+               internal void InsertString (Line line, int pos, string s)
+               {
+                       // Update our character count
+                       CharCount += s.Length;
 
-                       undo.RecordTyping (line, pos, ch);
-                       UpdateView(line, pos);
+                       // Insert the text into the Line
+                       line.InsertString (pos, s);
                }
 
                // Inserts a character at the current caret position
-               internal void InsertCharAtCaret(char ch, bool move_caret) {
-                       /*
-                       LineTag tag;
+               internal void InsertCharAtCaret (char ch, bool move_caret)
+               {
+                       caret.line.InsertString (caret.pos, ch.ToString(), caret.tag);
 
+                       // Update our character count
                        CharCount++;
-
-                       caret.line.text.Insert(caret.pos, ch);
-                       caret.tag.length++;
                        
-                       if (caret.tag.next != null) {
-                               tag = caret.tag.next;
-                               while (tag != null) {
-                                       tag.start++;
-                                       tag = tag.next;
-                               }
-                       }
-                       caret.line.Grow(1);
-                       caret.line.recalc = true;
-                       */
-                       InsertChar (caret.tag, caret.pos, ch);
+                       undo.RecordTyping (caret.line, caret.pos, ch);
 
-                       UpdateView(caret.line, caret.pos);
+                       UpdateView (caret.line, caret.pos);
+                       
                        if (move_caret) {
                                caret.pos++;
-                               UpdateCaret();
-                               SetSelectionToCaret(true);
+                               UpdateCaret ();
+                               SetSelectionToCaret (true);
                        }
-
                }
                
                internal void InsertPicture (Line line, int pos, RTF.Picture picture)
@@ -2354,23 +2089,23 @@ namespace System.Windows.Forms {
                        tag = LineTag.FindTag (line, pos);
                        picture_tag.CopyFormattingFrom (tag);
                        /*next_tag = */tag.Break (pos + 1);
-                       picture_tag.previous = tag;
-                       picture_tag.next = tag.next;
-                       tag.next = picture_tag;
+                       picture_tag.Previous = tag;
+                       picture_tag.Next = tag.Next;
+                       tag.Next = picture_tag;
 
                        //
                        // Picture tags need to be surrounded by text tags
                        //
-                       if (picture_tag.next == null) {
-                               picture_tag.next = new LineTag (line, pos + 1);
-                               picture_tag.next.CopyFormattingFrom (tag);
-                               picture_tag.next.previous = picture_tag;
+                       if (picture_tag.Next == null) {
+                               picture_tag.Next = new LineTag (line, pos + 1);
+                               picture_tag.Next.CopyFormattingFrom (tag);
+                               picture_tag.Next.Previous = picture_tag;
                        }
 
-                       tag = picture_tag.next;
+                       tag = picture_tag.Next;
                        while (tag != null) {
-                               tag.start += len;
-                               tag = tag.next;
+                               tag.Start += len;
+                               tag = tag.Next;
                        }
 
                        line.Grow (len);
@@ -2395,12 +2130,12 @@ namespace System.Windows.Forms {
                        SuspendUpdate ();
 
                        if (start.line == end.line) {
-                               DeleteChars (start.tag, pos, end.pos - pos);
+                               DeleteChars (start.line, pos, end.pos - pos);
                        } else {
 
                                // Delete first and last lines
-                               DeleteChars (start.tag, start.pos, start.line.text.Length - start.pos);
-                               DeleteChars (end.line.tags, 0, end.pos);
+                               DeleteChars (start.line, start.pos, start.line.text.Length - start.pos);
+                               DeleteChars (end.line, 0, end.pos);
 
                                int current = start.line.line_no + 1;
                                if (current < end.line.line_no) {
@@ -2422,88 +2157,17 @@ namespace System.Windows.Forms {
                
                // Deletes n characters at the given position; it will not delete past line limits
                // pos is 0-based
-               internal void DeleteChars(LineTag tag, int pos, int count) {
-                       Line    line;
-                       bool    streamline;
-
-                       streamline = false;
-                       line = tag.line;
-
+               public void DeleteChars (Line line, int pos, int count)
+               {
+                       // Reduce our character count
                        CharCount -= count;
+                       
+                       line.DeleteCharacters (pos, count);
 
-                       if (pos == line.text.Length) {
-                               return;
-                       }
-
-                       line.text.Remove(pos, count);
-
-                       // Make sure the tag points to the right spot
-                       while ((tag != null) && (tag.end) < pos) {
-                               tag = tag.next;
-                       }
-
-                       if (tag == null) {
-                               goto Cleanup;
-                       }
-
-                       // Check if we're crossing tag boundaries
-                       if ((pos + count) > (tag.start + tag.length - 1)) {
-                               int     left;
-
-                               // We have to delete cross tag boundaries
-                               streamline = true;
-                               left = count;
-
-                               left -= tag.start + tag.length - pos - 1;
-
-                               tag = tag.next;
-                               while ((tag != null) && (left > 0)) {
-                                       tag.start -= count - left;
-
-                                       if (tag.length > left) {
-                                               left = 0;
-                                       } else {
-                                               left -= tag.length;
-                                               tag = tag.next;
-                                       }
-
-                               }
-                       } else {
-                               // We got off easy, same tag
-
-                               if (tag.length == 0) {
-                                       streamline = true;
-                               }
-                       }
-
-                       // Delete empty orphaned tags at the end
-                       LineTag walk = tag;
-                       while (walk != null && walk.next != null && walk.next.length == 0) {
-                               LineTag t = walk;
-                               walk.next = walk.next.next;
-                               if (walk.next != null)
-                                       walk.next.previous = t;
-                               walk = walk.next;
-                       }
-
-                       // Adjust the start point of any tags following
-                       if (tag != null) {
-                               tag = tag.next;
-                               while (tag != null) {
-                                       tag.start -= count;
-                                       tag = tag.next;
-                               }
-                       }
-
-                       line.recalc = true;
-                       if (streamline) {
-                               line.Streamline(lines);
-                       }
-
-               Cleanup:
                        if (pos >= line.TextLengthWithoutEnding ()) {
                                LineEnding ending = line.ending;
                                GetLineEnding (line.text.ToString (), 0, out ending);
+                               
                                if (ending != line.ending) {
                                        line.ending = ending;
 
@@ -2518,95 +2182,26 @@ namespace System.Windows.Forms {
                                UpdateView (line, lines, pos);
                                owner.Invalidate ();
                        } else 
-                               UpdateView(line, pos);
+                               UpdateView (line, pos);
                }
 
                // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
-               internal void DeleteChar(LineTag tag, int pos, bool forward) {
-                       Line    line;
-                       bool    streamline;
-
-                       CharCount--;
-
-                       streamline = false;
-                       line = tag.line;
-
-                       if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true)) {
+               public void DeleteChar (Line line, int pos, bool forward)
+               {
+                       if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true))
                                return;
-                       }
-
+                       
+                       undo.BeginUserAction ("Delete");
 
                        if (forward) {
-                               line.text.Remove(pos, 1);
-
-                               while ((tag != null) && (tag.start + tag.length - 1) <= pos) {
-                                       tag = tag.next;
-                               }
-
-                               if (tag == null) {
-                                       goto Cleanup;
-                               }
-
-                               //      tag.length--;
-
-                               if (tag.length == 0) {
-                                       streamline = true;
-                               }
+                               undo.RecordDeleteString (line, pos, line, pos + 1);
+                               DeleteChars (line, pos, 1);
                        } else {
-                               pos--;
-                               line.text.Remove(pos, 1);
-                               if (pos >= (tag.start - 1)) {
-                                       //              tag.length--;
-                                       if (tag.length == 0) {
-                                               streamline = true;
-                                       }
-                               } else if (tag.previous != null) {
-                                       //              tag.previous.length--;
-                                       if (tag.previous.length == 0) {
-                                               streamline = true;
-                                       }
-                               }
+                               undo.RecordDeleteString (line, pos - 1, line, pos);
+                               DeleteChars (line, pos - 1, 1);
                        }
 
-                       // Delete empty orphaned tags at the end
-                       LineTag walk = tag;
-                       while (walk != null && walk.next != null && walk.next.length == 0) {
-                               LineTag t = walk;
-                               walk.next = walk.next.next;
-                               if (walk.next != null)
-                                       walk.next.previous = t;
-                               walk = walk.next;
-                       }
-
-                       tag = tag.next;
-                       while (tag != null) {
-                               tag.start--;
-                               tag = tag.next;
-                       }
-                       line.recalc = true;
-                       if (streamline) {
-                               line.Streamline(lines);
-                       }
-
-               Cleanup:
-                       if (pos >= line.TextLengthWithoutEnding ()) {
-                               LineEnding ending = line.ending;
-                               GetLineEnding (line.text.ToString (), 0, out ending);
-                               if (ending != line.ending) {
-                                       line.ending = ending;
-
-                                       if (!multiline) {
-                                               UpdateView (line, lines, pos);
-                                               owner.Invalidate ();
-                                               return;
-                                       }
-                               }
-                       }
-                       if (!multiline) {
-                               UpdateView (line, lines, pos);
-                               owner.Invalidate ();
-                       } else 
-                               UpdateView(line, pos);
+                       undo.EndUserAction ();
                }
 
                // Combine two lines
@@ -2627,21 +2222,21 @@ namespace System.Windows.Forms {
                        // Maintain the line ending style
                        first.ending = second.ending;
 
-                       while (last.next != null) {
-                               last = last.next;
+                       while (last.Next != null) {
+                               last = last.Next;
                        }
 
                        // need to get the shift before setting the next tag since that effects length
-                       shift = last.start + last.length - 1;
-                       last.next = second.tags;
-                       last.next.previous = last;
+                       shift = last.Start + last.Length - 1;
+                       last.Next = second.tags;
+                       last.Next.Previous = last;
 
                        // Fix up references within the chain
-                       last = last.next;
+                       last = last.Next;
                        while (last != null) {
-                               last.line = first;
-                               last.start += shift;
-                               last = last.next;
+                               last.Line = first;
+                               last.Start += shift;
+                               last = last.Next;
                        }
 
                        // Combine both lines' strings
@@ -2711,7 +2306,7 @@ namespace System.Windows.Forms {
                }
 
                ///<summary>Split line at given tag and position into two lines</summary>
-               ///if more space becomes available on previous line</param>
+               ///if more space becomes available on previous line
                internal void Split(Line line, LineTag tag, int pos) {
                        LineTag new_tag;
                        Line    new_line;
@@ -2723,6 +2318,13 @@ namespace System.Windows.Forms {
                        move_sel_start = false;
                        move_sel_end = false;
 
+#if DEBUG
+                       SanityCheck();
+
+                       if (tag.End < pos)
+                               throw new Exception ("Split called with the wrong tag");
+#endif
+
                        // Adjust selection and cursors
                        if (caret.line == line && caret.pos >= pos) {
                                move_caret = true;
@@ -2737,7 +2339,7 @@ namespace System.Windows.Forms {
 
                        // cover the easy case first
                        if (pos == line.text.Length) {
-                               Add (line.line_no + 1, String.Empty, line.alignment, tag.font, tag.color, line.ending);
+                               Add (line.line_no + 1, String.Empty, line.alignment, tag.Font, tag.Color, line.ending);
 
                                new_line = GetLine (line.line_no + 1);
                                
@@ -2745,6 +2347,10 @@ namespace System.Windows.Forms {
                                        caret.line = new_line;
                                        caret.tag = new_line.tags;
                                        caret.pos = 0;
+
+                                       if (selection_visible == false) {
+                                               SetSelectionToCaret (true);
+                                       }
                                }
 
                                if (move_sel_start) {
@@ -2758,11 +2364,15 @@ namespace System.Windows.Forms {
                                        selection_end.pos = 0;
                                        selection_end.tag = new_line.tags;
                                }
+
+#if DEBUG
+                               SanityCheck ();
+#endif
                                return;
                        }
 
                        // We need to move the rest of the text into the new line
-                       Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.font, tag.color, line.ending);
+                       Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.Font, tag.Color, line.ending);
 
                        // Now transfer our tags from this line to the next
                        new_line = GetLine(line.line_no + 1);
@@ -2770,50 +2380,59 @@ namespace System.Windows.Forms {
                        line.recalc = true;
                        new_line.recalc = true;
 
-                       if ((tag.start - 1) == pos) {
+                       //make sure that if we are at the end of a tag, we start on the begining
+                       //of a new one, if one exists... Stops us creating an empty tag and
+                       //make the operation easier.
+                       if (tag.Next != null && (tag.Next.Start - 1) == pos)
+                               tag = tag.Next;
+
+                       if ((tag.Start - 1) == pos) {
                                int     shift;
 
                                // We can simply break the chain and move the tag into the next line
+
+                               // if the tag we are moving is the first, create an empty tag
+                               // for the line we are leaving behind
                                if (tag == line.tags) {
                                        new_tag = new LineTag(line, 1);
                                        new_tag.CopyFormattingFrom (tag);
                                        line.tags = new_tag;
                                }
 
-                               if (tag.previous != null) {
-                                       tag.previous.next = null;
+                               if (tag.Previous != null) {
+                                       tag.Previous.Next = null;
                                }
                                new_line.tags = tag;
-                               tag.previous = null;
-                               tag.line = new_line;
+                               tag.Previous = null;
+                               tag.Line = new_line;
 
                                // Walk the list and correct the start location of the tags we just bumped into the next line
-                               shift = tag.start - 1;
+                               shift = tag.Start - 1;
 
                                new_tag = tag;
                                while (new_tag != null) {
-                                       new_tag.start -= shift;
-                                       new_tag.line = new_line;
-                                       new_tag = new_tag.next;
+                                       new_tag.Start -= shift;
+                                       new_tag.Line = new_line;
+                                       new_tag = new_tag.Next;
                                }
                        } else {
                                int     shift;
 
                                new_tag = new LineTag (new_line, 1);                    
-                               new_tag.next = tag.next;
+                               new_tag.Next = tag.Next;
                                new_tag.CopyFormattingFrom (tag);
                                new_line.tags = new_tag;
-                               if (new_tag.next != null) {
-                                       new_tag.next.previous = new_tag;
+                               if (new_tag.Next != null) {
+                                       new_tag.Next.Previous = new_tag;
                                }
-                               tag.next = null;
+                               tag.Next = null;
 
                                shift = pos;
-                               new_tag = new_tag.next;
+                               new_tag = new_tag.Next;
                                while (new_tag != null) {
-                                       new_tag.start -= shift;
-                                       new_tag.line = new_line;
-                                       new_tag = new_tag.next;
+                                       new_tag.Start -= shift;
+                                       new_tag.Line = new_line;
+                                       new_tag = new_tag.Next;
 
                                }
                        }
@@ -2822,12 +2441,21 @@ namespace System.Windows.Forms {
                                caret.line = new_line;
                                caret.pos = caret.pos - pos;
                                caret.tag = caret.line.FindTag(caret.pos);
+
+                               if (selection_visible == false) {
+                                       SetSelectionToCaret (true);
+                                       move_sel_start = false;
+                                       move_sel_end = false;
+                               }
                        }
 
                        if (move_sel_start) {
                                selection_start.line = new_line;
                                selection_start.pos = selection_start.pos - pos;
-                               selection_start.tag = new_line.FindTag(selection_start.pos);
+                               if  (selection_start.Equals(selection_end))
+                                       selection_start.tag = new_line.FindTag(selection_start.pos);
+                               else
+                                       selection_start.tag = new_line.FindTag (selection_start.pos + 1);
                        }
 
                        if (move_sel_end) {
@@ -2838,16 +2466,44 @@ namespace System.Windows.Forms {
 
                        CharCount -= line.text.Length - pos;
                        line.text.Remove(pos, line.text.Length - pos);
+#if DEBUG
+                       SanityCheck ();
+#endif
+               }
+
+#if DEBUG
+               private void SanityCheck () {
+                       for (int i = 1; i < lines; i++) {
+                               LineTag tag = GetLine (i).tags;
+
+                               if (tag.Start != 1)
+                                       throw new Exception ("Line doesn't start at the begining");
+
+                               int start = 1;
+                               tag = tag.Next;
+
+                               while (tag != null) {
+                                       if (tag.Start == start)
+                                               throw new Exception ("Empty tag!");
+
+                                       if (tag.Start < start)
+                                               throw new Exception ("Insane!!");
+
+                                       start = tag.Start;
+                                       tag = tag.Next;
+                               }
+                       }
                }
+#endif
 
                // Adds a line of text, with given font.
                // Bumps any line at that line number that already exists down
-               internal void Add (int LineNo, string Text, Font font, SolidBrush color, LineEnding ending)
+               internal void Add (int LineNo, string Text, Font font, Color color, LineEnding ending)
                {
                        Add (LineNo, Text, alignment, font, color, ending);
                }
 
-               internal void Add (int LineNo, string Text, HorizontalAlignment align, Font font, SolidBrush color, LineEnding ending)
+               internal void Add (int LineNo, string Text, HorizontalAlignment align, Font font, Color color, LineEnding ending)
                {
                        Line    add;
                        Line    line;
@@ -2917,22 +2573,22 @@ namespace System.Windows.Forms {
                        return clone;
                }
 
-               internal void Delete(int LineNo) {
+               private void Delete (int LineNo)
+               {
                        Line    line;
 
-                       if (LineNo>lines) {
+                       if (LineNo > lines)
                                return;
-                       }
 
-                       line = GetLine(LineNo);
+                       line = GetLine (LineNo);
 
                        CharCount -= line.text.Length;
 
-                       DecrementLines(LineNo + 1);
-                       Delete(line);
+                       DecrementLines (LineNo + 1);
+                       Delete (line);
                }
 
-               internal void Delete(Line line1) {
+               private void Delete(Line line1) {
                        Line    line2;// = new Line();
                        Line    line3;
 
@@ -2999,8 +2655,8 @@ namespace System.Windows.Forms {
 
                                tag = line1.tags;
                                while (tag != null) {
-                                       tag.line = line1;
-                                       tag = tag.next;
+                                       tag.Line = line1;
+                                       tag = tag.Next;
                                }
                        }
 
@@ -3010,6 +2666,11 @@ namespace System.Windows.Forms {
                        this.lines--;
                }
 
+               // Invalidates the start line until the end of the viewstate
+               internal void InvalidateLinesAfter (Line start) {
+                       owner.Invalidate (new Rectangle (0, start.Y - viewport_y, viewport_width, viewport_height - start.Y));
+               }
+
                // Invalidate a section of the document to trigger redraw
                internal void Invalidate(Line start, int start_pos, Line end, int end_pos) {
                        Line    l1;
@@ -3071,9 +2732,9 @@ namespace System.Windows.Forms {
                                #endif
 
                                owner.Invalidate(new Rectangle (
-                                       (int)l1.widths[p1] + l1.X - viewport_x, 
-                                       l1.Y - viewport_y, 
-                                       endpoint - (int)l1.widths[p1] + 1, 
+                                       offset_x + (int)l1.widths[p1] + l1.X - viewport_x, 
+                                       offset_y + l1.Y - viewport_y,
+                                       endpoint - (int) l1.widths [p1] + 1, 
                                        l1.height));
                                return;
                        }
@@ -3085,7 +2746,11 @@ namespace System.Windows.Forms {
 
                        // Three invalidates:
                        // First line from start
-                       owner.Invalidate(new Rectangle((int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
+                       owner.Invalidate(new Rectangle(
+                               offset_x + (int)l1.widths[p1] + l1.X - viewport_x, 
+                               offset_y + l1.Y - viewport_y, 
+                               viewport_width, 
+                               l1.height));
 
                        
                        // lines inbetween
@@ -3093,7 +2758,11 @@ namespace System.Windows.Forms {
                                int     y;
 
                                y = GetLine(l1.line_no + 1).Y;
-                               owner.Invalidate(new Rectangle(0, y - viewport_y, viewport_width, l2.Y - y));
+                               owner.Invalidate(new Rectangle(
+                                       offset_x, 
+                                       offset_y + y - viewport_y, 
+                                       viewport_width, 
+                                       l2.Y - y));
 
                                #if Debug
                                        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);
@@ -3102,10 +2771,14 @@ namespace System.Windows.Forms {
                        
 
                        // Last line to end
-                       owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height));
+                       owner.Invalidate(new Rectangle(
+                               offset_x + (int)l2.widths[0] + l2.X - viewport_x, 
+                               offset_y + l2.Y - viewport_y, 
+                               (int)l2.widths[p2] + 1, 
+                               l2.height));
+
                        #if Debug
                                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);
-
                        #endif
                }
 
@@ -3135,7 +2808,7 @@ namespace System.Windows.Forms {
                                                } else {
                                                        selection_start.line = selection_anchor.line;
                                                        selection_start.pos = selection_anchor.height;
-                                                       selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
+                                                       selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height + 1);
 
                                                        selection_end.line = caret.line;
                                                        selection_end.tag = caret.line.tags;
@@ -3166,7 +2839,7 @@ namespace System.Windows.Forms {
                                                }
                                                if (caret < selection_anchor) {
                                                        selection_start.line = caret.line;
-                                                       selection_start.tag = caret.line.FindTag(start_pos);
+                                                       selection_start.tag = caret.line.FindTag(start_pos + 1);
                                                        selection_start.pos = start_pos;
 
                                                        selection_end.line = selection_anchor.line;
@@ -3181,7 +2854,7 @@ namespace System.Windows.Forms {
                                                } else {
                                                        selection_start.line = selection_anchor.line;
                                                        selection_start.pos = selection_anchor.height;
-                                                       selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
+                                                       selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height + 1);
 
                                                        selection_end.line = caret.line;
                                                        selection_end.tag = caret.line.FindTag(end_pos);
@@ -3239,7 +2912,7 @@ namespace System.Windows.Forms {
                                                this.Invalidate(selection_start.line, start_pos, caret.line, end_pos);
 
                                                selection_start.line = caret.line;
-                                               selection_start.tag = caret.line.FindTag(start_pos);
+                                               selection_start.tag = caret.line.FindTag(start_pos + 1);
                                                selection_start.pos = start_pos;
 
                                                selection_end.line = caret.line;
@@ -3499,11 +3172,11 @@ namespace System.Windows.Forms {
                                start = selection_start.line.line_no;
                                end = selection_end.line.line_no;
 
-                               sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos) + Environment.NewLine);
+                               sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos));
 
                                if ((start + 1) < end) {
                                        for (i = start + 1; i < end; i++) {
-                                               sb.Append(GetLine(i).text.ToString() + Environment.NewLine);
+                                               sb.Append(GetLine(i).text.ToString());
                                        }
                                }
 
@@ -3516,7 +3189,6 @@ namespace System.Windows.Forms {
                internal void ReplaceSelection(string s, bool select_new) {
                        int             i;
 
-                       int selection_pos_on_line = selection_start.pos;
                        int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos);
                        SuspendRecalc ();
 
@@ -3525,10 +3197,10 @@ namespace System.Windows.Forms {
                                if (selection_start.line == selection_end.line) {
                                        undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
 
-                                       DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
+                                       DeleteChars (selection_start.line, selection_start.pos, selection_end.pos - selection_start.pos);
 
                                        // The tag might have been removed, we need to recalc it
-                                       selection_start.tag = selection_start.line.FindTag(selection_start.pos);
+                                       selection_start.tag = selection_start.line.FindTag(selection_start.pos + 1);
                                } else {
                                        int             start;
                                        int             end;
@@ -3538,14 +3210,14 @@ namespace System.Windows.Forms {
 
                                        undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
 
-                                       InvalidateSelectionArea ();
+                                       InvalidateLinesAfter(selection_start.line);
 
                                        // Delete first line
-                                       DeleteChars(selection_start.tag, selection_start.pos, selection_start.line.text.Length - selection_start.pos);
+                                       DeleteChars (selection_start.line, selection_start.pos, selection_start.line.text.Length - selection_start.pos);
                                        selection_start.line.recalc = true;
 
                                        // Delete last line
-                                       DeleteChars(selection_end.line.tags, 0, selection_end.pos);
+                                       DeleteChars(selection_end.line, 0, selection_end.pos);
 
                                        start++;
                                        if (start < end) {
@@ -3567,6 +3239,9 @@ namespace System.Windows.Forms {
                        undo.RecordInsertString (selection_start.line, selection_start.pos, s);
                        ResumeRecalc (false);
 
+                       Line begin_update_line = selection_start.line;
+                       int begin_update_pos = selection_start.pos;
+                       
                        if (!select_new) {
                                CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line,
                                                out selection_start.tag, out selection_start.pos);
@@ -3594,7 +3269,7 @@ namespace System.Windows.Forms {
                        }
 
                        PositionCaret (selection_start.line, selection_start.pos);
-                       UpdateView (selection_start.line, selection_pos_on_line);
+                       UpdateView (begin_update_line, selection_end.line.line_no - begin_update_line.line_no, begin_update_pos);
                }
 
                internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
@@ -3617,13 +3292,13 @@ namespace System.Windows.Forms {
                                        tag = line.tags;
 
                                        while (tag != null) {
-                                               if (index < (start + tag.start + tag.length - 1)) {
+                                               if (index < (start + tag.Start + tag.Length - 1)) {
                                                        line_out = line;
                                                        tag_out = LineTag.GetFinalTag (tag);
                                                        pos = index - start;
                                                        return;
                                                }
-                                               if (tag.next == null) {
+                                               if (tag.Next == null) {
                                                        Line    next_line;
 
                                                        next_line = GetLine(line.line_no + 1);
@@ -3640,15 +3315,15 @@ namespace System.Windows.Forms {
                                                                return;
                                                        }
                                                }
-                                               tag = tag.next;
+                                               tag = tag.Next;
                                        }
                                }
                        }
 
                        line_out = GetLine(lines);
                        tag = line_out.tags;
-                       while (tag.next != null) {
-                               tag = tag.next;
+                       while (tag.Next != null) {
+                               tag = tag.Next;
                        }
                        tag_out = tag;
                        pos = line_out.text.Length;
@@ -3706,6 +3381,8 @@ namespace System.Windows.Forms {
                }
 
 
+               // UIA: Method used via reflection in TextRangeProvider
+
                /// <summary>Give it a Line number and it returns the Line object at with that line number</summary>
                internal Line GetLine(int LineNo) {
                        Line    line = document;
@@ -3727,22 +3404,22 @@ namespace System.Windows.Forms {
                internal LineTag PreviousTag(LineTag tag) {
                        Line l; 
 
-                       if (tag.previous != null) {
-                               return tag.previous;
+                       if (tag.Previous != null) {
+                               return tag.Previous;
                        }
 
                        // Next line 
-                       if (tag.line.line_no == 1) {
+                       if (tag.Line.line_no == 1) {
                                return null;
                        }
 
-                       l = GetLine(tag.line.line_no - 1);
+                       l = GetLine(tag.Line.line_no - 1);
                        if (l != null) {
                                LineTag t;
 
                                t = l.tags;
-                               while (t.next != null) {
-                                       t = t.next;
+                               while (t.Next != null) {
+                                       t = t.Next;
                                }
                                return t;
                        }
@@ -3754,12 +3431,12 @@ namespace System.Windows.Forms {
                internal LineTag NextTag(LineTag tag) {
                        Line l;
 
-                       if (tag.next != null) {
-                               return tag.next;
+                       if (tag.Next != null) {
+                               return tag.Next;
                        }
 
                        // Next line
-                       l = GetLine(tag.line.line_no + 1);
+                       l = GetLine(tag.Line.line_no + 1);
                        if (l != null) {
                                return l.tags;
                        }
@@ -3768,9 +3445,15 @@ namespace System.Windows.Forms {
                }
 
                internal Line ParagraphStart(Line line) {
-                       while (line.ending == LineEnding.Wrap) {
-                               line = GetLine(line.line_no - 1);
-                       }
+                       Line lastline = line;
+                       do {
+                               if (line.line_no <= 1)
+                                       break;
+
+                               line = lastline;
+                               lastline = GetLine (line.line_no - 1);
+                       } while (lastline.ending == LineEnding.Wrap);
+
                        return line;
                }       
 
@@ -3824,95 +3507,33 @@ namespace System.Windows.Forms {
                        return last;
                }
 
-               // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
-               internal LineTag FindTag(int x, int y, out int index, bool exact) {
-                       Line    line;
-                       LineTag tag;
-
-                       line = GetLineByPixel(y, exact);
-                       if (line == null) {
-                               index = 0;
-                               return null;
-                       }
-                       tag = line.tags;
-
-                       // Alignment adjustment
-                       x += line.X;
-
-                       while (true) {
-                               if (x >= tag.X && x < (tag.X+tag.width)) {
-                                       int     end;
-
-                                       end = tag.start + tag.length - 1;
-
-                                       for (int pos = tag.start; pos < end; pos++) {
-                                               if (x < line.widths[pos]) {
-                                                       index = pos;
-                                                       return LineTag.GetFinalTag (tag);
-                                               }
-                                       }
-                                       index=end;
-                                       return LineTag.GetFinalTag (tag);
-                               }
-                               if (tag.next != null) {
-                                       tag = tag.next;
-                               } else {
-                                       if (exact) {
-                                               index = 0;
-                                               return null;
-                                       }
+               // UIA: Method used via reflection in TextProviderBehavior
 
-                                       index = line.text.Length;
-                                       return LineTag.GetFinalTag (tag);
-                               }
-                       }
-               }
+               // Give it x/y pixel coordinates and it returns the Tag at that position
+               internal LineTag FindCursor (int x, int y, out int index)
+               {
+                       Line line;
 
-               // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
-               internal LineTag FindCursor(int x, int y, out int index) {
-                       Line    line;
-                       LineTag tag;
+                       x -= offset_x;
+                       y -= offset_y;
 
-                       line = GetLineByPixel(multiline ? y : x, false);
-                       tag = line.tags;
+                       line = GetLineByPixel (multiline ? y : x, false);
 
-                       /// Special case going leftwards of the first tag
-                       if (x < tag.X) {
+                       LineTag tag = line.GetTag (x);
+                               
+                       if (tag.Length == 0 && tag.Start == 1)
                                index = 0;
-                               return LineTag.GetFinalTag (tag);
-                       }
-
-                       while (true) {
-                               if (x >= tag.X && x < (tag.X+tag.width)) {
-                                       int     end;
-
-                                       end = tag.TextEnd;
-
-                                       for (int pos = tag.start - 1; pos < end; pos++) {
-                                               // When clicking on a character, we position the cursor to whatever edge
-                                               // of the character the click was closer
-                                               if (x < (line.X + line.widths[pos] + ((line.widths[pos+1]-line.widths[pos])/2))) {
-                                                       index = pos;
-                                                       return LineTag.GetFinalTag (tag);
-                                               }
-                                       }
-                                       index=end;
-                                       return LineTag.GetFinalTag (tag);
-                               }
-                               if (tag.next != null) {
-                                       tag = tag.next;
-                               } else {
-                                       index = line.TextLengthWithoutEnding ();
-                                       return LineTag.GetFinalTag (tag);
-                               }
-                       }
+                       else
+                               index = tag.GetCharIndex (x - line.align_shift);
+                       
+                       return tag;
                }
 
                /// <summary>Format area of document in specified font and color</summary>
                /// <param name="start_pos">1-based start position on start_line</param>
                /// <param name="end_pos">1-based end position on end_line </param>
                internal void FormatText (Line start_line, int start_pos, Line end_line, int end_pos, Font font,
-                               SolidBrush color, SolidBrush back_color, FormatSpecified specified)
+                               Color color, Color back_color, FormatSpecified specified)
                {
                        Line    l;
 
@@ -3932,6 +3553,9 @@ namespace System.Windows.Forms {
                        } else {
                                // Special case, single line
                                LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color, back_color, specified);
+                               
+                               if ((end_pos - start_pos) == 0 && CaretTag.Length != 0)
+                                       CaretTag = CaretTag.Next;
                        }
                }
 
@@ -3999,6 +3623,7 @@ namespace System.Windows.Forms {
                        }
 
                        // Fixup the positions, they can go kinda nuts
+                       // (this is suspend and resume recalc - they set them to 1 and max)
                        start = Math.Max (start, 1);
                        end = Math.Min (end, lines);
 
@@ -4016,6 +3641,7 @@ namespace System.Windows.Forms {
                                line = GetLine(line_no++);
                                line.offset = offset;
 
+                               // if we are not calculating a password
                                if (!calc_pass) {
                                        if (!optimize) {
                                                line.RecalculateLine(g, this);
@@ -4080,6 +3706,12 @@ namespace System.Windows.Forms {
                                        HeightChanged(this, null);
                                }
                        }
+
+                       // scan for links and tell us if its all
+                       // changed, so we can update everything
+                       if (EnableLinks)
+                               ScanForLinks (start, end, ref changed);
+
                        UpdateCaret();
                        return changed;
                }
@@ -4378,8 +4010,8 @@ namespace System.Windows.Forms {
                        } else {
                                mark.line = GetLine(lines);
                                mark.tag = mark.line.tags;
-                               while (mark.tag.next != null) {
-                                       mark.tag = mark.tag.next;
+                               while (mark.tag.Next != null) {
+                                       mark.tag = mark.tag.Next;
                                }
                                mark.pos = mark.line.text.Length;
                        }
@@ -4391,6 +4023,7 @@ namespace System.Windows.Forms {
                internal event EventHandler WidthChanged;
                internal event EventHandler HeightChanged;
                internal event EventHandler LengthChanged;
+               internal event EventHandler UIASelectionChanged;
                #endregion      // Events
 
                #region Administrative
@@ -4442,7 +4075,7 @@ namespace System.Windows.Forms {
                        get { return false; }
                }
 
-               internal override SizeF SizeOfPosition (Graphics dc, int pos)
+               public override SizeF SizeOfPosition (Graphics dc, int pos)
                {
                        return picture.Size;
                }
@@ -4452,14 +4085,14 @@ namespace System.Windows.Forms {
                        return (int) (picture.Height + 0.5F);
                }
 
-               internal override void Draw (Graphics dc, Brush brush, float xoff, float y, int start, int end)
+               public override void Draw (Graphics dc, Color color, float xoff, float y, int start, int end)
                {
-                       picture.DrawImage (dc, xoff + line.widths [start], y, false);
+                       picture.DrawImage (dc, xoff + Line.widths [start], y, false);
                }
 
-               internal override void Draw (Graphics dc, Brush brush, float xoff, float y, int start, int end, string text)
+               public override void Draw (Graphics dc, Color color, float xoff, float y, int start, int end, string text)
                {
-                       picture.DrawImage (dc, xoff + + line.widths [start], y, false);
+                       picture.DrawImage (dc, xoff + + Line.widths [start], y, false);
                }
 
                public override string Text ()
@@ -4468,354 +4101,6 @@ namespace System.Windows.Forms {
                }
        }
 
-       internal class LineTag {
-               #region Local Variables;
-               // Payload; formatting
-               internal Font           font;           // System.Drawing.Font object for this tag
-               internal SolidBrush     color;          // The font color for this tag
-
-               // In 2.0 tags can have background colours.  I'm not going to #ifdef
-               // at this level though since I want to reduce code paths
-               internal SolidBrush back_color;  
-
-               // Payload; text
-               internal int            start;          // start, in chars; index into Line.text
-               internal bool           r_to_l;         // Which way is the font
-
-               // Drawing support
-               internal int            height;         // Height in pixels of the text this tag describes
-
-               internal int            ascent;         // Ascent of the font for this tag
-               internal int            shift;          // Shift down for this tag, to stay on baseline
-
-               // Administrative
-               internal Line           line;           // The line we're on
-               internal LineTag        next;           // Next tag on the same line
-               internal LineTag        previous;       // Previous tag on the same line
-               #endregion;
-
-               #region Constructors
-               internal LineTag(Line line, int start) {
-                       this.line = line;
-                       this.start = start;
-               }
-               #endregion      // Constructors
-
-               #region Internal Methods
-
-               public float X {
-                       get {
-                               if (start == 0)
-                                       return line.X;
-                               return line.X + line.widths [start - 1];
-                       }
-               }
-
-               public int end {
-                       get { return start + length; }
-               }
-
-               public int TextEnd {
-                       get { return start + TextLength; }
-               }
-
-               public float width {
-                       get {
-                               if (length == 0)
-                                       return 0;
-                               return line.widths [start + length - 1] - (start != 0 ? line.widths [start - 1] : 0);
-                       }
-               }
-
-               public int length {
-                       get {
-                               int res = 0;
-                               if (next != null)
-                                       res = next.start - start;
-                               else
-                                       res = line.text.Length - (start - 1);
-
-                               return res > 0 ? res : 0;
-                       }
-               }
-
-               public int TextLength {
-                       get {
-                               int res = 0;
-                               if (next != null)
-                                       res = next.start - start;
-                               else
-                                       res = line.TextLengthWithoutEnding () - (start - 1);
-
-                               return res > 0 ? res : 0;
-                       }
-               }
-
-               public virtual bool IsTextTag {
-                       get { return true; }
-               }
-
-               internal virtual SizeF SizeOfPosition (Graphics dc, int pos)
-               {
-                       if (pos >= line.TextLengthWithoutEnding () && line.document.multiline)
-                               return SizeF.Empty;
-
-                       string text = line.text.ToString (pos, 1);
-                       switch ((int) text [0]) {
-                       case '\t':
-                               if (!line.document.multiline)
-                                       goto case 10;
-                               SizeF res = dc.MeasureString (" ", font, 10000, Document.string_format);
-                               res.Width *= 8.0F;
-                               return res;
-                       case 10:
-                       case 13:
-                               return dc.MeasureString ("\u0013", font, 10000, Document.string_format);
-                       }
-                       
-                       return dc.MeasureString (text, font, 10000, Document.string_format);
-               }
-
-               internal virtual int MaxHeight ()
-               {
-                       return font.Height;
-               }
-
-               internal virtual void Draw (Graphics dc, Brush brush, float x, float y, int start, int end)
-               {
-                       dc.DrawString (line.text.ToString (start, end), font, brush, x, y, StringFormat.GenericTypographic);
-               }
-
-               internal virtual void Draw (Graphics dc, Brush brush, float xoff, float y, int start, int end, string text)
-               {
-                       while (start < end) {
-                               int tab_index = text.IndexOf ("\t", start);
-                               if (tab_index == -1)
-                                       tab_index = end;
-                               dc.DrawString (text.Substring (start, tab_index - start), font, brush, xoff + line.widths [start],
-                                               y, StringFormat.GenericTypographic);
-
-                               // non multilines get the unknown char 
-                               if (!line.document.multiline && tab_index != end)
-                                       dc.DrawString ("\u0013", font, brush,  xoff + line.widths [tab_index], y, Document.string_format);
-                                       
-                               start = tab_index + 1;
-                       }
-               }
-
-               ///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at &gt;pos&lt; or null if end-of-line</summary>
-               internal LineTag Break(int pos) {
-
-                       LineTag new_tag;
-
-                       // Sanity
-                       if (pos == this.start) {
-                               return this;
-                       } else if (pos >= (start + length)) {
-                               return null;
-                       }
-
-                       new_tag = new LineTag(line, pos);
-                       new_tag.CopyFormattingFrom (this);
-
-                       new_tag.next = this.next;
-                       this.next = new_tag;
-                       new_tag.previous = this;
-
-                       if (new_tag.next != null) {
-                               new_tag.next.previous = new_tag;
-                       }
-
-                       return new_tag;
-               }
-
-               public virtual string Text ()
-               {
-                       return line.text.ToString (start - 1, length);
-               }
-
-               public void CopyFormattingFrom (LineTag other)
-               {
-                       height = other.height;
-                       font = other.font;
-                       color = other.color;
-                       back_color = other.back_color;
-               }
-
-               /// <summary>Applies 'font' and 'brush' to characters starting at 'start' for 'length' chars; 
-               /// Removes any previous tags overlapping the same area; 
-               /// returns true if lineheight has changed</summary>
-               /// <param name="start">1-based character position on line</param>
-               internal static bool FormatText(Line line, int start, int length, Font font, SolidBrush color, SolidBrush back_color, FormatSpecified specified)
-               {
-                       LineTag tag;
-                       LineTag start_tag;
-                       LineTag end_tag;
-                       int     end;
-                       bool    retval = false;         // Assume line-height doesn't change
-
-                       // Too simple?
-                       if (((FormatSpecified.Font & specified) == FormatSpecified.Font) && font.Height != line.height) {
-                               retval = true;
-                       }
-                       line.recalc = true;             // This forces recalculation of the line in RecalculateDocument
-
-                       // A little sanity, not sure if it's needed, might be able to remove for speed
-                       if (length > line.text.Length) {
-                               length = line.text.Length;
-                       }
-
-                       tag = line.tags;
-                       end = start + length;
-
-                       // Common special case
-                       if ((start == 1) && (length == tag.length)) {
-                               tag.ascent = 0;
-                               SetFormat (tag, font, color, back_color, specified);
-                               return retval;
-                       }
-
-                       start_tag = FindTag (line, start);
-                       tag = start_tag.Break (start);
-
-                       while (tag != null && tag.end <= end) {
-                               SetFormat (tag, font, color, back_color, specified);
-                               tag = tag.next;
-                       }
-
-                       if (tag != null && tag.end == end)
-                               return retval;
-
-                       /// Now do the last tag
-                       end_tag = FindTag (line, end);
-
-                       if (end_tag != null) {
-                               end_tag.Break (end);
-                               SetFormat (end_tag, font, color, back_color, specified);
-                       }
-
-                       return retval;
-               }
-
-               private static void SetFormat (LineTag tag, Font font, SolidBrush color, SolidBrush back_color, FormatSpecified specified)
-               {
-                       if ((FormatSpecified.Font & specified) == FormatSpecified.Font)
-                               tag.font = font;
-                       if ((FormatSpecified.Color & specified) == FormatSpecified.Color)
-                               tag.color = color;
-                       if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor) {
-                               tag.back_color = back_color;
-                       }
-                       // Console.WriteLine ("setting format:   {0}  {1}   new color {2}", color.Color, specified, tag.color.Color);
-               }
-
-               /// <summary>Finds the tag that describes the character at position 'pos' on 'line'</summary>
-               internal static LineTag FindTag(Line line, int pos) {
-                       LineTag tag = line.tags;
-
-                       // Beginning of line is a bit special
-                       if (pos == 0) {
-                               // Not sure if we should get the final tag here
-                               return tag;
-                       }
-
-                       while (tag != null) {
-                               if ((tag.start <= pos) && (pos <= tag.end)) {
-                                       return GetFinalTag (tag);
-                               }
-
-                               tag = tag.next;
-                       }
-
-                       return null;
-               }
-
-               // There can be multiple tags at the same position, we want to make
-               // sure we are using the very last tag at the given position
-               internal static LineTag GetFinalTag (LineTag tag)
-               {
-                       LineTag res = tag;
-
-                       while (res.length == 0 && res.next != null && res.next.length == 0)
-                               res = res.next;
-
-                       return res;
-               }
-
-               /// <summary>Combines 'this' tag with 'other' tag</summary>
-               internal bool Combine(LineTag other) {
-                       if (!this.Equals(other)) {
-                               return false;
-                       }
-
-                       this.next = other.next;
-                       if (this.next != null) {
-                               this.next.previous = this;
-                       }
-
-                       return true;
-               }
-
-
-               /// <summary>Remove 'this' tag ; to be called when formatting is to be removed</summary>
-               internal bool Remove() {
-                       if ((this.start == 1) && (this.next == null)) {
-                               // We cannot remove the only tag
-                               return false;
-                       }
-                       if (this.start != 1) {
-                               this.previous.next = this.next;
-                               this.next.previous = this.previous;
-                       } else {
-                               this.next.start = 1;
-                               this.line.tags = this.next;
-                               this.next.previous = null;
-                       }
-                       return true;
-               }
-
-
-               /// <summary>Checks if 'this' tag describes the same formatting options as 'obj'</summary>
-               public override bool Equals(object obj) {
-                       LineTag other;
-
-                       if (obj == null) {
-                               return false;
-                       }
-
-                       if (!(obj is LineTag)) {
-                               return false;
-                       }
-
-                       if (obj == this) {
-                               return true;
-                       }
-
-                       other = (LineTag)obj;
-
-                       if (other.IsTextTag != IsTextTag)
-                               return false;
-
-                       if (this.font.Equals(other.font) && this.color.Equals(other.color)) {   // FIXME add checking for things like link or type later
-                               return true;
-                       }
-
-                       return false;
-               }
-
-               public override int GetHashCode() {
-                       return base.GetHashCode ();
-               }
-
-               public override string ToString() {
-                       if (length > 0)
-                               return GetType () + " Tag starts at index " + this.start + " length " + this.length + " text: " + Text () + "Font " + this.font.ToString();
-                       return "Zero Lengthed tag at index " + this.start;
-               }
-
-               #endregion      // Internal Methods
-       }
-
        internal class UndoManager {
 
                internal enum ActionType {
@@ -4899,16 +4184,13 @@ namespace System.Windows.Forms {
                        redo_actions.Clear();
                }
 
-               internal void Undo ()
+               internal bool Undo ()
                {
                        Action action;
                        bool user_action_finished = false;
 
                        if (undo_actions.Count == 0)
-                               return;
-
-                       // Nuke the redo queue
-                       redo_actions.Clear ();
+                               return false;
 
                        locked = true;
                        do {
@@ -4960,18 +4242,17 @@ namespace System.Windows.Forms {
                        } while (!user_action_finished && undo_actions.Count > 0);
 
                        locked = false;
+
+                       return true;
                }
 
-               internal void Redo ()
+               internal bool Redo ()
                {
                        Action action;
                        bool user_action_finished = false;
 
                        if (redo_actions.Count == 0)
-                               return;
-
-                       // You can't undo anything after redoing
-                       undo_actions.Clear ();
+                               return false;
 
                        locked = true;
                        do {
@@ -4979,6 +4260,7 @@ namespace System.Windows.Forms {
                                int start_index;
 
                                action = (Action) redo_actions.Pop ();
+                               undo_actions.Push (action);
 
                                switch (action.type) {
 
@@ -5032,6 +4314,8 @@ namespace System.Windows.Forms {
                        } while (!user_action_finished && redo_actions.Count > 0);
 
                        locked = false;
+
+                       return true;
                }
                #endregion      // Internal Methods
 
@@ -5042,6 +4326,9 @@ namespace System.Windows.Forms {
                        if (locked)
                                return;
 
+                       // Nuke the redo queue
+                       redo_actions.Clear ();
+
                        Action ua = new Action ();
                        ua.type = ActionType.UserActionBegin;
                        ua.data = name;
@@ -5066,6 +4353,9 @@ namespace System.Windows.Forms {
                        if (locked)
                                return;
 
+                       // Nuke the redo queue
+                       redo_actions.Clear ();
+
                        Action  a = new Action ();
 
                        // We cant simply store the string, because then formatting would be lost
@@ -5082,6 +4372,9 @@ namespace System.Windows.Forms {
                        if (locked || str.Length == 0)
                                return;
 
+                       // Nuke the redo queue
+                       redo_actions.Clear ();
+
                        Action a = new Action ();
 
                        a.type = ActionType.InsertString;
@@ -5097,6 +4390,9 @@ namespace System.Windows.Forms {
                        if (locked)
                                return;
 
+                       // Nuke the redo queue
+                       redo_actions.Clear ();
+
                        Action a = null;
 
                        if (undo_actions.Count > 0)
@@ -5154,19 +4450,19 @@ namespace System.Windows.Forms {
                                line.text = new StringBuilder (current.text.ToString (start, end - start));
 
                                // Copy tags from start to start+length onto new line
-                               current_tag = current.FindTag (start);
-                               while ((current_tag != null) && (current_tag.start <= end)) {
-                                       if ((current_tag.start <= start) && (start < (current_tag.start + current_tag.length))) {
+                               current_tag = current.FindTag (start + 1);
+                               while ((current_tag != null) && (current_tag.Start <= end)) {
+                                       if ((current_tag.Start <= start) && (start < (current_tag.Start + current_tag.Length))) {
                                                // start tag is within this tag
                                                tag_start = start;
                                        } else {
-                                               tag_start = current_tag.start;
+                                               tag_start = current_tag.Start;
                                        }
 
                                        tag = new LineTag(line, tag_start - start + 1);
                                        tag.CopyFormattingFrom (current_tag);
 
-                                       current_tag = current_tag.next;
+                                       current_tag = current_tag.Next;
 
                                        // Add the new tag to the line
                                        if (line.tags == null) {
@@ -5175,11 +4471,11 @@ namespace System.Windows.Forms {
                                                LineTag tail;
                                                tail = line.tags;
 
-                                               while (tail.next != null) {
-                                                       tail = tail.next;
+                                               while (tail.Next != null) {
+                                                       tail = tail.Next;
                                                }
-                                               tail.next = tag;
-                                               tag.previous = tail;
+                                               tail.Next = tag;
+                                               tag.Previous = tail;
                                        }
                                }
 
@@ -5218,21 +4514,21 @@ namespace System.Windows.Forms {
                                //Insert our tags at the end
                                tag = line.tags;
 
-                               while (tag.next != null) {
-                                       tag = tag.next;
+                               while (tag.Next != null) {
+                                       tag = tag.Next;
                                }
 
-                               offset = tag.start + tag.length - 1;
+                               offset = tag.Start + tag.Length - 1;
 
-                               tag.next = insert.tags;
+                               tag.Next = insert.tags;
                                line.text.Insert(offset, insert.text.ToString());
 
                                // Adjust start locations
-                               tag = tag.next;
+                               tag = tag.Next;
                                while (tag != null) {
-                                       tag.start += offset;
-                                       tag.line = line;
-                                       tag = tag.next;
+                                       tag.Start += offset;
+                                       tag.Line = line;
+                                       tag = tag.Next;
                                }
                                // Put it back together
                                document.Combine(line.line_no, line.line_no + 1);
@@ -5259,20 +4555,20 @@ namespace System.Windows.Forms {
                                        tag = line.tags;
 
                                        
-                                       if (tag != null && tag.length != 0) {
-                                               while (tag.next != null) {
-                                                       tag = tag.next;
+                                       if (tag != null && tag.Length != 0) {
+                                               while (tag.Next != null) {
+                                                       tag = tag.Next;
                                                }
-                                               offset = tag.start + tag.length - 1;
-                                               tag.next = current.tags;
-                                               tag.next.previous = tag;
+                                               offset = tag.Start + tag.Length - 1;
+                                               tag.Next = current.tags;
+                                               tag.Next.Previous = tag;
 
-                                               tag = tag.next;
+                                               tag = tag.Next;
 
                                        } else {
                                                offset = 0;
                                                line.tags = current.tags;
-                                               line.tags.previous = null;
+                                               line.tags.Previous = null;
                                                tag = line.tags;
                                        }
 
@@ -5281,16 +4577,16 @@ namespace System.Windows.Forms {
                                        document.Split(line.line_no, 0);
                                        offset = 0;
                                        line.tags = current.tags;
-                                       line.tags.previous = null;
+                                       line.tags.Previous = null;
                                        line.ending = current.ending;
                                        tag = line.tags;
                                }
 
                                // Adjust start locations and line pointers
                                while (tag != null) {
-                                       tag.start += offset - 1;
-                                       tag.line = line;
-                                       tag = tag.next;
+                                       tag.Start += offset - 1;
+                                       tag.Line = line;
+                                       tag = tag.Next;
                                }
 
                                line.text.Insert(offset, current.text.ToString());
@@ -5300,7 +4596,7 @@ namespace System.Windows.Forms {
                                line = document.GetLine(line.line_no + 1);
 
                                // FIXME? Test undo of line-boundaries
-                               if ((current.right == null) && (current.tags.length != 0)) {
+                               if ((current.right == null) && (current.tags.Length != 0)) {
                                        document.Combine(line.line_no - 1, line.line_no);
                                }
                                current = current.right;