2007-10-01 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextControl.cs
index 1661f9d2c9955d69c38efdb55a39e68798c00505..62e5ef4bc8c113c4d70624e73bec2acb78e1b762 100644 (file)
@@ -30,7 +30,7 @@
 // the time to write it all yet.
 // Stuff missing (in no particular order):
 // - Align text after RecalculateLine
-// - Implement tag types for hotlinks, images, etc.
+// - Implement tag types for hotlinks, etc.
 // - Implement CaretPgUp/PgDown
 
 // NOTE:
@@ -50,6 +50,7 @@ using System.Collections;
 using System.Drawing;
 using System.Drawing.Text;
 using System.Text;
+using RTF=System.Windows.Forms.RTF;
 
 namespace System.Windows.Forms {
        internal enum LineColor {
@@ -63,19 +64,13 @@ namespace System.Windows.Forms {
                Line            // Selection=Line under caret
        }
 
-       internal class FontDefinition {
-               internal String         face;
-               internal int            size;
-               internal FontStyle      add_style;
-               internal FontStyle      remove_style;
-               internal Color          color;
-               internal Font           font_obj;
+       [Flags]
+       internal enum FormatSpecified {
+               None,
 
-               internal FontDefinition() {
-                       face = null;
-                       size = 0;
-                       color = Color.Empty;
-               }
+               BackColor = 2,
+               Font = 4,
+               Color = 8,
        }
 
        internal enum CaretDirection {
@@ -99,25 +94,37 @@ namespace System.Windows.Forms {
                CharBackNoWrap      // Move a char backward, but don't wrap onto the previous line
        }
 
+       internal enum LineEnding {
+               Wrap,    // line wraps to the next line
+               Limp,    // \r
+               Hard,    // \r\n
+               Soft,    // \r\r\n
+               Rich,    // \n
+
+               None
+       }
+       
        // 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                    Y;                      // Baseline
+               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 bool                   soft_break;             // Tag is 'broken soft' and continuation from previous line
                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 bool carriage_return;
+               internal LineEnding ending;
 
 
                // Stuff that's important for the tree
@@ -126,57 +133,63 @@ namespace System.Windows.Forms {
                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 static StringFormat    string_format;          // For calculating widths/heights
                internal bool                   recalc;                 // Line changed
                #endregion      // Local Variables
 
                #region Constructors
-               internal Line() {
+               internal Line (Document document, LineEnding ending)
+               {
+                       this.document = document; 
                        color = LineColor.Red;
                        left = null;
                        right = null;
                        parent = null;
                        text = null;
                        recalc = true;
-                       soft_break = false;
-                       alignment = HorizontalAlignment.Left;
+                       alignment = document.alignment;
 
-                       if (string_format == null) {
-                               string_format = new StringFormat(StringFormat.GenericTypographic);
-                               string_format.Trimming = StringTrimming.None;
-                               string_format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
-                       }
+                       this.ending = ending;
                }
 
-               internal Line(int LineNo, string Text, Font font, Brush color) : this() {
+               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, text.Length);
+
+                       
+                       tags = new LineTag(this, 1);
                        tags.font = font;
-                       tags.color = color;
+                       tags.color = color;                             
                }
 
-               internal Line(int LineNo, string Text, HorizontalAlignment align, Font font, Brush color) : this() {
+               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, text.Length);
+
+                       
+                       tags = new LineTag(this, 1);
                        tags.font = font;
                        tags.color = color;
                }
 
-               internal Line(int LineNo, string Text, LineTag tag) : this() {
+               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];
@@ -186,6 +199,33 @@ namespace System.Windows.Forms {
                #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;
@@ -276,6 +316,45 @@ namespace System.Windows.Forms {
                #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;
@@ -306,14 +385,19 @@ namespace System.Windows.Forms {
                        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
-                       while ((current.length == 0) && (next != null)) {
+                       // 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;
@@ -321,7 +405,7 @@ namespace System.Windows.Forms {
 
                        while (next != null) {
                                // Take out 0 length tags unless it's the last tag in the document
-                               if (next.length == 0) {
+                               if (current.IsTextTag && next.length == 0 && next.IsTextTag) {
                                        if ((next.next != null) || (line_no != lines)) {
                                                current.next = next.next;
                                                if (current.next != null) {
@@ -381,13 +465,11 @@ namespace System.Windows.Forms {
                        tag = this.tags;
                        ascent = 0;
                        tag.shift = 0;
-                       tag.width = 0;
 
                        this.recalc = false;
-                       widths[0] = indent;
-                       tag.X = indent;
+                       widths[0] = document.left_margin + indent;
 
-                       w = g.MeasureString(doc.password_char, tags.font, 10000, string_format).Width;
+                       w = g.MeasureString(doc.password_char, tags.font, 10000, Document.string_format).Width;
 
                        if (this.height != (int)tag.font.Height) {
                                ret = true;
@@ -402,7 +484,6 @@ namespace System.Windows.Forms {
                        this.ascent = tag.ascent;
 
                        while (pos < len) {
-                               tag.width += w;
                                pos++;
                                widths[pos] = widths[pos-1] + w;
                        }
@@ -420,26 +501,24 @@ namespace System.Windows.Forms {
                        int     len;
                        SizeF   size;
                        float   w;
-                       int     prev_height;
+                       int     prev_offset;
                        bool    retval;
                        bool    wrapped;
                        Line    line;
                        int     wrap_pos;
-                       float   wrap_width;
 
                        pos = 0;
                        len = this.text.Length;
                        tag = this.tags;
-                       prev_height = this.height;      // For drawing optimization calculations
+                       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;
-                       tag.width = 0;
 
-                       if (this.soft_break) {
-                               widths[0] = hanging_indent;
+                       if (ending == LineEnding.Wrap) {
+                               widths[0] = document.left_margin + hanging_indent;
                        } else {
-                               widths[0] = indent;
+                               widths[0] = document.left_margin + indent;
                        }
 
                        this.recalc = false;
@@ -447,62 +526,58 @@ namespace System.Windows.Forms {
                        wrapped = false;
 
                        wrap_pos = 0;
-                       wrap_width = 0;
 
                        while (pos < len) {
-                               size = g.MeasureString(this.text.ToString(pos, 1), tag.font, 10000, string_format);
 
                                while (tag.length == 0) {       // We should always have tags after a tag.length==0 unless len==0
-                                       tag.width = 0;
                                        tag.ascent = 0;
-                                       if (tag.previous != null) {
-                                               tag.X = tag.previous.X;
-                                       } else {
-                                               tag.X = (int)widths[pos];
-                                       }
-                                       tag = tag.next;
-                                       tag.width = 0;
                                        tag.shift = 0;
+                                       tag = tag.next;
                                }
 
+                               size = tag.SizeOfPosition (g, pos);
                                w = size.Width;
 
                                if (Char.IsWhiteSpace(text[pos])) {
                                        wrap_pos = pos + 1;
-                                       wrap_width = tag.width + w;
                                }
 
                                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;
-                                               tag.width = wrap_width;
-                                               doc.Split(this, tag, pos, this.soft_break);
-                                               this.soft_break = true;
+                                               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)) {
+                                       }  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
-                                               tag.width = tag.width + w;
-                                               doc.Split(this, tag, pos, this.soft_break);
-                                               this.soft_break = true;
+
+                                               // 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 soft lines that follow back into our line
+                               // Contract all wrapped lines that follow back into our line
                                if (!wrapped) {
-                                       tag.width += w;
-
                                        pos++;
 
                                        widths[pos] = widths[pos-1] + w;
 
                                        if (pos == len) {
                                                line = doc.GetLine(this.line_no + 1);
-                                               if ((line != null) && soft_break) {
+                                               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;
@@ -513,7 +588,7 @@ namespace System.Windows.Forms {
 
                                if (pos == (tag.start-1 + tag.length)) {
                                        // We just found the end of our current tag
-                                       tag.height = (int)tag.font.Height;
+                                       tag.height = tag.MaxHeight ();
 
                                        // Check if we're the tallest on the line (so far)
                                        if (tag.height > this.height) {
@@ -530,9 +605,8 @@ namespace System.Windows.Forms {
                                                LineTag         t;
 
                                                // We have a tag that has a taller ascent than the line;
-
                                                t = tags;
-                                               while (t != tag) {
+                                               while (t != null && t != tag) {
                                                        t.shift = tag.ascent - t.ascent;
                                                        t = t.next;
                                                }
@@ -543,19 +617,10 @@ namespace System.Windows.Forms {
                                                tag.shift = this.ascent - tag.ascent;
                                        }
 
-                                       // Update our horizontal starting pixel position
-                                       if (tag.previous == null) {
-                                               tag.X = (int)widths[0];
-                                       } else {
-                                               tag.X = tag.previous.X + (int)tag.previous.width;
-                                       }
-
                                        tag = tag.next;
                                        if (tag != null) {
-                                               tag.width = 0;
                                                tag.shift = 0;
                                                wrap_pos = pos;
-                                               wrap_width = tag.width;
                                        }
                                }
                        }
@@ -565,7 +630,7 @@ namespace System.Windows.Forms {
                                tag.height = this.height;
                        }
 
-                       if (prev_height != this.height) {
+                       if (prev_offset != offset) {
                                retval = true;
                        }
                        return retval;
@@ -594,7 +659,7 @@ namespace System.Windows.Forms {
                public object Clone() {
                        Line    clone;
 
-                       clone = new Line();
+                       clone = new Line (document, ending);
 
                        clone.text = text;
 
@@ -612,7 +677,7 @@ namespace System.Windows.Forms {
                internal object CloneLine() {
                        Line    clone;
 
-                       clone = new Line();
+                       clone = new Line (document, ending);
 
                        clone.text = text;
 
@@ -739,16 +804,24 @@ namespace System.Windows.Forms {
                private bool            calc_pass;
                private int             char_count;
 
-               private bool            no_recalc;
+               // For calculating widths/heights
+               public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic);
+
+               private int             recalc_suspended;
                private bool            recalc_pending;
-               private int             recalc_start;
+               private int             recalc_start = 1;   // This starts at one, since lines are 1 based
                private int             recalc_end;
                private bool            recalc_optimize;
 
+               private int             update_suspended;
+               private bool update_pending;
+               private int update_start = 1;
+
                internal bool           multiline;
+               internal HorizontalAlignment alignment;
                internal bool           wrap;
 
-               internal UndoClass      undo;
+               internal UndoManager    undo;
 
                internal Marker         caret;
                internal Marker         selection_start;
@@ -773,10 +846,15 @@ namespace System.Windows.Forms {
                internal TextBoxBase    owner;                  // Who's owning us?
                static internal int     caret_width = 1;
                static internal int     caret_shift = 1;
+
+               internal int left_margin = 2;  // A left margin for all lines
+               internal int top_margin = 2;
+               internal int right_margin = 2;
                #endregion      // Local Variables
 
                #region Constructors
-               internal Document(TextBoxBase owner) {
+               internal Document (TextBoxBase owner)
+               {
                        lines = 0;
 
                        this.owner = owner;
@@ -784,11 +862,10 @@ namespace System.Windows.Forms {
                        multiline = true;
                        password_char = "";
                        calc_pass = false;
-                       no_recalc = false;
                        recalc_pending = false;
 
                        // Tree related stuff
-                       sentinel = new Line();
+                       sentinel = new Line (this, LineEnding.None);
                        sentinel.color = LineColor.Black;
 
                        document = sentinel;
@@ -797,11 +874,9 @@ namespace System.Windows.Forms {
                        owner.HandleCreated += new EventHandler(owner_HandleCreated);
                        owner.VisibleChanged += new EventHandler(owner_VisibleChanged);
 
-                       Add(1, "", owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush(owner.ForeColor));
-                       Line l = GetLine (1);
-                       l.soft_break = true;
+                       Add (1, String.Empty, owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush (owner.ForeColor), LineEnding.None);
 
-                       undo = new UndoClass(this);
+                       undo = new UndoManager (this);
 
                        selection_visible = false;
                        selection_start.line = this.document;
@@ -825,6 +900,11 @@ namespace System.Windows.Forms {
                        // Default selection is empty
 
                        document_id = random.Next();
+
+                       string_format.Trimming = StringTrimming.None;
+                       string_format.FormatFlags = StringFormatFlags.DisplayFormatControl;
+
+                       UpdateMargins ();
                }
                #endregion
 
@@ -859,7 +939,7 @@ namespace System.Windows.Forms {
 
                internal Point Caret {
                        get {
-                               return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.align_shift, caret.line.Y);
+                               return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.X, caret.line.Y);
                        }
                }
 
@@ -890,22 +970,23 @@ namespace System.Windows.Forms {
 
                        set {
                                password_char = value;
+                               PasswordCache.Length = 0;
                                if ((password_char.Length != 0) && (password_char[0] != '\0')) {
-                                       char    ch;
-
                                        calc_pass = true;
-                                       ch = value[0];
-                                       password_cache = new StringBuilder(1024);
-                                       for (int i = 0; i < 1024; i++) {
-                                               password_cache.Append(ch);
-                                       }
                                } else {
                                        calc_pass = false;
-                                       password_cache = null;
                                }
                        }
                }
 
+               private StringBuilder PasswordCache {
+                       get { 
+                               if (password_cache == null) 
+                                         password_cache = new StringBuilder(); 
+                               return password_cache;
+                       }
+               }
+
                internal int ViewPortX {
                        get {
                                return viewport_x;
@@ -936,22 +1017,6 @@ namespace System.Windows.Forms {
                        }
                }
 
-               ///<summary>Setting NoRecalc to true will prevent the document from being recalculated.
-               ///This ensures that coordinates of added text are predictable after adding the text even with wrapped view</summary>
-               internal bool NoRecalc {
-                       get {
-                               return no_recalc;
-                       }
-
-                       set {
-                               no_recalc = value;
-                               if (!no_recalc && recalc_pending) {
-                                       RecalculateDocument(owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
-                                       recalc_pending = false;
-                               }
-                       }
-               }
-
                internal int ViewPortY {
                        get {
                                return viewport_y;
@@ -1014,14 +1079,68 @@ namespace System.Windows.Forms {
                #endregion      // Internal Properties
 
                #region Private Methods
+
+               internal void UpdateMargins ()
+               {
+                       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 ()
+               {
+                       recalc_suspended++;
+               }
+
+               internal void ResumeRecalc (bool immediate_update)
+               {
+                       if (recalc_suspended > 0)
+                               recalc_suspended--;
+
+                       if (immediate_update && recalc_suspended == 0 && recalc_pending) {
+                               RecalculateDocument (owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
+                               recalc_pending = false;
+                       }
+               }
+
+               internal void SuspendUpdate ()
+               {
+                       update_suspended++;
+               }
+
+               internal void ResumeUpdate (bool immediate_update)
+               {
+                       if (update_suspended > 0)
+                               update_suspended--;
+
+                       if (immediate_update && update_suspended == 0 && update_pending) {
+                               UpdateView (GetLine (update_start), 0);
+                               update_pending = false;
+                       }
+               }
+
                // For debugging
                internal int DumpTree(Line line, bool with_tags) {
                        int     total;
 
                        total = 1;
 
-                       Console.Write("Line {0} [# {1}], Y: {2}, soft: {3},  Text {4}",
-                                       line.line_no, line.GetHashCode(), line.Y, line.soft_break,
+                       Console.Write("Line {0} [# {1}], Y: {2}, ending style: {3},  Text: '{4}'",
+                                       line.line_no, line.GetHashCode(), line.Y, line.ending,
                                        line.text != null ? line.text.ToString() : "undefined");
 
                        if (line.left == sentinel) {
@@ -1048,7 +1167,8 @@ namespace System.Windows.Forms {
                                length = 0;
                                Console.Write("   Tags: ");
                                while (tag != null) {
-                                       Console.Write("{0} <{1}>-<{2}> ", count++, tag.start, tag.length);
+                                       Console.Write("{0} <{1}>-<{2}>", count++, tag.start, tag.end
+                                                       /*line.text.ToString (tag.start - 1, tag.length)*/);
                                        length += tag.length;
 
                                        if (tag.line != line) {
@@ -1109,7 +1229,7 @@ namespace System.Windows.Forms {
                        selection_visible = value;
 
                        // cursor and selection are enemies, we can't have both in the same room at the same time
-                       if (owner.IsHandleCreated)
+                       if (owner.IsHandleCreated && !owner.show_caret_w_selection)
                                XplatUI.CaretVisible (owner.Handle, !selection_visible);
                }
 
@@ -1302,11 +1422,11 @@ namespace System.Windows.Forms {
                                return;
                        }
 
-                       if (no_recalc) {
-                               recalc_start = line.line_no;
-                               recalc_end = line.line_no;
-                               recalc_optimize = true;
-                               recalc_pending = true;
+                       if (update_suspended > 0) {
+                               update_start = Math.Min (update_start, line.line_no);
+                               // update_end = Math.Max (update_end, line.line_no);
+                               // recalc_optimize = true;
+                               update_pending = true;
                                return;
                        }
 
@@ -1323,17 +1443,17 @@ namespace System.Windows.Forms {
                        } else {
                                switch(line.alignment) {
                                        case HorizontalAlignment.Left: {
-                                               owner.Invalidate(new Rectangle((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, line.Y - viewport_y, viewport_width, line.height + 1));
                                                break;
                                        }
 
                                        case HorizontalAlignment.Center: {
-                                               owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, line.height + 1));
+                                               owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, viewport_width, line.height + 1));
                                                break;
                                        }
 
                                        case HorizontalAlignment.Right: {
-                                               owner.Invalidate(new Rectangle(0, line.Y - viewport_y, (int)line.widths[pos + 1] - viewport_x + line.align_shift, line.height + 1));
+                                               owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, (int)line.widths[pos + 1] - viewport_x + line.X, line.height + 1));
                                                break;
                                        }
                                }
@@ -1347,35 +1467,42 @@ namespace System.Windows.Forms {
                                return;
                        }
 
-                       if (no_recalc) {
-                               recalc_start = line.line_no;
-                               recalc_end = line.line_no + line_count - 1;
+                       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 (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count - 1, true)) {
+                       int start_line_top = line.Y;                    
+
+                       int end_line_bottom;
+                       Line end_line;
+
+                       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
-//blah Console.WriteLine("TextControl.cs(981) Invalidate called in UpdateView(line, line_count, pos)");
                                        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
-//blah Console.WriteLine("TextControl.cs(985) Invalidate called in UpdateView(line, line_count, pos)");
                                        owner.Invalidate();
                                }
                        } else {
-                               Line    end_line;
-
-                               end_line = GetLine(line.line_no + line_count -1);
-                               if (end_line == null) {
-                                       end_line = line;
-                               }
+                               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);
 
-//blah Console.WriteLine("TextControl.cs(996) Invalidate called in UpdateView(line, line_count, pos)");
-                               owner.Invalidate(new Rectangle(0 - viewport_x, line.Y - viewport_y, (int)line.widths[line.text.Length], end_line.Y + end_line.height));
+                               owner.Invalidate (new Rectangle (x, y, w, h));
                        }
                }
                #endregion      // Private Methods
@@ -1388,9 +1515,7 @@ namespace System.Windows.Forms {
                        lines = 0;
 
                        // We always have a blank line
-                       Add(1, "", owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush(owner.ForeColor));
-                       Line l = GetLine (1);
-                       l.soft_break = true;
+                       Add (1, String.Empty, owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush (owner.ForeColor), LineEnding.None);
                        
                        this.RecalculateDocument(owner.CreateGraphicsInternal());
                        PositionCaret(0, 0);
@@ -1410,26 +1535,32 @@ namespace System.Windows.Forms {
 
                        document_x = 0;
                        document_y = 0;
+
+                       if (owner.IsHandleCreated)
+                               owner.Invalidate ();
                }
 
                internal void PositionCaret(Line line, int pos) {
-                       if (owner.IsHandleCreated) {
-                               undo.RecordCursor();
-                       }
+                       caret.tag = line.FindTag (pos);
+
+                       MoveCaretToTextTag ();
 
-                       caret.tag = line.FindTag(pos);
                        caret.line = line;
                        caret.pos = pos;
-                       caret.height = caret.tag.height;
 
                        if (owner.IsHandleCreated) {
                                if (owner.Focused) {
-                                       XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
+                                       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);
                                }
 
                                if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
                        }
 
+                       // 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;
 
                }
 
@@ -1438,14 +1569,16 @@ namespace System.Windows.Forms {
                                return;
                        }
 
-                       undo.RecordCursor();
-
                        caret.tag = FindCursor(x, y, out caret.pos);
+
+                       MoveCaretToTextTag ();
+                       
                        caret.line = caret.tag.line;
                        caret.height = caret.tag.height;
 
-                       if (owner.Focused) {
-                               XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
+                       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);
                        }
 
                        if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
@@ -1454,10 +1587,14 @@ 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.align_shift - 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, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
 
                                DisplayCaret ();
                        }
+
+                       if (owner.IsHandleCreated && SelectionLength () > 0) {
+                               InvalidateSelectionArea ();
+                       }
                }
 
                internal void CaretLostFocus() {
@@ -1472,14 +1609,15 @@ namespace System.Windows.Forms {
                                return;
                        }
 
-                       undo.RecordCursor();
+                       caret.tag = LineTag.FindTag (caret.line, caret.pos);
+
+                       MoveCaretToTextTag ();
 
-                       caret.tag = LineTag.FindTag(caret.line, caret.pos);
                        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.align_shift - 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, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
                                DisplayCaret ();
                        }
 
@@ -1491,7 +1629,7 @@ namespace System.Windows.Forms {
                                return;
                        }
 
-                       undo.RecordCursor();
+                       MoveCaretToTextTag ();
 
                        if (caret.tag.height != caret.height) {
                                caret.height = caret.tag.height;
@@ -1500,10 +1638,11 @@ namespace System.Windows.Forms {
                                }
                        }
 
-                       XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
-
-                       DisplayCaret ();
-
+                       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);
+                               DisplayCaret ();
+                       }
+                       
                        if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
                }
 
@@ -1512,7 +1651,7 @@ namespace System.Windows.Forms {
                                return;
                        }
 
-                       if (owner.Focused && !selection_visible) {
+                       if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
                                XplatUI.CaretVisible(owner.Handle, true);
                        }
                }
@@ -1527,6 +1666,21 @@ namespace System.Windows.Forms {
                        }
                }
 
+               
+               internal void MoveCaretToTextTag ()
+               {
+                       if (caret.tag == null || caret.tag.IsTextTag)
+                               return;
+
+                       
+
+                       if (caret.pos < caret.tag.start) {
+                               caret.tag = caret.tag.previous;
+                       } else {
+                               caret.tag = caret.tag.next;
+                       }
+               }
+
                internal void MoveCaret(CaretDirection direction) {
                        // FIXME should we use IsWordSeparator to detect whitespace, instead 
                        // of looking for actual spaces in the Word move cases?
@@ -1538,8 +1692,8 @@ namespace System.Windows.Forms {
                                        goto case CaretDirection.CharForward;
                                case CaretDirection.CharForward: {
                                        caret.pos++;
-                                       if (caret.pos > caret.line.text.Length) {
-                                               if (multiline && !nowrap) {
+                                       if (caret.pos > caret.line.TextLengthWithoutEnding ()) {
+                                               if (!nowrap) {
                                                        // Go into next line
                                                        if (caret.line.line_no < this.lines) {
                                                                caret.line = GetLine(caret.line.line_no+1);
@@ -1567,6 +1721,7 @@ namespace System.Windows.Forms {
                                case CaretDirection.CharBack: {
                                        if (caret.pos > 0) {
                                                // caret.pos--; // folded into the if below
+                                               
                                                if (--caret.pos > 0) {
                                                        if (caret.tag.start > caret.pos) {
                                                                caret.tag = caret.tag.previous;
@@ -1575,7 +1730,7 @@ namespace System.Windows.Forms {
                                        } else {
                                                if (caret.line.line_no > 1 && !nowrap) {
                                                        caret.line = GetLine(caret.line.line_no - 1);
-                                                       caret.pos = caret.line.text.Length;
+                                                       caret.pos = caret.line.TextLengthWithoutEnding ();
                                                        caret.tag = LineTag.FindTag(caret.line, caret.pos);
                                                }
                                        }
@@ -1675,8 +1830,8 @@ namespace System.Windows.Forms {
                                }
 
                                case CaretDirection.End: {
-                                       if (caret.pos < caret.line.text.Length) {
-                                               caret.pos = caret.line.text.Length;
+                                       if (caret.pos < caret.line.TextLengthWithoutEnding ()) {
+                                               caret.pos = caret.line.TextLengthWithoutEnding ();
                                                caret.tag = LineTag.FindTag(caret.line, caret.pos);
                                                UpdateCaret();
                                        }
@@ -1685,56 +1840,38 @@ namespace System.Windows.Forms {
 
                                case CaretDirection.PgUp: {
 
-                                       int new_y, y_offset;
-
-                                       if (viewport_y == 0) {
-
-                                               // This should probably be handled elsewhere
-                                               if (!(owner is RichTextBox)) {
-                                                       // Page down doesn't do anything in a regular TextBox
-                                                       // if the bottom of the document
-                                                       // is already visible, the page and the caret stay still
-                                                       return;
-                                               }
-
-                                               // We're just placing the caret at the end of the document, no scrolling needed
+                                       if (viewport_y == 0 && owner.richtext) {
                                                owner.vscroll.Value = 0;
                                                Line line = GetLine (1);
                                                PositionCaret (line, 0);
                                        }
 
-                                       y_offset = caret.line.Y - viewport_y;
-                                       new_y = caret.line.Y - viewport_height;
+                                       int y_offset = caret.line.Y + caret.line.height - 1 - viewport_y;
+                                       int index;
+                                       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);
+                                       PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
 
-                                       owner.vscroll.Value = Math.Max (new_y, 0);
-                                       PositionCaret ((int)caret.line.widths[caret.pos], y_offset + viewport_y);
                                        return;
                                }
 
                                case CaretDirection.PgDn: {
-                                       int new_y, y_offset;
-
-                                       if ((viewport_y + viewport_height) > document_y) {
-
-                                               // This should probably be handled elsewhere
-                                               if (!(owner is RichTextBox)) {
-                                                       // Page up doesn't do anything in a regular TextBox
-                                                       // if the bottom of the document
-                                                       // is already visible, the page and the caret stay still
-                                                       return;
-                                               }
 
-                                               // We're just placing the caret at the end of the document, no scrolling needed
+                                       if (viewport_y + viewport_height >= document_y && owner.richtext) {
                                                owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1;
                                                Line line = GetLine (lines);
                                                PositionCaret (line, line.Text.Length);
                                        }
 
-                                       y_offset = caret.line.Y - viewport_y;
-                                       new_y = caret.line.Y + viewport_height;
-                                       
-                                       owner.vscroll.Value = Math.Min (new_y, owner.vscroll.Maximum - viewport_height + 1);
-                                       PositionCaret ((int)caret.line.widths[caret.pos], y_offset + viewport_y);
+                                       int y_offset = caret.line.Y - viewport_y;
+                                       int index;
+                                       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);
+                                       PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
                                        
                                        return;
                                }
@@ -1772,7 +1909,7 @@ namespace System.Windows.Forms {
 
                                case CaretDirection.CtrlEnd: {
                                        caret.line = GetLine(lines);
-                                       caret.pos = caret.line.text.Length;
+                                       caret.pos = caret.line.TextLengthWithoutEnding ();
                                        caret.tag = LineTag.FindTag(caret.line, caret.pos);
 
                                        UpdateCaret();
@@ -1799,350 +1936,313 @@ namespace System.Windows.Forms {
                        }
                }
 
-               // Draw the document
-               internal void Draw(Graphics g, Rectangle clip) {
-                       Line            line;           // Current line being drawn
-                       LineTag         tag;            // Current tag being drawn
-                       int             start;          // First line to draw
-                       int             end;            // Last line to draw
-                       StringBuilder   text;           // String representing the current line
-                       int             line_no;        //
-                       Brush           disabled;
-                       Brush           hilight;
-                       Brush           hilight_text;
+               internal void DumpDoc ()
+               {
+                       Console.WriteLine ("<doc lines='{0}'>", lines);
+                       for (int i = 1; i <= lines ; i++) {
+                               Line line = GetLine (i);
+                               Console.WriteLine ("<line no='{0}' ending='{1}'>", line.line_no, line.ending);
+
+                               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);
+                                       Console.Write (tag.Text ());
+                                       Console.WriteLine ("</tag>");
+                                       tag = tag.next;
+                               }
+                               Console.WriteLine ("</line>");
+                       }
+                       Console.WriteLine ("</doc>");
+               }
+
+               internal void Draw (Graphics g, Rectangle clip)
+               {
+                       Line line;              // Current line being drawn
+                       LineTag tag;            // Current tag being drawn
+                       int start;              // First line to draw
+                       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;
 
                        // First, figure out from what line to what line we need to draw
-                       start = GetLineByPixel(clip.Top + viewport_y, false).line_no;
-                       end = GetLineByPixel(clip.Bottom + viewport_y, false).line_no;
-//Console.WriteLine("Starting drawing at line {0}, ending at line {1} (clip-bottom:{2})", start, end, clip.Bottom);
 
-                       // Now draw our elements; try to only draw those that are visible
+                       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;
+                       }
+
+                       ///
+                       /// We draw the single border ourself
+                       ///
+                       if (owner.actual_border_style == BorderStyle.FixedSingle) {
+                               ControlPaint.DrawBorder (g, owner.ClientRectangle, Color.Black, ButtonBorderStyle.Solid);
+                       }
+
+                       /// 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--;                  
+
                        line_no = start;
 
                        #if Debug
                                DateTime        n = DateTime.Now;
-                               Console.WriteLine("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
+                               Console.WriteLine ("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
+                               Console.WriteLine ("CLIP:  {0}", clip);
+                               Console.WriteLine ("S: {0}", GetLine (start).text);
+                               Console.WriteLine ("E: {0}", GetLine (end).text);
                        #endif
 
-                       disabled = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorGrayText);
+                       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);
 
-                       while (line_no <= end) {
-                               line = GetLine(line_no);
-#if not
-if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
-       g.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), new Rectangle(clip.Left, line.Y - viewport_y, clip.Width, line.Y - viewport_y + line.Height));
-} else {
-       g.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorControl), new Rectangle(clip.Left, line.Y - viewport_y, clip.Width, line.Y - viewport_y + line.Height));
-}
-#endif
-
+                       // 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] +
+                                               selection_start.line.X - viewport_x, 
+                                               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);
+                       }
 
+                       while (line_no <= end) {
+                               line = GetLine (line_no);
+                               float line_y = line.Y - viewport_y;
+                               
                                tag = line.tags;
                                if (!calc_pass) {
                                        text = line.text;
                                } else {
-                                       // This fails if there's a password > 1024 chars...
-                                       text = this.password_cache;
+                                       if (PasswordCache.Length < line.text.Length)
+                                               PasswordCache.Append(Char.Parse(password_char), line.text.Length - PasswordCache.Length);
+                                       else if (PasswordCache.Length > line.text.Length)
+                                               PasswordCache.Remove(line.text.Length, PasswordCache.Length - line.text.Length);
+                                       text = PasswordCache;
+                               }
+
+                               int line_selection_start = text.Length + 1;
+                               int line_selection_end = text.Length + 1;
+                               if (selection_visible && owner.ShowSelection &&
+                                               (line_no >= selection_start.line.line_no) &&
+                                               (line_no <= selection_end.line.line_no)) {
+
+                                       if (line_no == selection_start.line.line_no)
+                                               line_selection_start = selection_start.pos + 1;
+                                       else
+                                               line_selection_start = 1;
+
+                                       if (line_no == selection_end.line.line_no)
+                                               line_selection_end = selection_end.pos + 1;
+                                       else
+                                               line_selection_end = text.Length + 1;
+
+                                       if (line_selection_end == line_selection_start) {
+                                               // There isn't really selection
+                                               line_selection_start = text.Length + 1;
+                                               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, 
+                                                               line_y, line.widths [line_selection_end - 1] - line.widths [line_selection_start - 1], 
+                                                               line.height);
+                                       }
                                }
+
+                               current_brush = line.tags.color;
                                while (tag != null) {
+
+                                       // Skip empty tags
                                        if (tag.length == 0) {
                                                tag = tag.next;
                                                continue;
                                        }
 
-                                       if (((tag.X + tag.width) > (clip.Left - viewport_x)) || (tag.X < (clip.Right - viewport_x))) {
-                                               // Check for selection
-                                               if ((!selection_visible) || (!owner.ShowSelection) || (line_no < selection_start.line.line_no) || (line_no > selection_end.line.line_no)) {
-                                                       // regular drawing, no selection to deal with
-                                                       //g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
-                                                       if (owner.is_enabled) {
-                                                               g.DrawString(text.ToString(tag.start-1, tag.length), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
-                                                       } else {
-                                                               Color a;
-                                                               Color b;
+                                       if (((tag.X + tag.width) < (clip.Left - viewport_x)) && (tag.X > (clip.Right - viewport_x))) {
+                                               tag = tag.next;
+                                               continue;
+                                       }
 
-                                                               a = ((SolidBrush)tag.color).Color;
-                                                               b = ThemeEngine.Current.ColorWindowText;
+                                       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 ((a.R == b.R) && (a.G == b.G) && (a.B == b.B)) {
-                                                                       g.DrawString(text.ToString(tag.start-1, tag.length), tag.font, disabled, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
-                                                               } else {
-                                                                       g.DrawString(text.ToString(tag.start-1, tag.length), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
-                                                               }
-                                                       }
-                                               } else {
-                                                       // we might have to draw our selection
-                                                       if ((line_no != selection_start.line.line_no) && (line_no != selection_end.line.line_no)) {
-                                                               // Special case, whole line is selected, draw this tag selected
-                                                               g.FillRectangle(
-                                                                       hilight,                                        // Brush 
-                                                                       tag.X + line.align_shift - viewport_x,          // X
-                                                                       line.Y + tag.shift - viewport_y,                // Y
-                                                                       line.widths[tag.start + tag.length - 1],        // width
-                                                                       tag.height                                      // Height
-                                                               );
-
-                                                               g.DrawString(
-                                                                       //s.Substring(tag.start-1, tag.length),         // String
-                                                                       text.ToString(tag.start-1, tag.length), // String
-                                                                       tag.font,                                       // Font
-                                                                       hilight_text,                                   // Brush
-                                                                       tag.X + line.align_shift - viewport_x,          // X
-                                                                       line.Y + tag.shift  - viewport_y,               // Y
-                                                                       StringFormat.GenericTypographic);
-                                                       } else {
-                                                               bool    highlight;
-                                                               bool    partial;
-
-                                                               highlight = false;
-                                                               partial = false;
-
-                                                               // One or more, but not all tags on the line are selected
-                                                               if ((selection_start.tag == tag) && (selection_end.tag == tag)) {
-                                                                       // Single tag selected, draw "normalSELECTEDnormal"
-                                                                       partial = true;
-                                                                       // First, the regular part
-                                                                       g.DrawString(
-                                                                               //s.Substring(tag.start - 1, selection_start.pos - tag.start + 1),      // String
-                                                                               text.ToString(tag.start - 1, selection_start.pos - tag.start + 1),      // String
-                                                                               tag.font,                                                               // Font
-                                                                               tag.color,                                                              // Brush
-                                                                               tag.X + line.align_shift - viewport_x,                                  // X
-                                                                               line.Y + tag.shift  - viewport_y,                                       // Y
-                                                                               StringFormat.GenericTypographic);
-
-                                                                       // Now the highlight
-                                                                       g.FillRectangle(
-                                                                               hilight,                                                                // Brush
-                                                                               line.widths[selection_start.pos] + line.align_shift - viewport_x,                       // X
-                                                                               line.Y + tag.shift - viewport_y,                                        // Y
-                                                                               line.widths[selection_end.pos] - line.widths[selection_start.pos],      // Width
-                                                                               tag.height);                                                            // Height
-
-                                                                       g.DrawString(
-                                                                               //s.Substring(selection_start.pos, selection_end.pos - selection_start.pos), // String
-                                                                               text.ToString(selection_start.pos, selection_end.pos - selection_start.pos), // String
-                                                                               tag.font,                                                               // Font
-                                                                               hilight_text,                                                           // Brush
-                                                                               line.widths[selection_start.pos] + line.align_shift - viewport_x,       // X
-                                                                               line.Y + tag.shift - viewport_y,                                        // Y
-                                                                               StringFormat.GenericTypographic);
-
-                                                                       // And back to the regular
-                                                                       g.DrawString(
-                                                                               //s.Substring(selection_end.pos, tag.start + tag.length - selection_end.pos - 1),       // String
-                                                                               text.ToString(selection_end.pos, tag.start + tag.length - selection_end.pos - 1),       // String
-                                                                               tag.font,                                                               // Font
-                                                                               tag.color,                                                              // Brush
-                                                                               line.widths[selection_end.pos] + line.align_shift - viewport_x,         // X
-                                                                               line.Y + tag.shift - viewport_y,                                        // Y
-                                                                               StringFormat.GenericTypographic);
-
-                                                               } else if (selection_start.tag == tag) {
-                                                                       partial = true;
-
-                                                                       // The highlighted part
-                                                                       g.FillRectangle(
-                                                                               hilight, 
-                                                                               line.widths[selection_start.pos] + line.align_shift - viewport_x, 
-                                                                               line.Y + tag.shift - viewport_y, 
-                                                                               line.widths[tag.start + tag.length - 1] - line.widths[selection_start.pos], 
-                                                                               tag.height);
-
-                                                                       g.DrawString(
-                                                                               //s.Substring(selection_start.pos, tag.start + tag.length - selection_start.pos - 1),   // String
-                                                                               text.ToString(selection_start.pos, tag.start + tag.length - selection_start.pos - 1),   // String
-                                                                               tag.font,                                                               // Font
-                                                                               hilight_text,                                                           // Brush
-                                                                               line.widths[selection_start.pos] + line.align_shift - viewport_x,       // X
-                                                                               line.Y + tag.shift - viewport_y,                                        // Y
-                                                                               StringFormat.GenericTypographic);
-
-                                                                       // The regular part
-                                                                       g.DrawString(
-                                                                               //s.Substring(tag.start - 1, selection_start.pos - tag.start + 1),      // String
-                                                                               text.ToString(tag.start - 1, selection_start.pos - tag.start + 1), // String
-                                                                               tag.font,                                                               // Font
-                                                                               tag.color,                                                              // Brush
-                                                                               tag.X + line.align_shift - viewport_x,                                  // X
-                                                                               line.Y + tag.shift  - viewport_y,                                       // Y
-                                                                               StringFormat.GenericTypographic);
-                                                               } else if (selection_end.tag == tag) {
-                                                                       partial = true;
-
-                                                                       // The highlighted part
-                                                                       g.FillRectangle(
-                                                                               hilight, 
-                                                                               tag.X + line.align_shift - viewport_x, 
-                                                                               line.Y + tag.shift - viewport_y, 
-                                                                               line.widths[selection_end.pos] - line.widths[tag.start - 1], 
-                                                                               tag.height);
-
-                                                                       g.DrawString(
-                                                                               //s.Substring(tag.start - 1, selection_end.pos - tag.start + 1),         // String
-                                                                               text.ToString(tag.start - 1, selection_end.pos - tag.start + 1),         // String
-                                                                               tag.font,                                                               // Font
-                                                                               hilight_text,                                                           // Brush
-                                                                               tag.X + line.align_shift - viewport_x,                                  // X
-                                                                               line.Y + tag.shift  - viewport_y,                                       // Y
-                                                                               StringFormat.GenericTypographic);
-
-                                                                       // The regular part
-                                                                       g.DrawString(
-                                                                               //s.Substring(selection_end.pos, tag.start + tag.length - selection_end.pos - 1),               // String
-                                                                               text.ToString(selection_end.pos, tag.start + tag.length - selection_end.pos - 1),               // String
-                                                                               tag.font,                                                               // Font
-                                                                               tag.color,                                                              // Brush
-                                                                               line.widths[selection_end.pos] + line.align_shift - viewport_x,         // X
-                                                                               line.Y + tag.shift - viewport_y,                                        // Y
-                                                                               StringFormat.GenericTypographic);
-                                                               } else {
-                                                                       // no partially selected tags here, simple checks...
-                                                                       if (selection_start.line == line) {
-                                                                               int begin;
-                                                                               int stop;
-
-                                                                               begin = tag.start - 1;
-                                                                               stop = tag.start + tag.length - 1;
-                                                                               if (selection_end.line == line) {
-                                                                                       if ((begin >= selection_start.pos) && (stop < selection_end.pos)) {
-                                                                                               highlight = true;
-                                                                                       }
-                                                                               } else {
-                                                                                       if (stop > selection_start.pos) {
-                                                                                               highlight = true;
-                                                                                       }
-                                                                               }
-                                                                       } else if (selection_end.line == line) {
-                                                                               if ((tag.start - 1) < selection_end.pos) {
-                                                                                       highlight = true;
-                                                                               }
-                                                                       }
-                                                               }
-
-                                                               if (!partial) {
-                                                                       if (highlight) {
-                                                                               g.FillRectangle(
-                                                                                       hilight, 
-                                                                                       tag.X + line.align_shift - viewport_x, 
-                                                                                       line.Y + tag.shift  - viewport_y, 
-                                                                                       line.widths[tag.start + tag.length - 1] - line.widths[tag.start - 1],
-                                                                                       tag.height);
-
-                                                                               g.DrawString(
-                                                                                       //s.Substring(tag.start-1, tag.length),                 // String
-                                                                                       text.ToString(tag.start-1, tag.length),         // String
-                                                                                       tag.font,                                               // Font
-                                                                                       hilight_text,                                           // Brush
-                                                                                       tag.X + line.align_shift - viewport_x,                  // X
-                                                                                       line.Y + tag.shift  - viewport_y,                       // Y
-                                                                                       StringFormat.GenericTypographic);
-                                                                       } else {
-                                                                               g.DrawString(
-                                                                                       //s.Substring(tag.start-1, tag.length),                 // String
-                                                                                       text.ToString(tag.start-1, tag.length),         // String
-                                                                                       tag.font,                                               // Font
-                                                                                       tag.color,                                              // Brush
-                                                                                       tag.X + line.align_shift - viewport_x,                  // X
-                                                                                       line.Y + tag.shift  - viewport_y,                       // Y
-                                                                                       StringFormat.GenericTypographic);
-                                                                       }
-                                                               }
-                                                       }
+                                       tag_brush = tag.color;
+                                       current_brush = tag_brush;
 
+                                       if (!owner.is_enabled) {
+                                               Color a = ((SolidBrush) tag.color).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;
                                        }
 
+                                       int tag_pos = tag.start;
+                                       current_brush = tag_brush;
+                                       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);
+                                               } else if (tag_pos < line_selection_start) {
+                                                       current_brush = tag_brush;
+                                                       tag_pos = Math.Min (tag.end, line_selection_start);
+                                               } else {
+                                                       current_brush = tag_brush;
+                                                       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() );
+                                       }
                                        tag = tag.next;
                                }
 
+                               line.DrawEnding (g, line_y);
                                line_no++;
                        }
-                       #if Debug
-                               n = DateTime.Now;
-                               Console.WriteLine("Finished drawing: {0}s {1}ms", n.Second, n.Millisecond);
-                       #endif
-
                }
 
-               internal void Insert(Line line, int pos, string s) {
-                       Insert(line, null, pos, false, s);
-               }
+               internal int GetLineEnding (string line, int start, out LineEnding ending)
+               {
+                       int res;
 
-               // Insert multi-line text at the given position; use formatting at insertion point for inserted text
-               internal void Insert(Line line, LineTag tag, int pos, bool update_caret, string s) {
-                       int             i;
-                       int             base_line;
-                       string[]        ins;
-                       int             insert_lines;
-                       int             old_line_count;
-                       bool carriage_return = false;
+                       res = line.IndexOf ('\r', start);
+                       if (res != -1) {
+                               if (res + 2 < line.Length && line [res + 1] == '\r' && line [res + 2] == '\n') {
+                                       ending = LineEnding.Soft;
+                                       return res;
+                               }
+                               if (res + 1 < line.Length && line [res + 1] == '\n') {
+                                       ending = LineEnding.Hard;
+                                       return res;
+                               }
+                               ending = LineEnding.Limp;
+                               return res;
+                       }
 
-                       NoRecalc = true;
+                       res = line.IndexOf ('\n', start);
+                       if (res != -1) {
+                               ending = LineEnding.Rich;
+                               return res;
+                       }
 
-                       // The formatting at the insertion point is used for the inserted text
-                       if (tag == null) {
-                               tag = LineTag.FindTag(line, pos);
+                       ending = LineEnding.Wrap;
+                       return line.Length;
+               }
+
+               internal int LineEndingLength (LineEnding ending)
+               {
+                       int res = 0;
+
+                       switch (ending) {
+                       case LineEnding.Limp:
+                       case LineEnding.Rich:
+                               res = 1;
+                               break;
+                       case LineEnding.Hard:
+                               res = 2;
+                               break;
+                       case LineEnding.Soft:
+                               res = 3;
+                               break;
                        }
 
-                       base_line = line.line_no;
+                       return res;
+               }
 
-                       ins = s.Split(new char[] {'\n'});
+               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;
+               }
 
-                       insert_lines = ins.Length;
+               
+               // 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) {
+                       int break_index;
+                       int base_line;
+                       int old_line_count;
+                       int count = 1;
+                       LineEnding ending;
+                       LineTag tag = LineTag.FindTag (line, pos);
+                       
+                       SuspendRecalc ();
+                       
+                       base_line = line.line_no;
                        old_line_count = lines;
 
+                       break_index = GetLineEnding (s, 0, out ending);
+
                        // Bump the text at insertion point a line down if we're inserting more than one line
-                       if (insert_lines > 1) {
-                               Split(line, pos);
-                               line.soft_break = false;
+                       if (break_index != s.Length) {
+                               Split (line, pos);
+                               line.ending = ending;
                                // Remainder of start line is now in base_line + 1
                        }
 
-                       if (ins [0].EndsWith ("\r")) {
-                               ins [0] = ins[0].Substring (0, ins[0].Length - 1);
-                               carriage_return = true;
-                       }
+                       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));
 
-                       // Insert the first line
-                       InsertString(tag, pos, ins[0]);
+                               Add (base_line + count, line_text, line.alignment, tag.font, tag.color, ending);
 
-                       if (carriage_return) {
-                               Line l = GetLine (base_line);
-                               l.carriage_return = true;
-                       }
+                               Line last = GetLine (base_line + count);
+                               last.ending = ending;
 
-                       if (insert_lines > 1) {
-                               for (i = 1; i < insert_lines; i++) {
-                                       carriage_return = false;
-                                       if (ins [i].EndsWith ("\r")) {
-                                               ins [i] = ins[i].Substring (0, ins[i].Length - 1);
-                                               carriage_return = true;
-                                       }
-                                       Add(base_line + i, ins[i], line.alignment, tag.font, tag.color);
-                                       if (carriage_return) {
-                                               Line l = GetLine (base_line + i);
-                                               l.carriage_return = true;
-                                       }
-                               }
-                               if (!s.EndsWith("\n\n")) {
-                                       this.Combine(base_line + (lines - old_line_count) - 1, base_line + lines - old_line_count);
-                               }
+                               count++;
+                               break_index = next_break + LineEndingLength (ending);
                        }
 
-                       NoRecalc = false;
+                       ResumeRecalc (true);
+
                        UpdateView(line, lines - old_line_count + 1, pos);
 
                        if (update_caret) {
                                // Move caret to the end of the inserted text
-                               if (insert_lines > 1) {
-                                       Line l = GetLine (line.line_no + lines - old_line_count);
-                                       PositionCaret(l, l.text.Length);
-                               } else {
-                                       PositionCaret(line, pos + ins[0].Length);
-                               }
+                               Line l = GetLine (line.line_no + lines - old_line_count);
+                               PositionCaret(l, l.text.Length);
                                DisplayCaret ();
                        }
                }
@@ -2163,9 +2263,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        line = tag.line;
                        line.text.Insert(pos, s);
-                       tag.length += len;
 
-                       // TODO: sometimes getting a null tag here when pasting ???
                        tag = tag.next;
                        while (tag != null) {
                                tag.start += len;
@@ -2179,29 +2277,12 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                // Inserts a string at the caret position
                internal void InsertStringAtCaret(string s, bool move_caret) {
-                       LineTag tag;
-                       int     len;
-
-                       len = s.Length;
-
-                       CharCount += len;
 
-                       caret.line.text.Insert(caret.pos, s);
-                       caret.tag.length += len;
-                       
-                       if (caret.tag.next != null) {
-                               tag = caret.tag.next;
-                               while (tag != null) {
-                                       tag.start += len;
-                                       tag = tag.next;
-                               }
-                       }
-                       caret.line.Grow(len);
-                       caret.line.recalc = true;
+                       InsertString (caret.tag, caret.pos, s);
 
                        UpdateView(caret.line, caret.pos);
                        if (move_caret) {
-                               caret.pos += len;
+                               caret.pos += s.Length;
                                UpdateCaret();
                        }
                }
@@ -2221,7 +2302,6 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        line = tag.line;
                        line.text.Insert(pos, ch);
-                       tag.length++;
 
                        tag = tag.next;
                        while (tag != null) {
@@ -2231,11 +2311,13 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        line.Grow(1);
                        line.recalc = true;
 
+                       undo.RecordTyping (line, pos, ch);
                        UpdateView(line, pos);
                }
 
                // Inserts a character at the current caret position
                internal void InsertCharAtCaret(char ch, bool move_caret) {
+                       /*
                        LineTag tag;
 
                        CharCount++;
@@ -2252,6 +2334,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
                        caret.line.Grow(1);
                        caret.line.recalc = true;
+                       */
+                       InsertChar (caret.tag, caret.pos, ch);
 
                        UpdateView(caret.line, caret.pos);
                        if (move_caret) {
@@ -2259,10 +2343,52 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                UpdateCaret();
                                SetSelectionToCaret(true);
                        }
-               }
 
-               internal void DeleteMultiline (Line start_line, int pos, int length)
-               {
+               }
+               
+               internal void InsertPicture (Line line, int pos, RTF.Picture picture)
+               {
+                       //LineTag next_tag;
+                       LineTag tag;
+                       int len;
+
+                       len = 1;
+
+                       // Just a place holder basically
+                       line.text.Insert (pos, "I");
+
+                       PictureTag picture_tag = new PictureTag (line, pos + 1, picture);
+
+                       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 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;
+                       }
+
+                       tag = picture_tag.next;
+                       while (tag != null) {
+                               tag.start += len;
+                               tag = tag.next;
+                       }
+
+                       line.Grow (len);
+                       line.recalc = true;
+
+                       UpdateView (line, pos);
+               }
+
+               internal void DeleteMultiline (Line start_line, int pos, int length)
+               {
                        Marker start = new Marker ();
                        Marker end = new Marker ();
                        int start_index = LineTagToCharIndex (start_line, pos);
@@ -2274,6 +2400,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        CharIndexToLineTag (start_index + length, out end.line,
                                        out end.tag, out end.pos);
 
+                       SuspendUpdate ();
+
                        if (start.line == end.line) {
                                DeleteChars (start.tag, pos, end.pos - pos);
                        } else {
@@ -2295,6 +2423,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                // Join start and end
                                Combine (start.line.line_no, current);
                        }
+
+                       ResumeUpdate (true);
                }
 
                
@@ -2316,12 +2446,12 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        line.text.Remove(pos, count);
 
                        // Make sure the tag points to the right spot
-                       while ((tag != null) && (tag.start + tag.length - 1) <= pos) {
+                       while ((tag != null) && (tag.end) < pos) {
                                tag = tag.next;
                        }
 
                        if (tag == null) {
-                               return;
+                               goto Cleanup;
                        }
 
                        // Check if we're crossing tag boundaries
@@ -2333,26 +2463,22 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                left = count;
 
                                left -= tag.start + tag.length - pos - 1;
-                               tag.length -= tag.start + tag.length - pos - 1;
 
                                tag = tag.next;
                                while ((tag != null) && (left > 0)) {
                                        tag.start -= count - left;
+
                                        if (tag.length > left) {
-                                               tag.length -= left;
                                                left = 0;
                                        } else {
                                                left -= tag.length;
-                                               tag.length = 0;
-       
                                                tag = tag.next;
                                        }
+
                                }
                        } else {
                                // We got off easy, same tag
 
-                               tag.length -= count;
-
                                if (tag.length == 0) {
                                        streamline = true;
                                }
@@ -2382,7 +2508,25 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                line.Streamline(lines);
                        }
 
-                       UpdateView(line, pos);
+               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);
                }
 
                // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
@@ -2408,10 +2552,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
 
                                if (tag == null) {
-                                       return;
+                                       goto Cleanup;
                                }
 
-                               tag.length--;
+                               //      tag.length--;
 
                                if (tag.length == 0) {
                                        streamline = true;
@@ -2420,12 +2564,12 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                pos--;
                                line.text.Remove(pos, 1);
                                if (pos >= (tag.start - 1)) {
-                                       tag.length--;
+                                       //              tag.length--;
                                        if (tag.length == 0) {
                                                streamline = true;
                                        }
                                } else if (tag.previous != null) {
-                                       tag.previous.length--;
+                                       //              tag.previous.length--;
                                        if (tag.previous.length == 0) {
                                                streamline = true;
                                        }
@@ -2452,7 +2596,25 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                line.Streamline(lines);
                        }
 
-                       UpdateView(line, pos);
+               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);
                }
 
                // Combine two lines
@@ -2464,21 +2626,24 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        LineTag last;
                        int     shift;
 
+                       // strip the ending off of the first lines text
+                       first.text.Length = first.text.Length - LineEndingLength (first.ending);
+
                        // Combine the two tag chains into one
                        last = first.tags;
 
                        // Maintain the line ending style
-                       first.soft_break = second.soft_break;
+                       first.ending = second.ending;
 
                        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;
-
                        // Fix up references within the chain
                        last = last.next;
                        while (last != null) {
@@ -2543,20 +2708,19 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        line = GetLine(LineNo);
                        tag = LineTag.FindTag(line, pos);
-                       Split(line, tag, pos, false);
+                       Split(line, tag, pos);
                }
 
                internal void Split(Line line, int pos) {
                        LineTag tag;
 
                        tag = LineTag.FindTag(line, pos);
-                       Split(line, tag, pos, false);
+                       Split(line, tag, pos);
                }
 
                ///<summary>Split line at given tag and position into two lines</summary>
-               ///<param name="soft">True if the split should be marked as 'soft', indicating that it can be contracted 
                ///if more space becomes available on previous line</param>
-               internal void Split(Line line, LineTag tag, int pos, bool soft) {
+               internal void Split(Line line, LineTag tag, int pos) {
                        LineTag new_tag;
                        Line    new_line;
                        bool    move_caret;
@@ -2568,7 +2732,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        move_sel_end = false;
 
                        // Adjust selection and cursors
-                       if (soft && (caret.line == line) && (caret.pos >= pos)) {
+                       if (caret.line == line && caret.pos >= pos) {
                                move_caret = true;
                        }
                        if (selection_start.line == line && selection_start.pos > pos) {
@@ -2581,22 +2745,14 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        // cover the easy case first
                        if (pos == line.text.Length) {
-                               Add(line.line_no + 1, "", line.alignment, tag.font, tag.color);
+                               Add (line.line_no + 1, String.Empty, line.alignment, tag.font, tag.color, line.ending);
 
-                               new_line = GetLine(line.line_no + 1);
-
-                               line.carriage_return = false;
-                               new_line.carriage_return = line.carriage_return;
+                               new_line = GetLine (line.line_no + 1);
                                
-                               if (soft) {
-                                       if (move_caret) {
-                                               caret.line = new_line;
-                                               caret.line.soft_break = true;
-                                               caret.tag = new_line.tags;
-                                               caret.pos = 0;
-                                       } else {
-                                               new_line.soft_break = true;
-                                       }
+                               if (move_caret) {
+                                       caret.line = new_line;
+                                       caret.tag = new_line.tags;
+                                       caret.pos = 0;
                                }
 
                                if (move_sel_start) {
@@ -2614,14 +2770,11 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
 
                        // 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);
+                       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);
 
-                       line.carriage_return = false;
-                       new_line.carriage_return = line.carriage_return;
-
                        line.recalc = true;
                        new_line.recalc = true;
 
@@ -2630,9 +2783,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                                // We can simply break the chain and move the tag into the next line
                                if (tag == line.tags) {
-                                       new_tag = new LineTag(line, 1, 0);
-                                       new_tag.font = tag.font;
-                                       new_tag.color = tag.color;
+                                       new_tag = new LineTag(line, 1);
+                                       new_tag.CopyFormattingFrom (tag);
                                        line.tags = new_tag;
                                }
 
@@ -2655,16 +2807,14 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        } else {
                                int     shift;
 
-                               new_tag = new LineTag(new_line, 1, tag.start - 1 + tag.length - pos);
+                               new_tag = new LineTag (new_line, 1);                    
                                new_tag.next = tag.next;
-                               new_tag.font = tag.font;
-                               new_tag.color = tag.color;
+                               new_tag.CopyFormattingFrom (tag);
                                new_line.tags = new_tag;
                                if (new_tag.next != null) {
                                        new_tag.next.previous = new_tag;
                                }
                                tag.next = null;
-                               tag.length = pos - tag.start + 1;
 
                                shift = pos;
                                new_tag = new_tag.next;
@@ -2676,13 +2826,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
                        }
 
-                       if (soft) {
-                               if (move_caret) {
-                                       caret.line = new_line;
-                                       caret.pos = caret.pos - pos;
-                                       caret.tag = caret.line.FindTag(caret.pos);
-                               }
-                               new_line.soft_break = true;
+                       if (move_caret) {
+                               caret.line = new_line;
+                               caret.pos = caret.pos - pos;
+                               caret.tag = caret.line.FindTag(caret.pos);
                        }
 
                        if (move_sel_start) {
@@ -2703,11 +2850,13 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                // 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, Brush color) {
-                       Add(LineNo, Text, HorizontalAlignment.Left, font, color);
+               internal void Add (int LineNo, string Text, Font font, SolidBrush color, LineEnding ending)
+               {
+                       Add (LineNo, Text, alignment, font, color, ending);
                }
 
-               internal void Add(int LineNo, string Text, HorizontalAlignment align, Font font, Brush color) {
+               internal void Add (int LineNo, string Text, HorizontalAlignment align, Font font, SolidBrush color, LineEnding ending)
+               {
                        Line    add;
                        Line    line;
                        int     line_no;
@@ -2722,7 +2871,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
                        }
 
-                       add = new Line(LineNo, Text, align, font, color);
+                       add = new Line (this, LineNo, Text, align, font, color, ending);
 
                        line = document;
                        while (line != sentinel) {
@@ -2849,12 +2998,12 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                line1.line_no = line3.line_no;
                                line1.recalc = line3.recalc;
                                line1.right_indent = line3.right_indent;
-                               line1.soft_break = line3.soft_break;
+                               line1.ending = line3.ending;
                                line1.space = line3.space;
                                line1.tags = line3.tags;
                                line1.text = line3.text;
                                line1.widths = line3.widths;
-                               line1.Y = line3.Y;
+                               line1.offset = line3.offset;
 
                                tag = line1.tags;
                                while (tag != null) {
@@ -2912,45 +3061,59 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                        p2 = start_pos;
                                }
 
+                               int endpoint = (int) l1.widths [p2];
+                               if (p2 == l1.text.Length + 1) {
+                                       endpoint = (int) viewport_width;
+                               }
+
                                #if Debug
-                                       Console.WriteLine("Invaliding from {0}:{1} to {2}:{3}", l1.line_no, p1, l2.line_no, p2);
+                                       Console.WriteLine("Invaliding backwards from {0}:{1} to {2}:{3}   {4}",
+                                                       l1.line_no, p1, l2.line_no, p2,
+                                                       new Rectangle(
+                                                               (int)l1.widths[p1] + l1.X - viewport_x, 
+                                                               l1.Y - viewport_y, 
+                                                               (int)l1.widths[p2], 
+                                                               l1.height
+                                                               )
+                                               );
                                #endif
 
-                               owner.Invalidate(
-                                       new Rectangle(
-                                               (int)l1.widths[p1] + l1.align_shift - viewport_x, 
-                                               l1.Y - viewport_y, 
-                                               (int)l2.widths[p2] - (int)l1.widths[p1] + 1, 
-                                               l1.height
-                                       )
-                               );
+                               owner.Invalidate(new Rectangle (
+                                       (int)l1.widths[p1] + l1.X - viewport_x, 
+                                       l1.Y - viewport_y, 
+                                       endpoint - (int)l1.widths[p1] + 1, 
+                                       l1.height));
                                return;
                        }
 
                        #if Debug
-                               Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Start  => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l1.widths[p1] + l1.align_shift - viewport_x, l1.Y - viewport_y, viewport_width, l1.height);
+                               Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Start  => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height);
+                               Console.WriteLine ("invalidate start line:  {0}  position:  {1}", l1.text, p1);
                        #endif
 
                        // Three invalidates:
                        // First line from start
-                       owner.Invalidate(new Rectangle((int)l1.widths[p1] + l1.align_shift - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
+                       owner.Invalidate(new Rectangle((int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
 
+                       
                        // lines inbetween
                        if ((l1.line_no + 1) < l2.line_no) {
                                int     y;
 
                                y = GetLine(l1.line_no + 1).Y;
-                               owner.Invalidate(new Rectangle(0, y - viewport_y, viewport_width, GetLine(l2.line_no).Y - y - viewport_y));
+                               owner.Invalidate(new Rectangle(0, 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, GetLine(l2.line_no).Y - y - viewport_y);
+                                       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);
                                #endif
                        }
+                       
 
                        // Last line to end
-                       owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.align_shift - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height));
+                       owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.X - viewport_x, 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.align_shift - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height);
+                               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
                }
 
@@ -3204,9 +3367,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
                }
 
-               internal void SetSelectionStart(Line start, int start_pos) {
+               internal void SetSelectionStart(Line start, int start_pos, bool invalidate) {
                        // Invalidate from the previous to the new start pos
-                       Invalidate(selection_start.line, selection_start.pos, start, start_pos);
+                       if (invalidate)
+                               Invalidate(selection_start.line, selection_start.pos, start, start_pos);
 
                        selection_start.line = start;
                        selection_start.pos = start_pos;
@@ -3225,10 +3389,11 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                SetSelectionVisible (false);
                        }
 
-                       Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+                       if (invalidate)
+                               Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
                }
 
-               internal void SetSelectionStart(int character_index) {
+               internal void SetSelectionStart(int character_index, bool invalidate) {
                        Line    line;
                        LineTag tag;
                        int     pos;
@@ -3238,10 +3403,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
 
                        CharIndexToLineTag(character_index, out line, out tag, out pos);
-                       SetSelectionStart(line, pos);
+                       SetSelectionStart(line, pos, invalidate);
                }
 
-               internal void SetSelectionEnd(Line end, int end_pos) {
+               internal void SetSelectionEnd(Line end, int end_pos, bool invalidate) {
 
                        if (end == selection_end.line && end_pos == selection_start.pos) {
                                selection_anchor.line = selection_start.line;
@@ -3277,14 +3442,15 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
                                SetSelectionVisible (true);
-                               Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+                               if (invalidate)
+                                       Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
                        } else {
                                SetSelectionVisible (false);
                                // ?? Do I need to invalidate here, tests seem to work without it, but I don't think they should :-s
                        }
                }
 
-               internal void SetSelectionEnd(int character_index) {
+               internal void SetSelectionEnd(int character_index, bool invalidate) {
                        Line    line;
                        LineTag tag;
                        int     pos;
@@ -3294,7 +3460,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
 
                        CharIndexToLineTag(character_index, out line, out tag, out pos);
-                       SetSelectionEnd(line, pos);
+                       SetSelectionEnd(line, pos, invalidate);
                }
 
                internal void SetSelection(Line start, int start_pos) {
@@ -3329,8 +3495,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                return string.Empty;
                        }
 
-                       if (!multiline || (selection_start.line == selection_end.line)) {
-                               return selection_start.line.text.ToString(selection_start.pos, selection_end.pos - selection_start.pos);
+                       if (selection_start.line == selection_end.line) {
+                               return selection_start.line.text.ToString (selection_start.pos, selection_end.pos - selection_start.pos);
                        } else {
                                StringBuilder   sb;
                                int             i;
@@ -3358,13 +3524,14 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                internal void ReplaceSelection(string s, bool select_new) {
                        int             i;
 
-                       undo.BeginCompoundAction ();
-
+                       int selection_pos_on_line = selection_start.pos;
                        int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos);
+                       SuspendRecalc ();
+
                        // First, delete any selected text
                        if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) {
-                               if (!multiline || (selection_start.line == selection_end.line)) {
-                                       undo.RecordDeleteChars(selection_start.line, selection_start.pos + 1, selection_end.pos - selection_start.pos);
+                               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);
 
@@ -3377,10 +3544,13 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                        start = selection_start.line.line_no;
                                        end = selection_end.line.line_no;
 
-                                       undo.RecordDelete(selection_start.line, selection_start.pos + 1, selection_end.line, selection_end.pos);
+                                       undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+
+                                       InvalidateSelectionArea ();
 
                                        // Delete first line
                                        DeleteChars(selection_start.tag, 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);
@@ -3400,9 +3570,11 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
                        }
 
-                       Insert(selection_start.line, null, selection_start.pos, true, s);
+
+                       Insert(selection_start.line, selection_start.pos, false, s);
                        undo.RecordInsertString (selection_start.line, selection_start.pos, s);
-                       
+                       ResumeRecalc (false);
+
                        if (!select_new) {
                                CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line,
                                                out selection_start.tag, out selection_start.pos);
@@ -3429,7 +3601,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                SetSelectionVisible (true);
                        }
 
-                       undo.EndCompoundAction ();
+                       PositionCaret (selection_start.line, selection_start.pos);
+                       UpdateView (selection_start.line, selection_pos_on_line);
                }
 
                internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
@@ -3445,14 +3618,14 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                line = GetLine(i);
 
                                start = chars;
-                               chars += line.text.Length + crlf_size;
+                               chars += line.text.Length;
 
                                if (index <= chars) {
                                        // we found the line
                                        tag = line.tags;
 
                                        while (tag != null) {
-                                               if (index < (start + tag.start + tag.length)) {
+                                               if (index < (start + tag.start + tag.length - 1)) {
                                                        line_out = line;
                                                        tag_out = LineTag.GetFinalTag (tag);
                                                        pos = index - start;
@@ -3499,7 +3672,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        // Count the lines in the middle
 
                        for (i = 1; i < line.line_no; i++) {
-                               length += GetLine(i).text.Length + crlf_size;
+                               length += GetLine(i).text.Length;
                        }
 
                        length += pos;
@@ -3512,7 +3685,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                return 0;
                        }
 
-                       if (!multiline || (selection_start.line == selection_end.line)) {
+                       if (selection_start.line == selection_end.line) {
                                return selection_end.pos - selection_start.pos;
                        } else {
                                int     i;
@@ -3529,7 +3702,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                                if (start < end) {
                                        for (i = start; i < end; i++) {
-                                               length += GetLine(i).text.Length + crlf_size;
+                                               Line line = GetLine (i);
+                                               length += line.text.Length + LineEndingLength (line.ending);
                                        }
                                }
 
@@ -3602,7 +3776,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                }
 
                internal Line ParagraphStart(Line line) {
-                       while (line.soft_break) {
+                       while (line.ending == LineEnding.Wrap) {
                                line = GetLine(line.line_no - 1);
                        }
                        return line;
@@ -3611,9 +3785,9 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                internal Line ParagraphEnd(Line line) {
                        Line    l;
    
-                       while (line.soft_break) {
+                       while (line.ending == LineEnding.Wrap) {
                                l = GetLine(line.line_no + 1);
-                               if ((l == null) || (!l.soft_break)) {
+                               if ((l == null) || (l.ending != LineEnding.Wrap)) {
                                        break;
                                }
                                line = l;
@@ -3621,19 +3795,34 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        return line;
                }
 
-               /// <summary>Give it a Y pixel coordinate and it returns the Line covering that Y coordinate</summary>
-               internal Line GetLineByPixel(int y, bool exact) {
+               /// <summary>Give it a pixel offset coordinate and it returns the Line covering that are (offset
+               /// is either X or Y depending on if we are multiline
+               /// </summary>
+               internal Line GetLineByPixel (int offset, bool exact)
+               {
                        Line    line = document;
                        Line    last = null;
 
-                       while (line != sentinel) {
-                               last = line;
-                               if ((y >= line.Y) && (y < (line.Y+line.height))) {
-                                       return line;
-                               } else if (y < line.Y) {
-                                       line = line.left;
-                               } else {
-                                       line = line.right;
+                       if (multiline) {
+                               while (line != sentinel) {
+                                       last = line;
+                                       if ((offset >= line.Y) && (offset < (line.Y+line.height))) {
+                                               return line;
+                                       } else if (offset < line.Y) {
+                                               line = line.left;
+                                       } else {
+                                               line = line.right;
+                                       }
+                               }
+                       } else {
+                               while (line != sentinel) {
+                                       last = line;
+                                       if ((offset >= line.X) && (offset < (line.X + line.Width)))
+                                               return line;
+                                       else if (offset < line.X)
+                                               line = line.left;
+                                       else
+                                               line = line.right;
                                }
                        }
 
@@ -3656,7 +3845,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        tag = line.tags;
 
                        // Alignment adjustment
-                       x += line.align_shift;
+                       x += line.X;
 
                        while (true) {
                                if (x >= tag.X && x < (tag.X+tag.width)) {
@@ -3692,34 +3881,37 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        Line    line;
                        LineTag tag;
 
-                       line = GetLineByPixel(y, false);
+                       line = GetLineByPixel(multiline ? y : x, false);
                        tag = line.tags;
 
-                       // Adjust for alignment
-                       x -= line.align_shift;
+                       /// Special case going leftwards of the first tag
+                       if (x < tag.X) {
+                               index = 0;
+                               return LineTag.GetFinalTag (tag);
+                       }
 
                        while (true) {
                                if (x >= tag.X && x < (tag.X+tag.width)) {
                                        int     end;
 
-                                       end = tag.start + tag.length - 1;
+                                       end = tag.TextEnd;
 
-                                       for (int pos = tag.start-1; pos < end; pos++) {
+                                       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.widths[pos] + ((line.widths[pos+1]-line.widths[pos])/2))) {
+                                               if (x < (line.X + line.widths[pos] + ((line.widths[pos+1]-line.widths[pos])/2))) {
                                                        index = pos;
-                                                       return tag;
+                                                       return LineTag.GetFinalTag (tag);
                                                }
                                        }
                                        index=end;
-                                       return tag;
+                                       return LineTag.GetFinalTag (tag);
                                }
                                if (tag.next != null) {
                                        tag = tag.next;
                                } else {
-                                       index = line.text.Length;
-                                       return tag;
+                                       index = line.TextLengthWithoutEnding ();
+                                       return LineTag.GetFinalTag (tag);
                                }
                        }
                }
@@ -3727,62 +3919,39 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                /// <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, Brush color) {
-                       Line    l;
-
-                       // First, format the first line
-                       if (start_line != end_line) {
-                               // First line
-                               LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, font, color);
-
-                               // Format last line
-                               LineTag.FormatText(end_line, 1, end_pos, font, color);
-
-                               // Now all the lines inbetween
-                               for (int i = start_line.line_no + 1; i < end_line.line_no; i++) {
-                                       l = GetLine(i);
-                                       LineTag.FormatText(l, 1, l.text.Length, font, color);
-                               }
-                       } else {
-                               // Special case, single line
-                               LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color);
-                       }
-               }
-
-               /// <summary>Re-format areas of the 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>
-               /// <param name="font">Font specifying attributes</param>
-               /// <param name="color">Color (or NULL) to apply</param>
-               /// <param name="apply">Attributes from font and color to apply</param>
-               internal void FormatText(Line start_line, int start_pos, Line end_line, int end_pos, FontDefinition attributes) {
+               internal void FormatText (Line start_line, int start_pos, Line end_line, int end_pos, Font font,
+                               SolidBrush color, SolidBrush back_color, FormatSpecified specified)
+               {
                        Line    l;
 
                        // First, format the first line
                        if (start_line != end_line) {
                                // First line
-                               LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, attributes);
+                               LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, font, color, back_color, specified);
 
                                // Format last line
-                               LineTag.FormatText(end_line, 1, end_pos - 1, attributes);
+                               LineTag.FormatText(end_line, 1, end_pos, font, color, back_color, specified);
 
                                // Now all the lines inbetween
                                for (int i = start_line.line_no + 1; i < end_line.line_no; i++) {
                                        l = GetLine(i);
-                                       LineTag.FormatText(l, 1, l.text.Length, attributes);
+                                       LineTag.FormatText(l, 1, l.text.Length, font, color, back_color, specified);
                                }
                        } else {
                                // Special case, single line
-                               LineTag.FormatText(start_line, start_pos, end_pos - start_pos, attributes);
+                               LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color, back_color, specified);
                        }
                }
 
-               internal void RecalculateAlignments() {
+               internal void RecalculateAlignments ()
+               {
                        Line    line;
                        int     line_no;
 
                        line_no = 1;
 
+
+
                        while (line_no <= lines) {
                                line = GetLine(line_no);
 
@@ -3795,7 +3964,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                                line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
                                                break;
                                        case HorizontalAlignment.Right:
-                                               line.align_shift = viewport_width - (int)line.widths[line.text.Length];
+                                               line.align_shift = viewport_width - (int)line.widths[line.text.Length] - right_margin;
                                                break;
                                        }
                                }
@@ -3824,20 +3993,24 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
                        Line    line;
                        int     line_no;
-                       int     Y;
+                       int     offset;
                        int     new_width;
                        bool    changed;
                        int     shift;
 
-                       if (no_recalc) {
+                       if (recalc_suspended > 0) {
                                recalc_pending = true;
-                               recalc_start = start;
-                               recalc_end = end;
+                               recalc_start = Math.Min (recalc_start, start);
+                               recalc_end = Math.Max (recalc_end, end);
                                recalc_optimize = optimize;
                                return false;
                        }
 
-                       Y = GetLine(start).Y;
+                       // Fixup the positions, they can go kinda nuts
+                       start = Math.Max (start, 1);
+                       end = Math.Min (end, lines);
+
+                       offset = GetLine(start).offset;
                        line_no = start;
                        new_width = 0;
                        shift = this.lines;
@@ -3849,7 +4022,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        while (line_no <= (end + this.lines - shift)) {
                                line = GetLine(line_no++);
-                               line.Y = Y;
+                               line.offset = offset;
 
                                if (!calc_pass) {
                                        if (!optimize) {
@@ -3888,7 +4061,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                        }
                                }
 
-                               Y += line.height;
+                               if (multiline)
+                                       offset += line.height;
+                               else
+                                       offset += (int) line.widths [line.text.Length];
 
                                if (line_no > lines) {
                                        break;
@@ -3931,20 +4107,21 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
                }
 
-               internal static bool IsWordSeparator(char ch) {
-                       switch(ch) {
-                               case ' ':
-                               case '\t':
-                               case '(':
-                               case ')': {
-                                       return true;
-                               }
-
-                               default: {
-                                       return false;
-                               }
+               internal static bool IsWordSeparator (char ch)
+               {
+                       switch (ch) {
+                       case ' ':
+                       case '\t':
+                       case '(':
+                       case ')':
+                       case '\r':
+                       case '\n':
+                               return true;
+                       default:
+                               return false;
                        }
                }
+
                internal int FindWordSeparator(Line line, int pos, bool forward) {
                        int len;
 
@@ -4067,7 +4244,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                                Line    prev_line;
 
                                                prev_line = GetLine(line_no - 1);
-                                               if (prev_line.soft_break) {
+                                               if (prev_line.ending == LineEnding.Wrap) {
                                                        if (IsWordSeparator(prev_line.text[prev_line.text.Length - 1])) {
                                                                word = true;
                                                        } else {
@@ -4097,6 +4274,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
 
                                while (pos < line_len) {
+
                                        if (word_option && (current == search_string.Length)) {
                                                if (IsWordSeparator(line.text[pos])) {
                                                        if (!reverse) {
@@ -4117,6 +4295,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                        }
 
                                        if (c == search_string[current]) {
+                                               
                                                if (current == 0) {
                                                        result.line = line;
                                                        result.pos = pos;
@@ -4151,7 +4330,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                                if (word_option) {
                                        // Mark that we just saw a word boundary
-                                       if (!line.soft_break) {
+                                       if (line.ending != LineEnding.Wrap || line.line_no == lines - 1) {
                                                word = true;
                                        }
 
@@ -4258,21 +4437,62 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                #endregion      // Administrative
        }
 
+       internal class PictureTag : LineTag {
+
+               internal RTF.Picture picture;
+
+               internal PictureTag (Line line, int start, RTF.Picture picture) : base (line, start)
+               {
+                       this.picture = picture;
+               }
+
+               public override bool IsTextTag {
+                       get { return false; }
+               }
+
+               internal override SizeF SizeOfPosition (Graphics dc, int pos)
+               {
+                       return picture.Size;
+               }
+
+               internal override int MaxHeight ()
+               {
+                       return (int) (picture.Height + 0.5F);
+               }
+
+               internal override void Draw (Graphics dc, Brush brush, float xoff, float y, int start, int end)
+               {
+                       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)
+               {
+                       picture.DrawImage (dc, xoff + + line.widths [start], y, false);
+               }
+
+               public override string Text ()
+               {
+                       return "I";
+               }
+       }
+
        internal class LineTag {
                #region Local Variables;
                // Payload; formatting
                internal Font           font;           // System.Drawing.Font object for this tag
-               internal Brush          color;          // System.Drawing.Brush object
+               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 int            length;         // length, in chars
                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            X;              // X location of the text this tag describes
-               internal float          width;          // Width 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
 
@@ -4283,18 +4503,117 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                #endregion;
 
                #region Constructors
-               internal LineTag(Line line, int start, int length) {
+               internal LineTag(Line line, int start) {
                        this.line = line;
                        this.start = start;
-                       this.length = length;
-                       this.X = 0;
-                       this.width = 0;
                }
                #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
@@ -4304,13 +4623,13 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                return null;
                        }
 
-                       new_tag = new LineTag(line, pos, start + length - pos);
-                       new_tag.color = color;
-                       new_tag.font = font;
-                       this.length -= new_tag.length;
+                       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;
                        }
@@ -4318,54 +4637,25 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        return new_tag;
                }
 
-               ///<summary>Create new font and brush from existing font and given new attributes. Returns true if fontheight changes</summary>
-               internal static bool GenerateTextFormat(Font font_from, Brush color_from, FontDefinition attributes, out Font new_font, out Brush new_color) {
-                       float           size;
-                       string          face;
-                       FontStyle       style;
-                       GraphicsUnit    unit;
-
-                       if (attributes.font_obj == null) {
-                               size = font_from.SizeInPoints;
-                               unit = font_from.Unit;
-                               face = font_from.Name;
-                               style = font_from.Style;
-
-                               if (attributes.face != null) {
-                                       face = attributes.face;
-                               }
-                               
-                               if (attributes.size != 0) {
-                                       size = attributes.size;
-                               }
-
-                               style |= attributes.add_style;
-                               style &= ~attributes.remove_style;
-
-                               // Create new font
-                               new_font = new Font(face, size, style, unit);
-                       } else {
-                               new_font = attributes.font_obj;
-                       }
-
-                       // Create 'new' color brush
-                       if (attributes.color != Color.Empty) {
-                               new_color = new SolidBrush(attributes.color);
-                       } else {
-                               new_color = color_from;
-                       }
+               public virtual string Text ()
+               {
+                       return line.text.ToString (start - 1, length);
+               }
 
-                       if (new_font.Height == font_from.Height) {
-                               return false;
-                       }
-                       return true;
+               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, Brush color) {
+               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;
@@ -4373,7 +4663,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        bool    retval = false;         // Assume line-height doesn't change
 
                        // Too simple?
-                       if (font.Height != line.height) {
+                       if (((FormatSpecified.Font & specified) == FormatSpecified.Font) && font.Height != line.height) {
                                retval = true;
                        }
                        line.recalc = true;             // This forces recalculation of the line in RecalculateDocument
@@ -4389,123 +4679,43 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        // Common special case
                        if ((start == 1) && (length == tag.length)) {
                                tag.ascent = 0;
-                               tag.font = font;
-                               tag.color = color;
+                               SetFormat (tag, font, color, back_color, specified);
                                return retval;
                        }
 
-                       //Console.WriteLine("Finding tag for {0} {1}", line, start);
-                       start_tag = FindTag(line, start);
-                       end_tag = FindTag (line, end);
-
-                       if (start_tag == null) {        // FIXME - is there a better way to handle this, or do we even need it?
-                               throw new Exception(String.Format("Could not find start_tag in document at line {0} position {1}", line.line_no, start));
-                       }
-
-                       tag = new LineTag(line, start, length);
-                       tag.font = font;
-                       tag.color = color;
+                       start_tag = FindTag (line, start);
+                       tag = start_tag.Break (start);
 
-                       if (start == 1) {
-                               line.tags = tag;
-                       }
-                       //Console.WriteLine("Start tag: '{0}'", start_tag!=null ? start_tag.ToString() : "NULL");
-                       if (start_tag.start == start) {
-                               tag.next = start_tag;
-                               tag.previous = start_tag.previous;
-                               if (start_tag.previous != null) {
-                                       start_tag.previous.next = tag;
-                               }
-                               start_tag.previous = tag;
-                       } else {
-                               tag.next = end_tag;
-
-                               if (end_tag != null) {
-                                       // Shorten up the end tag
-                                       end_tag.previous = tag;
-                                       end_tag.length = end - start_tag.start + start_tag.length;
-                                       end_tag.start = end;
-                               }
-                       }
-
-                       // Elimination loop
-                       tag = tag.next;
-                       while (tag != end_tag) {
-                               if ((tag.start + tag.length) <= end) {
-                                       // remove the tag
-                                       tag.previous.next = tag.next;
-                                       if (tag.next != null) {
-                                               tag.next.previous = tag.previous;
-                                       }
-                                       tag = tag.previous;
-                               }
+                       while (tag != null && tag.end <= end) {
+                               SetFormat (tag, font, color, back_color, specified);
                                tag = tag.next;
                        }
 
-                       return retval;
-               }
-
-               /// <summary>Applies font attributes specified to characters starting at 'start' for 'length' chars; 
-               /// Breaks tags at start and end point, keeping middle tags with altered attributes.
-               /// 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, FontDefinition attributes) {
-                       LineTag tag;
-                       LineTag start_tag;
-                       LineTag end_tag;
-                       bool    retval = false;         // Assume line-height doesn't change
-
-                       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;
-
-                       // Common special case
-                       if ((start == 1) && (length == tag.length)) {
-                               tag.ascent = 0;
-                               GenerateTextFormat(tag.font, tag.color, attributes, out tag.font, out tag.color);
+                       if (tag != null && tag.end == end)
                                return retval;
-                       }
 
-                       start_tag = FindTag(line, start);
-                       
-                       if (start_tag == null) {
-                               if (length == 0) {
-                                       // We are 'starting' after all valid tags; create a new tag with the right attributes
-                                       start_tag = FindTag(line, line.text.Length - 1);
-                                       start_tag.next = new LineTag(line, line.text.Length + 1, 0);
-                                       start_tag.next.font = start_tag.font;
-                                       start_tag.next.color = start_tag.color;
-                                       start_tag.next.previous = start_tag;
-                                       start_tag = start_tag.next;
-                               } else {
-                                       throw new Exception(String.Format("Could not find start_tag in document at line {0} position {1}", line.line_no, start));
-                               }
-                       } else {
-                               start_tag = start_tag.Break(start);
-                       }
+                       /// Now do the last tag
+                       end_tag = FindTag (line, end);
 
-                       end_tag = FindTag(line, start + length);
                        if (end_tag != null) {
-                               end_tag = end_tag.Break(start + length);
+                               end_tag.Break (end);
+                               SetFormat (end_tag, font, color, back_color, specified);
                        }
 
-                       // start_tag or end_tag might be null; we're cool with that
-                       // we now walk from start_tag to end_tag, applying new attributes
-                       tag = start_tag;
-                       while ((tag != null) && tag != end_tag) {
-                               if (LineTag.GenerateTextFormat(tag.font, tag.color, attributes, out tag.font, out tag.color)) {
-                                       retval = true;
-                               }
-                               tag = tag.next;
-                       }
                        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) {
@@ -4518,7 +4728,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        }
 
                        while (tag != null) {
-                               if ((tag.start <= pos) && (pos < (tag.start+tag.length))) {
+                               if ((tag.start <= pos) && (pos <= tag.end)) {
                                        return GetFinalTag (tag);
                                }
 
@@ -4534,8 +4744,9 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                {
                        LineTag res = tag;
 
-                       while (res.next != null && res.next.length == 0)
+                       while (res.length == 0 && res.next != null && res.next.length == 0)
                                res = res.next;
+
                        return res;
                }
 
@@ -4545,8 +4756,6 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                return false;
                        }
 
-                       this.width += other.width;
-                       this.length += other.length;
                        this.next = other.next;
                        if (this.next != null) {
                                this.next.previous = this;
@@ -4563,14 +4772,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                return false;
                        }
                        if (this.start != 1) {
-                               this.previous.length += this.length;
-                               this.previous.width = -1;
                                this.previous.next = this.next;
                                this.next.previous = this.previous;
                        } else {
                                this.next.start = 1;
-                               this.next.length += this.length;
-                               this.next.width = -1;
                                this.line.tags = this.next;
                                this.next.previous = null;
                        }
@@ -4596,6 +4801,9 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                        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;
                        }
@@ -4609,23 +4817,25 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                public override string ToString() {
                        if (length > 0)
-                               return "Tag starts at index " + this.start + "length " + this.length + " text: " + this.line.Text.Substring(this.start-1, this.length) + "Font " + this.font.ToString();
+                               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 UndoClass {
+       internal class UndoManager {
+
                internal enum ActionType {
-                       InsertChar,
+
+                       Typing,
+
+                       // This is basically just cut & paste
                        InsertString,
-                       DeleteChar,
-                       DeleteChars,
-                       CursorMove,
-                       Mark,
-                       CompoundBegin,
-                       CompoundEnd,
+                       DeleteString,
+
+                       UserActionBegin,
+                       UserActionEnd
                }
 
                internal class Action {
@@ -4639,199 +4849,247 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                private Document        document;
                private Stack           undo_actions;
                private Stack           redo_actions;
-               private int             caret_line;
-               private int             caret_pos;
+
+               //private int           caret_line;
+               //private int           caret_pos;
+
+               // When performing an action, we lock the queue, so that the action can't be undone
+               private bool locked;
                #endregion      // Local Variables
 
                #region Constructors
-               internal UndoClass(Document doc) {
-                       document = doc;
-                       undo_actions = new Stack(50);
-                       redo_actions = new Stack(50);
+               internal UndoManager (Document document)
+               {
+                       this.document = document;
+                       undo_actions = new Stack (50);
+                       redo_actions = new Stack (50);
                }
                #endregion      // Constructors
 
                #region Properties
-               [MonoTODO("Change this to be configurable")]
-               internal int UndoLevels {
-                       get {
-                               return undo_actions.Count;
-                       }
+               internal bool CanUndo {
+                       get { return undo_actions.Count > 0; }
                }
 
-               [MonoTODO("Change this to be configurable")]
-               internal int RedoLevels {
-                       get {
-                               return redo_actions.Count;
-                       }
+               internal bool CanRedo {
+                       get { return redo_actions.Count > 0; }
                }
 
-               [MonoTODO("Come up with good naming and localization")]
-               internal string UndoName {
+               internal string UndoActionName {
                        get {
-                               Action action;
-
-                               action = (Action)undo_actions.Peek();
-                               switch(action.type) {
-                                       case ActionType.InsertChar: {
-                                               Locale.GetText("Insert character");
-                                               break;
-                                       }
-
-                                       case ActionType.DeleteChar: {
-                                               Locale.GetText("Delete character");
-                                               break;
-                                       }
-
-                                       case ActionType.InsertString: {
-                                               Locale.GetText("Insert string");
-                                               break;
-                                       }
-
-                                       case ActionType.DeleteChars: {
-                                               Locale.GetText("Delete string");
-                                               break;
-                                       }
-
-                                       case ActionType.CursorMove: {
-                                               Locale.GetText("Cursor move");
-                                               break;
-                                       }
+                               foreach (Action action in undo_actions) {
+                                       if (action.type == ActionType.UserActionBegin)
+                                               return (string) action.data;
+                                       if (action.type == ActionType.Typing)
+                                               return Locale.GetText ("Typing");
                                }
-                               return null;
+                               return String.Empty;
                        }
                }
 
-               internal string RedoName() {
-                       return null;
+               internal string RedoActionName {
+                       get {
+                               foreach (Action action in redo_actions) {
+                                       if (action.type == ActionType.UserActionBegin)
+                                               return (string) action.data;
+                                       if (action.type == ActionType.Typing)
+                                               return Locale.GetText ("Typing");
+                               }
+                               return String.Empty;
+                       }
                }
                #endregion      // Properties
 
                #region Internal Methods
-               internal void Clear() {
+               internal void Clear ()
+               {
                        undo_actions.Clear();
                        redo_actions.Clear();
                }
 
-               internal void Undo() {
+               internal void Undo ()
+               {
                        Action action;
-                       int compound_stack = 0;
+                       bool user_action_finished = false;
 
-                       if (undo_actions.Count == 0) {
+                       if (undo_actions.Count == 0)
                                return;
-                       }
 
-                       
+                       // Nuke the redo queue
+                       redo_actions.Clear ();
 
+                       locked = true;
                        do {
-                               action = (Action)undo_actions.Pop();
+                               Line start;
+                               action = (Action) undo_actions.Pop ();
 
                                // Put onto redo stack
                                redo_actions.Push(action);
 
                                // Do the thing
                                switch(action.type) {
-                               case ActionType.CompoundEnd:
-                                       compound_stack++;
+
+                               case ActionType.UserActionBegin:
+                                       user_action_finished = true;
                                        break;
 
-                               case ActionType.CompoundBegin:
-                                       compound_stack--;
+                               case ActionType.UserActionEnd:
+                                       // noop
                                        break;
 
                                case ActionType.InsertString:
-                                       document.DeleteMultiline (document.GetLine (action.line_no),
-                                                       action.pos, ((string) action.data).Length + 1);
+                                       start = document.GetLine (action.line_no);
+                                       document.SuspendUpdate ();
+                                       document.DeleteMultiline (start, action.pos, ((string) action.data).Length + 1);
+                                       document.PositionCaret (start, action.pos);
+                                       document.SetSelectionToCaret (true);
+                                       document.ResumeUpdate (true);
                                        break;
 
-                               case ActionType.InsertChar: {
-                                       // FIXME - implement me
+                               case ActionType.Typing:
+                                       start = document.GetLine (action.line_no);
+                                       document.SuspendUpdate ();
+                                       document.DeleteMultiline (start, action.pos, ((StringBuilder) action.data).Length);
+                                       document.PositionCaret (start, action.pos);
+                                       document.SetSelectionToCaret (true);
+                                       document.ResumeUpdate (true);
+
+                                       // This is an open ended operation, so only a single typing operation can be undone at once
+                                       user_action_finished = true;
                                        break;
-                               }
-                                       
-                               case ActionType.DeleteChars: {
-                                       this.Insert(document.GetLine(action.line_no), action.pos, (Line)action.data);
-                                       Undo(); // Grab the cursor location
+
+                               case ActionType.DeleteString:
+                                       start = document.GetLine (action.line_no);
+                                       document.SuspendUpdate ();
+                                       Insert (start, action.pos, (Line) action.data, true);
+                                       document.ResumeUpdate (true);
                                        break;
                                }
+                       } while (!user_action_finished && undo_actions.Count > 0);
 
-                               case ActionType.CursorMove: {
-                                       document.caret.line = document.GetLine(action.line_no);
-                                       if (document.caret.line == null) {
-                                               Undo();
-                                               break;
-                                       }
+                       locked = false;
+               }
 
-                                       document.caret.tag = document.caret.line.FindTag(action.pos);
-                                       document.caret.pos = action.pos;
-                                       document.caret.height = document.caret.tag.height;
+               internal void Redo ()
+               {
+                       Action action;
+                       bool user_action_finished = false;
 
-                                       if (document.owner.IsHandleCreated) {
-                                               XplatUI.DestroyCaret(document.owner.Handle);
-                                               XplatUI.CreateCaret(document.owner.Handle, 2, document.caret.height);
-                                               XplatUI.SetCaretPos(document.owner.Handle, (int)document.caret.tag.line.widths[document.caret.pos] + document.caret.line.align_shift - document.viewport_x, document.caret.line.Y + document.caret.tag.shift - document.viewport_y + Document.caret_shift);
+                       if (redo_actions.Count == 0)
+                               return;
 
-                                               document.DisplayCaret ();
-                                       }
+                       // You can't undo anything after redoing
+                       undo_actions.Clear ();
+
+                       locked = true;
+                       do {
+                               Line start;
+                               int start_index;
+
+                               action = (Action) redo_actions.Pop ();
+
+                               switch (action.type) {
+
+                               case ActionType.UserActionBegin:
+                                       //  Noop
+                                       break;
+
+                               case ActionType.UserActionEnd:
+                                       user_action_finished = true;
+                                       break;
+
+                               case ActionType.InsertString:
+                                       start = document.GetLine (action.line_no);
+                                       document.SuspendUpdate ();
+                                       start_index = document.LineTagToCharIndex (start, action.pos);
+                                       document.InsertString (start, action.pos, (string) action.data);
+                                       document.CharIndexToLineTag (start_index + ((string) action.data).Length,
+                                                       out document.caret.line, out document.caret.tag,
+                                                       out document.caret.pos);
+                                       document.UpdateCaret ();
+                                       document.SetSelectionToCaret (true);
+                                       document.ResumeUpdate (true);
+                                       break;
+
+                               case ActionType.Typing:
+                                       start = document.GetLine (action.line_no);
+                                       document.SuspendUpdate ();
+                                       start_index = document.LineTagToCharIndex (start, action.pos);
+                                       document.InsertString (start, action.pos, ((StringBuilder) action.data).ToString ());
+                                       document.CharIndexToLineTag (start_index + ((StringBuilder) action.data).Length,
+                                                       out document.caret.line, out document.caret.tag,
+                                                       out document.caret.pos);
+                                       document.UpdateCaret ();
+                                       document.SetSelectionToCaret (true);
+                                       document.ResumeUpdate (true);
+
+                                       // This is an open ended operation, so only a single typing operation can be undone at once
+                                       user_action_finished = true;
+                                       break;
+
+                               case ActionType.DeleteString:
+                                       start = document.GetLine (action.line_no);
+                                       document.SuspendUpdate ();
+                                       document.DeleteMultiline (start, action.pos, ((Line) action.data).text.Length);
+                                       document.PositionCaret (start, action.pos);
+                                       document.SetSelectionToCaret (true);
+                                       document.ResumeUpdate (true);
 
-                                       // FIXME - enable call
-                                       //if (document.CaretMoved != null) document.CaretMoved(this, EventArgs.Empty);
                                        break;
                                }
-                               }
-                       } while (compound_stack > 0);
-               }
+                       } while (!user_action_finished && redo_actions.Count > 0);
 
-               internal void Redo() {
-                       if (redo_actions.Count == 0) {
-                               return;
-                       }
+                       locked = false;
                }
                #endregion      // Internal Methods
 
                #region Private Methods
 
-               public void BeginCompoundAction ()
+               public void BeginUserAction (string name)
                {
-                       Action cb = new Action ();
-                       cb.type = ActionType.CompoundBegin;
+                       if (locked)
+                               return;
+
+                       Action ua = new Action ();
+                       ua.type = ActionType.UserActionBegin;
+                       ua.data = name;
 
-                       undo_actions.Push (cb);
+                       undo_actions.Push (ua);
                }
 
-               public void EndCompoundAction ()
+               public void EndUserAction ()
                {
-                       Action ce = new Action ();
-                       ce.type = ActionType.CompoundEnd;
+                       if (locked)
+                               return;
 
-                       undo_actions.Push (ce);
-               }
+                       Action ua = new Action ();
+                       ua.type = ActionType.UserActionEnd;
 
-               // pos = 1-based
-               public void RecordDeleteChars(Line line, int pos, int length) {
-                       RecordDelete(line, pos, line, pos + length - 1);
+                       undo_actions.Push (ua);
                }
 
                // start_pos, end_pos = 1 based
-               public void RecordDelete(Line start_line, int start_pos, Line end_line, int end_pos) {
-                       Line    l;
-                       Action  a;
+               public void RecordDeleteString (Line start_line, int start_pos, Line end_line, int end_pos)
+               {
+                       if (locked)
+                               return;
 
-                       l = Duplicate(start_line, start_pos, end_line, end_pos);
+                       Action  a = new Action ();
 
-                       a = new Action();
-                       a.type = ActionType.DeleteChars;
-                       a.data = l;
+                       // We cant simply store the string, because then formatting would be lost
+                       a.type = ActionType.DeleteString;
                        a.line_no = start_line.line_no;
-                       a.pos = start_pos - 1;
+                       a.pos = start_pos;
+                       a.data = Duplicate (start_line, start_pos, end_line, end_pos);
 
-                       // Record the cursor position before, since the actions will occur in reverse order
-                       RecordCursor();
                        undo_actions.Push(a);
                }
 
                public void RecordInsertString (Line line, int pos, string str)
                {
+                       if (locked || str.Length == 0)
+                               return;
+
                        Action a = new Action ();
 
                        a.type = ActionType.InsertString;
@@ -4842,35 +5100,34 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        undo_actions.Push (a);
                }
 
-               public void RecordCursor() {
-                       if (document.caret.line == null) {
+               public void RecordTyping (Line line, int pos, char ch)
+               {
+                       if (locked)
                                return;
-                       }
 
-                       RecordCursor(document.caret.line, document.caret.pos);
-               }
+                       Action a = null;
 
-               public void RecordCursor(Line line, int pos) {
-                       Action a;
-
-                       if ((line.line_no == caret_line) && (pos == caret_pos)) {
-                               return;
-                       }
+                       if (undo_actions.Count > 0)
+                               a = (Action) undo_actions.Peek ();
 
-                       caret_line = line.line_no;
-                       caret_pos = pos;
+                       if (a == null || a.type != ActionType.Typing) {
+                               a = new Action ();
+                               a.type = ActionType.Typing;
+                               a.data = new StringBuilder ();
+                               a.line_no = line.line_no;
+                               a.pos = pos;
 
-                       a = new Action();
-                       a.type = ActionType.CursorMove;
-                       a.line_no = line.line_no;
-                       a.pos = pos;
+                               undo_actions.Push (a);
+                       }
 
-                       undo_actions.Push(a);
+                       StringBuilder data = (StringBuilder) a.data;
+                       data.Append (ch);
                }
 
                // start_pos = 1-based
                // end_pos = 1-based
-               public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos) {
+               public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos)
+               {
                        Line    ret;
                        Line    line;
                        Line    current;
@@ -4879,9 +5136,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        int     start;
                        int     end;
                        int     tag_start;
-                       int     tag_length;
 
-                       line = new Line();
+                       line = new Line (start_line.document, start_line.ending);
                        ret = line;
 
                        for (int i = start_line.line_no; i <= end_line.line_no; i++) {
@@ -4890,7 +5146,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                if (start_line.line_no == i) {
                                        start = start_pos;
                                } else {
-                                       start = 1;
+                                       start = 0;
                                }
 
                                if (end_line.line_no == i) {
@@ -4899,12 +5155,15 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                        end = current.text.Length;
                                }
 
+                               if (end_pos == 0)
+                                       continue;
+
                                // Text for the tag
-                               line.text = new StringBuilder(current.text.ToString(start - 1, end - start + 1));
+                               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 - 1);
-                               while ((current_tag != null) && (current_tag.start < end)) {
+                               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))) {
                                                // start tag is within this tag
                                                tag_start = start;
@@ -4912,14 +5171,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                                tag_start = current_tag.start;
                                        }
 
-                                       if (end < (current_tag.start + current_tag.length)) {
-                                               tag_length = end - tag_start + 1;
-                                       } else {
-                                               tag_length = current_tag.start + current_tag.length - tag_start;
-                                       }
-                                       tag = new LineTag(line, tag_start - start + 1, tag_length);
-                                       tag.color = current_tag.color;
-                                       tag.font = current_tag.font;
+                                       tag = new LineTag(line, tag_start - start + 1);
+                                       tag.CopyFormattingFrom (current_tag);
 
                                        current_tag = current_tag.next;
 
@@ -4939,10 +5192,10 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
 
                                if ((i + 1) <= end_line.line_no) {
-                                       line.soft_break = current.soft_break;
+                                       line.ending = current.ending;
 
                                        // Chain them (we use right/left as next/previous)
-                                       line.right = new Line();
+                                       line.right = new Line (start_line.document, start_line.ending);
                                        line.right.left = line;
                                        line = line.right;
                                }
@@ -4952,7 +5205,8 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                }
 
                // Insert multi-line text at the given position; use formatting at insertion point for inserted text
-               internal void Insert(Line line, int pos, Line insert) {
+               internal void Insert(Line line, int pos, Line insert, bool select)
+               {
                        Line    current;
                        LineTag tag;
                        int     offset;
@@ -4980,7 +5234,7 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
 
                                tag.next = insert.tags;
                                line.text.Insert(offset, insert.text.ToString());
-                       
+
                                // Adjust start locations
                                tag = tag.next;
                                while (tag != null) {
@@ -4990,6 +5244,12 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                }
                                // Put it back together
                                document.Combine(line.line_no, line.line_no + 1);
+
+                               if (select) {
+                                       document.SetSelectionStart (line, pos, false);
+                                       document.SetSelectionEnd (line, pos + insert.text.Length, false);
+                               }
+
                                document.UpdateView(line, pos);
                                return;
                        }
@@ -4997,14 +5257,17 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                        first = line;
                        lines = 1;
                        current = insert;
+
                        while (current != null) {
+
                                if (current == insert) {
                                        // Inserting the first line we split the line (and make space)
-                                       document.Split(line, pos);
+                                       document.Split(line.line_no, pos);
                                        //Insert our tags at the end of the line
                                        tag = line.tags;
 
-                                       if (tag != null) {
+                                       
+                                       if (tag != null && tag.length != 0) {
                                                while (tag.next != null) {
                                                        tag = tag.next;
                                                }
@@ -5020,16 +5283,20 @@ if (owner.backcolor_set || (owner.Enabled && !owner.read_only)) {
                                                line.tags.previous = null;
                                                tag = line.tags;
                                        }
+
+                                       line.ending = current.ending;
                                } else {
                                        document.Split(line.line_no, 0);
                                        offset = 0;
                                        line.tags = current.tags;
                                        line.tags.previous = null;
+                                       line.ending = current.ending;
                                        tag = line.tags;
                                }
+
                                // Adjust start locations and line pointers
                                while (tag != null) {
-                                       tag.start += offset;
+                                       tag.start += offset - 1;
                                        tag.line = line;
                                        tag = tag.next;
                                }