2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextControl.cs
index 81c3ed347c917925d365e5441036bcce97b63387..ef847501a0af63f7be31f24780e21316608acfa5 100644 (file)
 
 // NOT COMPLETE
 
+// There's still plenty of things missing, I've got most of it planned, just hadn't had
+// the time to write it all yet.
+// Stuff missing (in no particular order):
+// - Align text after RecalculateLine
+// - Implement tag types to support wrapping (ie have a 'newline' tag), for images, etc.
+// - Wrap and recalculate lines
+// - Implement CaretPgUp/PgDown
+// - Finish selection calculations (invalidate only changed, more ways to select)
+// - Implement C&P
+
+
 #undef Debug
 
 using System;
@@ -35,12 +46,12 @@ using System.Drawing.Text;
 using System.Text;
 
 namespace System.Windows.Forms {
-       public enum LineColor {
+       internal enum LineColor {
                Red     = 0,
                Black   = 1
        }
 
-       public enum CaretDirection {
+       internal enum CaretDirection {
                CharForward,    // Move a char to the right
                CharBack,       // Move a char to the left
                LineUp,         // Move a line up
@@ -56,7 +67,7 @@ namespace System.Windows.Forms {
        }
 
        // Being cloneable should allow for nice line and document copies...
-       public class Line : ICloneable, IComparable {
+       internal class Line : ICloneable, IComparable {
                #region Local Variables
                // Stuff that matters for our line
                internal StringBuilder          text;                   // Characters for the line
@@ -67,11 +78,13 @@ namespace System.Windows.Forms {
                internal int                    Y;                      // Baseline
                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
 
                // Stuff that's important for the tree
                internal Line                   parent;                 // Our parent line
-               public Line                     left;                   // Line with smaller line number
-               public Line                     right;                  // Line with higher line number
+               internal Line                   left;                   // Line with smaller line number
+               internal Line                   right;                  // Line with higher line number
                internal LineColor              color;                  // We're doing a black/red tree. this is the node color
                internal int                    DEFAULT_TEXT_LEN;       // 
                internal static StringFormat    string_format;          // For calculating widths/heights
@@ -79,13 +92,14 @@ namespace System.Windows.Forms {
                #endregion      // Local Variables
 
                #region Constructors
-               public Line() {
+               internal Line() {
                        color = LineColor.Red;
                        left = null;
                        right = null;
                        parent = null;
                        text = null;
                        recalc = true;
+                       alignment = HorizontalAlignment.Left;
 
                        if (string_format == null) {
                                string_format = new StringFormat(StringFormat.GenericTypographic);
@@ -94,7 +108,7 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public Line(int LineNo, string Text, Font font) : this() {
+               internal Line(int LineNo, string Text, Font font, Brush color) : this() {
                        space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
 
                        text = new StringBuilder(Text, space);
@@ -103,9 +117,23 @@ namespace System.Windows.Forms {
                        widths = new float[space + 1];
                        tags = new LineTag(this, 1, text.Length);
                        tags.font = font;
+                       tags.color = color;
                }
 
-               public Line(int LineNo, string Text, LineTag tag) : this() {
+               internal Line(int LineNo, string Text, HorizontalAlignment align, Font font, Brush color) : this() {
+                       space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
+
+                       text = new StringBuilder(Text, space);
+                       line_no = LineNo;
+                       alignment = align;
+
+                       widths = new float[space + 1];
+                       tags = new LineTag(this, 1, text.Length);
+                       tags.font = font;
+                       tags.color = color;
+               }
+
+               internal Line(int LineNo, string Text, LineTag tag) : this() {
                        space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
 
                        text = new StringBuilder(Text, space);
@@ -117,8 +145,8 @@ namespace System.Windows.Forms {
 
                #endregion      // Constructors
 
-               #region Public Properties
-               public int Height {
+               #region Internal Properties
+               internal int Height {
                        get {
                                return height;
                        }
@@ -128,7 +156,7 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public int LineNo {
+               internal int LineNo {
                        get {
                                return line_no;
                        }
@@ -138,7 +166,7 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public string Text {
+               internal string Text {
                        get {
                                return text.ToString();
                        }
@@ -147,8 +175,21 @@ namespace System.Windows.Forms {
                                text = new StringBuilder(value, value.Length > DEFAULT_TEXT_LEN ? value.Length : DEFAULT_TEXT_LEN);
                        }
                }
+
+               internal HorizontalAlignment Alignment {
+                       get {
+                               return alignment;
+                       }
+
+                       set {
+                               if (alignment != value) {
+                                       alignment = value;
+                                       recalc = true;
+                               }
+                       }
+               }
 #if no
-               public StringBuilder Text {
+               internal StringBuilder Text {
                        get {
                                return text;
                        }
@@ -158,11 +199,11 @@ namespace System.Windows.Forms {
                        }
                }
 #endif
-               #endregion      // Public Properties
+               #endregion      // Internal Properties
 
-               #region Public Methods
+               #region Internal Methods
                // Make sure we always have enoughs space in text and widths
-               public void Grow(int minimum) {
+               internal void Grow(int minimum) {
                        int     length;
                        float[] new_widths;
 
@@ -184,7 +225,7 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public void Streamline() {
+               internal void Streamline() {
                        LineTag current;
                        LineTag next;
 
@@ -225,7 +266,7 @@ namespace System.Windows.Forms {
                }
 
                // Find the tag on a line based on the character position
-               public LineTag FindTag(int pos) {
+               internal LineTag FindTag(int pos) {
                        LineTag tag;
 
                        if (pos == 0) {
@@ -252,7 +293,7 @@ namespace System.Windows.Forms {
                // Go through all tags on a line and recalculate all size-related values
                // returns true if lineheight changed
                //
-               public bool RecalculateLine(Graphics g) {
+               internal bool RecalculateLine(Graphics g) {
                        LineTag tag;
                        int     pos;
                        int     len;
@@ -285,6 +326,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;
+
                                        // Check if we're the tallest on the line (so far)
                                        if (tag.height > this.height) {
                                                this.height = tag.height;               // Yep; make sure the line knows
@@ -300,9 +342,10 @@ namespace System.Windows.Forms {
                                                LineTag         t;
 
                                                // We have a tag that has a taller ascent than the line;
+
                                                t = tags;
                                                while (t != tag) {
-                                                       t.shift += tag.ascent - this.ascent;
+                                                       t.shift = tag.ascent - t.ascent;
                                                        t = t.next;
                                                }
 
@@ -322,6 +365,7 @@ namespace System.Windows.Forms {
                                        tag = tag.next;
                                        if (tag != null) {
                                                tag.width = 0;
+                                               tag.shift = 0;
                                        }
                                }
                        }
@@ -336,7 +380,7 @@ namespace System.Windows.Forms {
                        }
                        return false;
                }
-               #endregion      // Public Methods
+               #endregion      // Internal Methods
 
                #region Administrative
                public int CompareTo(object obj) {
@@ -375,7 +419,7 @@ namespace System.Windows.Forms {
                        return clone;
                }
 
-               public object CloneLine() {
+               internal object CloneLine() {
                        Line    clone;
 
                        clone = new Line();
@@ -405,6 +449,9 @@ namespace System.Windows.Forms {
                        return false;
                }
 
+               public override int GetHashCode() {\r
+                       return base.GetHashCode ();\r
+               }\r
 
                public override string ToString() {
                        return "Line " + line_no;
@@ -413,7 +460,7 @@ namespace System.Windows.Forms {
                #endregion      // Administrative
        }
 
-       public class Document : ICloneable, IEnumerable {
+       internal class Document : ICloneable, IEnumerable {
                #region Structures
                internal struct Marker {
                        internal Line           line;
@@ -426,21 +473,18 @@ namespace System.Windows.Forms {
                #region Local Variables
                private Line            document;
                private int             lines;
-               private static Line     sentinel;
+               private Line            sentinel;
                private Line            last_found;
                private int             document_id;
                private Random          random = new Random();
 
                internal bool           multiline;
-
-               private Line            selection_start_line;
-               private int             selection_start_pos;
-               private Line            selection_end_line;
-               private int             selection_end_pos;
+               internal bool           wrap;
 
                internal Marker         caret;
                internal Marker         selection_start;
                internal Marker         selection_end;
+               internal bool           selection_visible;
 
                internal int            viewport_x;
                internal int            viewport_y;             // The visible area of the document
@@ -452,7 +496,7 @@ namespace System.Windows.Forms {
                #endregion      // Local Variables
 
                #region Constructors
-               public Document(Control owner) {
+               internal Document(Control owner) {
                        lines = 0;
 
                        this.owner = owner;
@@ -467,19 +511,27 @@ namespace System.Windows.Forms {
                        last_found = sentinel;
 
                        // We always have a blank line
-                       Add(1, "", owner.Font);
+                       Add(1, "", owner.Font, new SolidBrush(owner.ForeColor));
                        this.RecalculateDocument(owner.CreateGraphics());
                        PositionCaret(0, 0);
                        lines=1;
 
+                       selection_visible = false;
+                       selection_start.line = this.document;
+                       selection_start.pos = 0;
+                       selection_end.line = this.document;
+                       selection_end.pos = 0;
+
+
+
                        // Default selection is empty
 
                        document_id = random.Next();
                }
                #endregion
 
-               #region Public Properties
-               public Line Root {
+               #region Internal Properties
+               internal Line Root {
                        get {
                                return document;
                        }
@@ -489,31 +541,31 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public int Lines {
+               internal int Lines {
                        get {
                                return lines;
                        }
                }
 
-               public Line CaretLine {
+               internal Line CaretLine {
                        get {
                                return caret.line;
                        }
                }
 
-               public int CaretPosition {
+               internal int CaretPosition {
                        get {
                                return caret.pos;
                        }
                }
 
-               public LineTag CaretTag {
+               internal LineTag CaretTag {
                        get {
                                return caret.tag;
                        }
                }
 
-               public int ViewPortX {
+               internal int ViewPortX {
                        get {
                                return viewport_x;
                        }
@@ -523,7 +575,7 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public int ViewPortY {
+               internal int ViewPortY {
                        get {
                                return viewport_y;
                        }
@@ -533,7 +585,19 @@ namespace System.Windows.Forms {
                        }
                }
 
-               #endregion      // Public Properties
+               internal int Width {
+                       get {
+                               return this.document_x;
+                       }
+               }
+
+               internal int Height {
+                       get {
+                               return this.document_y;
+                       }
+               }
+
+               #endregion      // Internal Properties
 
                #region Private Methods
                // For debugging
@@ -779,12 +843,7 @@ namespace System.Windows.Forms {
                }        
 
 
-               public void UpdateView(Line line, int pos) {
-                       int     prev_width;
-
-                       // This is an optimization; we need to invalidate 
-                       prev_width = (int)line.widths[line.text.Length];
-
+               internal void UpdateView(Line line, int pos) {
                        if (RecalculateDocument(owner.CreateGraphics(), line.line_no, line.line_no, true)) {
                                // Lineheight changed, invalidate the rest of the document
                                if ((line.Y - viewport_y) >=0 ) {
@@ -795,18 +854,13 @@ namespace System.Windows.Forms {
                                        owner.Invalidate();
                                }
                        } else {
-                               owner.Invalidate(new Rectangle((int)line.widths[pos] - viewport_x, line.Y - viewport_y, (int)owner.Width, line.height));
+                               owner.Invalidate(new Rectangle((int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, (int)owner.Width, line.height));
                        }
                }
 
 
                // Update display from line, down line_count lines; pos is unused, but required for the signature
-               public void UpdateView(Line line, int line_count, int pos) {
-                       int     prev_width;
-
-                       // This is an optimization; we need to invalidate 
-                       prev_width = (int)line.widths[line.text.Length];
-
+               internal void UpdateView(Line line, int line_count, int pos) {
                        if (RecalculateDocument(owner.CreateGraphics(), line.line_no, line.line_no + line_count - 1, true)) {
                                // Lineheight changed, invalidate the rest of the document
                                if ((line.Y - viewport_y) >=0 ) {
@@ -829,8 +883,33 @@ namespace System.Windows.Forms {
                }
                #endregion      // Private Methods
 
-               #region Public Methods
-               public void PositionCaret(Line line, int pos) {
+               #region Internal Methods
+               // Clear the document and reset state
+               internal void Empty() {
+
+                       document = sentinel;
+                       last_found = sentinel;
+                       lines = 0;
+
+                       // We always have a blank line
+                       Add(1, "", owner.Font, new SolidBrush(owner.ForeColor));
+                       this.RecalculateDocument(owner.CreateGraphics());
+                       PositionCaret(0, 0);
+
+                       selection_visible = false;
+                       selection_start.line = this.document;
+                       selection_start.pos = 0;
+                       selection_end.line = this.document;
+                       selection_end.pos = 0;
+
+                       viewport_x = 0;
+                       viewport_y = 0;
+
+                       document_x = 0;
+                       document_y = 0;
+               }
+
+               internal void PositionCaret(Line line, int pos) {
                        caret.tag = line.FindTag(pos);
                        caret.line = line;
                        caret.pos = pos;
@@ -838,58 +917,58 @@ namespace System.Windows.Forms {
 
                        XplatUI.DestroyCaret(owner.Handle);
                        XplatUI.CreateCaret(owner.Handle, 2, caret.height);
-                       XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos], caret.tag.line.Y + caret.tag.shift);
+                       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);
                }
 
-               public void PositionCaret(int x, int y) {
-                       caret.tag = FindCursor(xy, out caret.pos);
+               internal void PositionCaret(int x, int y) {
+                       caret.tag = FindCursor(x + viewport_x, y + viewport_y, out caret.pos);
                        caret.line = caret.tag.line;
                        caret.height = caret.tag.height;
 
                        XplatUI.DestroyCaret(owner.Handle);
                        XplatUI.CreateCaret(owner.Handle, 2, caret.height);
-                       XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos], caret.tag.line.Y + caret.tag.shift);
+                       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);
                }
 
-               public void CaretHasFocus() {
+               internal void CaretHasFocus() {
                        if (caret.tag != null) {
                                XplatUI.CreateCaret(owner.Handle, 2, caret.height);
-                               XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos], caret.tag.line.Y + caret.tag.shift);
+                               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);
                                XplatUI.CaretVisible(owner.Handle, true);
                        }
                }
 
-               public void CaretLostFocus() {
+               internal void CaretLostFocus() {
                        XplatUI.DestroyCaret(owner.Handle);
                }
 
-               public void AlignCaret() {
+               internal void AlignCaret() {
                        caret.tag = LineTag.FindTag(caret.line, caret.pos);
                        caret.height = caret.tag.height;
 
                        XplatUI.CreateCaret(owner.Handle, 2, caret.height);
-                       XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos], caret.tag.line.Y + caret.tag.shift);
+                       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);
                        XplatUI.CaretVisible(owner.Handle, true);
                }
 
-               public void UpdateCaret() {
+               internal void UpdateCaret() {
                        if (caret.tag.height != caret.height) {
                                caret.height = caret.tag.height;
                                XplatUI.CreateCaret(owner.Handle, 2, caret.height);
                        }
-                       XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos], caret.tag.line.Y + caret.tag.shift);
+                       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);
                        XplatUI.CaretVisible(owner.Handle, true);
                }
 
-               public void DisplayCaret() {
+               internal void DisplayCaret() {
                        XplatUI.CaretVisible(owner.Handle, true);
                }
 
-               public void HideCaret() {
+               internal void HideCaret() {
                        XplatUI.CaretVisible(owner.Handle, false);
                }
 
-               public void MoveCaret(CaretDirection direction) {
+               internal void MoveCaret(CaretDirection direction) {
                        switch(direction) {
                                case CaretDirection.CharForward: {
                                        caret.pos++;
@@ -962,10 +1041,6 @@ namespace System.Windows.Forms {
 
                                case CaretDirection.WordBack: {
                                        if (caret.pos > 0) {
-                                               int     len;
-
-                                               len = caret.line.text.Length;
-
                                                caret.pos--;
 
                                                while ((caret.pos > 0) && (caret.line.text.ToString(caret.pos, 1) == " ")) {
@@ -1065,13 +1140,15 @@ namespace System.Windows.Forms {
                }
 
                // Draw the document
-               public void Draw(Graphics g, Rectangle clip, Brush brush) {
+               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
                        string  s;              // String representing the current line
                        int     line_no;        //
+                       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;
@@ -1085,13 +1162,87 @@ namespace System.Windows.Forms {
                                Console.WriteLine("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
                        #endif
 
+                       hilight = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHilight);
+                       hilight_text = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHilightText);
+
                        while (line_no <= end) {
                                line = GetLine(line_no);
                                tag = line.tags;
                                s = line.text.ToString();
                                while (tag != null) {
                                        if (((tag.X + tag.width) > (clip.Left - viewport_x)) || (tag.X < (clip.Right - viewport_x))) {
-                                               g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, brush, tag.X - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+                                               // Check for selection
+                                               if ((!selection_visible) || (!owner.has_focus) || (line_no < selection_start.line.line_no) || (line_no > selection_end.line.line_no)) {
+                                               // regular drawing
+                                                       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);
+                                               } else {
+                                                       // we might have to draw our selection
+                                                       if ((line_no != selection_start.line.line_no) && (line_no != selection_end.line.line_no)) {
+                                                               g.FillRectangle(hilight, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, line.widths[tag.start + tag.length - 1], tag.height);
+                                                               g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, hilight_text, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+                                                       } else {
+                                                               bool    highlight;
+                                                               bool    partial;
+
+                                                               highlight = false;
+                                                               partial = false;
+
+                                                               // Check the partial drawings first
+                                                               if ((selection_start.tag == tag) && (selection_end.tag == tag)) {
+                                                                       partial = true;
+
+                                                                       // First, the regular part
+                                                                       g.DrawString(s.Substring(tag.start - 1, selection_start.pos - tag.start + 1), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+
+                                                                       // Now the highlight
+                                                                       g.FillRectangle(hilight, line.widths[selection_start.pos] + line.align_shift, line.Y + tag.shift - viewport_y, line.widths[selection_end.pos] - line.widths[selection_start.pos], tag.height);
+                                                                       g.DrawString(s.Substring(selection_start.pos, selection_end.pos - selection_start.pos), tag.font, hilight_text, line.widths[selection_start.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+
+                                                                       // And back to the regular
+                                                                       g.DrawString(s.Substring(selection_end.pos, tag.length - selection_end.pos), tag.font, tag.color, line.widths[selection_end.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_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, 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.length - selection_start.pos), tag.font, hilight_text, line.widths[selection_start.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+
+                                                                       // The regular part
+                                                                       g.DrawString(s.Substring(tag.start - 1, selection_start.pos - tag.start + 1), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_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], tag.height);
+                                                                       g.DrawString(s.Substring(tag.start - 1, selection_end.pos - tag.start + 1), tag.font, hilight_text, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+
+                                                                       // The regular part
+                                                                       g.DrawString(s.Substring(selection_end.pos, tag.length - selection_end.pos), tag.font, tag.color, line.widths[selection_end.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+                                                               } else {
+                                                                       // no partially selected tags here, simple checks...
+                                                                       if (selection_start.line == line) {
+                                                                               if ((tag.start + tag.length - 1) > selection_start.pos) {
+                                                                                       highlight = true;
+                                                                               }
+                                                                       }
+                                                                       if (selection_end.line == line) {
+                                                                               if ((tag.start + tag.length - 1) < selection_start.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], tag.height);
+                                                                               g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, hilight_text, tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+                                                                       } else {
+                                                                               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);
+                                                                       }
+                                                               }
+                                                       }
+
+                                               }
                                        }
 
                                        tag = tag.next;
@@ -1108,12 +1259,68 @@ namespace System.Windows.Forms {
 
 
                // Inserts a character at the given position
-               public void InsertChar(Line line, int pos, char ch) {
+               internal void InsertString(Line line, int pos, string s) {
+                       InsertString(line.FindTag(pos), pos, s);
+               }
+
+               // Inserts a string at the given position
+               internal void InsertString(LineTag tag, int pos, string s) {
+                       Line    line;
+                       int     len;
+
+                       len = s.Length;
+
+                       line = tag.line;
+                       line.text.Insert(pos, s);
+                       tag.length += len;
+
+                       tag = tag.next;
+                       while (tag != null) {
+                               tag.start += len;
+                               tag = tag.next;
+                       }
+                       line.Grow(len);
+                       line.recalc = true;
+
+                       UpdateView(line, pos);
+               }
+
+               // Inserts a string at the caret position
+               internal void InsertStringAtCaret(string s, bool move_caret) {
+                       LineTag tag;
+                       int     len;
+
+                       len = s.Length;
+
+                       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;
+
+                       UpdateView(caret.line, caret.pos);
+                       if (move_caret) {
+                               caret.pos += len;
+                               UpdateCaret();
+                       }
+               }
+
+
+
+               // Inserts a character at the given position
+               internal void InsertChar(Line line, int pos, char ch) {
                        InsertChar(line.FindTag(pos), pos, ch);
                }
 
                // Inserts a character at the given position
-               public void InsertChar(LineTag tag, int pos, char ch) {
+               internal void InsertChar(LineTag tag, int pos, char ch) {
                        Line    line;
 
                        line = tag.line;
@@ -1131,8 +1338,8 @@ namespace System.Windows.Forms {
                        UpdateView(line, pos);
                }
 
-               // Inserts a character at the given position
-               public void InsertCharAtCaret(char ch, bool move_caret) {
+               // Inserts a character at the current caret position
+               internal void InsertCharAtCaret(char ch, bool move_caret) {
                        LineTag tag;
 
                        caret.line.text.Insert(caret.pos, ch);
@@ -1155,13 +1362,70 @@ namespace System.Windows.Forms {
                        }
                }
 
-
-               // Inserts a character at the given position; it will not delete past line limits
-               public void DeleteChar(LineTag tag, int pos, bool forward) {
+               // Inserts n characters at the given position; it will not delete past line limits
+               internal void DeleteChars(LineTag tag, int pos, int count) {
                        Line    line;
                        bool    streamline;
 
 
+                       streamline = false;
+                       line = tag.line;
+
+                       if (pos == line.text.Length) {
+                               return;
+                       }
+
+                       line.text.Remove(pos, count);
+
+                       // Check if we're crossing tag boundaries
+                       if ((pos + count) > (tag.start + tag.length)) {
+                               int     left;
+
+                               // We have to delete cross tag boundaries
+                               streamline = true;
+                               left = count;
+
+                               left -= pos - tag.start;
+                               tag.length -= pos - tag.start;
+
+                               tag = tag.next;
+                               while ((tag != null) && (left > 0)) {
+                                       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;
+                       }
+
+                       tag = tag.next;
+                       while (tag != null) {
+                               tag.start -= count;
+                               tag = tag.next;
+                       }
+
+                       line.recalc = true;
+                       if (streamline) {
+                               line.Streamline();
+                       }
+
+                       UpdateView(line, pos);
+               }
+
+
+               // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
+               internal void DeleteChar(LineTag tag, int pos, bool forward) {
+                       Line    line;
+                       bool    streamline;
+
                        streamline = false;
                        line = tag.line;
 
@@ -1198,17 +1462,19 @@ namespace System.Windows.Forms {
                                tag = tag.next;
                        }
                        line.recalc = true;
-                       line.Streamline();
+                       if (streamline) {
+                               line.Streamline();
+                       }
 
                        UpdateView(line, pos);
                }
 
                // Combine two lines
-               public void Combine(int FirstLine, int SecondLine) {
+               internal void Combine(int FirstLine, int SecondLine) {
                        Combine(GetLine(FirstLine), GetLine(SecondLine));
                }
 
-               public void Combine(Line first, Line second) {
+               internal void Combine(Line first, Line second) {
                        LineTag last;
                        int     shift;
 
@@ -1269,7 +1535,7 @@ namespace System.Windows.Forms {
                }
 
                // Split the line at the position into two
-               public void Split(int LineNo, int pos) {
+               internal void Split(int LineNo, int pos) {
                        Line    line;
                        LineTag tag;
 
@@ -1278,25 +1544,25 @@ namespace System.Windows.Forms {
                        Split(line, tag, pos);
                }
 
-               public void Split(Line line, int pos) {
+               internal void Split(Line line, int pos) {
                        LineTag tag;
 
                        tag = LineTag.FindTag(line, pos);
                        Split(line, tag, pos);
                }
 
-               public void Split(Line line, LineTag tag, int pos) {
+               internal void Split(Line line, LineTag tag, int pos) {
                        LineTag new_tag;
                        Line    new_line;
 
                        // cover the easy case first
                        if (pos == line.text.Length) {
-                               Add(line.line_no + 1, "", tag.font);
+                               Add(line.line_no + 1, "", line.alignment, tag.font, tag.color);
                                return;
                        }
 
                        // We need to move the rest of the text into the new line
-                       Add(line.line_no + 1, line.text.ToString(pos, line.text.Length - pos), tag.font);
+                       Add(line.line_no + 1, line.text.ToString(pos, line.text.Length - pos), line.alignment, tag.font, tag.color);
 
                        // Now transfer our tags from this line to the next
                        new_line = GetLine(line.line_no + 1);
@@ -1309,6 +1575,7 @@ namespace System.Windows.Forms {
                                if (tag == line.tags) {
                                        new_tag = new LineTag(line, 1, 0);
                                        new_tag.font = tag.font;
+                                       new_tag.color = tag.color;
                                        line.tags = new_tag;
                                }
 
@@ -1334,6 +1601,7 @@ namespace System.Windows.Forms {
                                new_tag = new LineTag(new_line, 1, tag.start - 1 + tag.length - pos);
                                new_tag.next = tag.next;
                                new_tag.font = tag.font;
+                               new_tag.color = tag.color;
                                new_line.tags = new_tag;
                                if (new_tag.next != null) {
                                        new_tag.next.previous = new_tag;
@@ -1355,7 +1623,11 @@ namespace System.Windows.Forms {
 
                // Adds a line of text, with given font.
                // Bumps any line at that line number that already exists down
-               public void Add(int LineNo, string Text, Font font) {
+               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, HorizontalAlignment align, Font font, Brush color) {
                        Line    add;
                        Line    line;
                        int     line_no;
@@ -1368,7 +1640,7 @@ namespace System.Windows.Forms {
                                }
                        }
 
-                       add = new Line(LineNo, Text, font);
+                       add = new Line(LineNo, Text, align, font, color);
 
                        line = document;
                        while (line != sentinel) {
@@ -1405,7 +1677,7 @@ namespace System.Windows.Forms {
                        lines++;
                }
 
-               public virtual void Clear() {
+               internal virtual void Clear() {
                        lines = 0;
                        document = sentinel;
                }
@@ -1421,7 +1693,7 @@ namespace System.Windows.Forms {
                        return clone;
                }
 
-               public void Delete(int LineNo) {
+               internal void Delete(int LineNo) {
                        if (LineNo>lines) {
                                return;
                        }
@@ -1429,7 +1701,7 @@ namespace System.Windows.Forms {
                        Delete(GetLine(LineNo));
                }
 
-               public void Delete(Line line1) {
+               internal void Delete(Line line1) {
                        Line    line2;// = new Line();
                        Line    line3;
 
@@ -1488,23 +1760,448 @@ namespace System.Windows.Forms {
                }
 
                // Set our selection markers
-               public void SetSelection(Line start, int start_pos, Line end, int end_pos) {
-                       selection_start_line = start;
-                       selection_start_pos = start_pos;
-                       selection_end_line = end;
-                       selection_end_pos = end_pos;
+               internal void Invalidate(Line start, int start_pos, Line end, int end_pos) {
+                       Line    l1;
+                       Line    l2;
+                       int     p1;
+                       int     p2;
+       
+                       // figure out what's before what so the logic below is straightforward
+                       if (start.line_no < end.line_no) {
+                               l1 = start;
+                               p1 = start_pos;
+
+                               l2 = end;
+                               p2 = end_pos;
+                       } else if (start.line_no > end.line_no) {
+                               l1 = end;
+                               p1 = end_pos;
+
+                               l2 = start;
+                               p2 = start_pos;
+                       } else {
+                               if (start_pos < end_pos) {
+                                       l1 = start;
+                                       p1 = start_pos;
+
+                                       l2 = end;
+                                       p2 = end_pos;
+                               } else {
+                                       l1 = end;
+                                       p1 = end_pos;
+
+                                       l2 = start;
+                                       p2 = start_pos;
+                               }
+
+                               owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, (int)l2.widths[p2] - (int)l1.widths[p1], l1.height));
+                               return;
+                       }
+
+                       // Three invalidates:
+                       // First line from start
+                       owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, owner.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 - viewport_x, y - viewport_y, owner.Width, GetLine(l2.line_no - 1).Y - y - viewport_y));
+                       }
+
+                       // Last line to end
+                       owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, owner.Width, l1.height));
+
+
                }
 
-               public void SetSelection(Line start, int start_pos) {
-                       selection_start_line = start;
-                       selection_start_pos = start_pos;
-                       selection_end_line = start;
-                       selection_end_pos = start_pos;
+               // It's nothing short of pathetic to always invalidate the whole control
+               // I will find time to finish the optimization and make it invalidate deltas only
+               internal void SetSelectionToCaret(bool start) {
+                       if (start) {
+                               selection_start.line = caret.line;
+                               selection_start.tag = caret.tag;
+                               selection_start.pos = caret.pos;
+
+                               // start always also selects end
+                               selection_end.line = caret.line;
+                               selection_end.tag = caret.tag;
+                               selection_end.pos = caret.pos;
+                       } else {
+                               if (caret.line.line_no <= selection_end.line.line_no) {
+                                       if ((caret.line != selection_end.line) || (caret.pos < selection_end.pos)) {
+                                               selection_start.line = caret.line;
+                                               selection_start.tag = caret.tag;
+                                               selection_start.pos = caret.pos;
+                                       } else {
+                                               selection_end.line = caret.line;
+                                               selection_end.tag = caret.tag;
+                                               selection_end.pos = caret.pos;
+                                       }
+                               } else {
+                                       selection_end.line = caret.line;
+                                       selection_end.tag = caret.tag;
+                                       selection_end.pos = caret.pos;
+                               }
+                       }
+
+
+                       if ((selection_start.line == selection_end.line) && (selection_start.pos == selection_end.pos)) {
+                               selection_visible = false;
+                       } else {
+                               selection_visible = true;
+                       }
+                       owner.Invalidate();
+               }
+
+#if buggy
+               internal void SetSelection(Line start, int start_pos, Line end, int end_pos) {
+                       // Ok, this is ugly, bad and slow, but I don't have time right now to optimize it.
+                       if (selection_visible) {
+                               // Try to only invalidate what's changed so we don't redraw the whole thing
+                               if ((start != selection_start.line) || (start_pos != selection_start.pos) && ((end == selection_end.line) && (end_pos == selection_end.pos))) {
+                                       Invalidate(start, start_pos, selection_start.line, selection_start.pos);
+                               } else if ((end != selection_end.line) || (end_pos != selection_end.pos) && ((start == selection_start.line) && (start_pos == selection_start.pos))) {
+                                       Invalidate(end, end_pos, selection_end.line, selection_end.pos);
+                               } else {
+                                       // both start and end changed
+                                       Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+                               }
+                       }
+                       selection_start.line = start;
+                       selection_start.pos = start_pos;
+if (start != null) {
+                       selection_start.tag = LineTag.FindTag(start, start_pos);
+}
+
+                       selection_end.line = end;
+                       selection_end.pos = end_pos;
+if (end != null) {
+                       selection_end.tag = LineTag.FindTag(end, end_pos);
+}
+
+                       if (((start == end) && (start_pos == end_pos)) || start == null || end == null) {
+                               selection_visible = false;
+                       } else {
+                               selection_visible = true;
+                               
+                       }
+               }
+#endif
+               // Make sure that start is always before end
+               private void FixupSelection() {
+                       if (selection_start.line.line_no > selection_end.line.line_no) {
+                               Line    line;
+                               int     pos;
+                               LineTag tag;
+
+                               line = selection_start.line;
+                               tag = selection_start.tag;
+                               pos = selection_start.pos;
+
+                               selection_start.line = selection_end.line;
+                               selection_start.tag =  selection_end.tag;
+                               selection_start.pos = selection_end.pos;
+                               
+                               selection_end.line = line;
+                               selection_end.tag =  tag;
+                               selection_end.pos = pos;
+
+                               return;
+                       }
+
+                       if ((selection_start.line == selection_end.line) && (selection_start.pos > selection_end.pos)) {
+                               int     pos;
+
+                               pos = selection_start.pos;
+                               selection_start.pos = selection_end.pos;
+                               selection_end.pos = pos;
+                               Console.WriteLine("flipped: sel start: {0} end: {1}", selection_start.pos, selection_end.pos);
+                               return;
+                       }
+
+                       return;
+               }
+
+               internal void SetSelectionStart(Line start, int start_pos) {
+                       selection_start.line = start;
+                       selection_start.pos = start_pos;
+                       selection_start.tag = LineTag.FindTag(start, start_pos);
+
+                       FixupSelection();
+
+                       if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
+                               selection_visible = true;
+                       }
+
+                       if (selection_visible) {
+                               Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+                       }
+
+               }
+
+               internal void SetSelectionEnd(Line end, int end_pos) {
+                       selection_end.line = end;
+                       selection_end.pos = end_pos;
+                       selection_end.tag = LineTag.FindTag(end, end_pos);
+
+                       FixupSelection();
+
+                       if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
+                               selection_visible = true;
+                       }
+
+                       if (selection_visible) {
+                               Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+                       }
+               }
+
+               internal void SetSelection(Line start, int start_pos) {
+                       if (selection_visible) {
+                               Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
+                       }
+
+
+                       selection_start.line = start;
+                       selection_start.pos = start_pos;
+                       selection_start.tag = LineTag.FindTag(start, start_pos);
+
+                       selection_end.line = start;
+                       selection_end.tag = selection_start.tag;
+                       selection_end.pos = start_pos;
+
+                       selection_visible = false;
+               }
+
+               internal void InvalidateSelectionArea() {
+                       // implement me
+               }
+
+               // Return the current selection, as string
+               internal string GetSelection() {
+                       // We return String.Empty if there is no selection
+                       if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
+                               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);
+                       } else {
+                               StringBuilder   sb;
+                               int             i;
+                               int             start;
+                               int             end;
+
+                               sb = new StringBuilder();
+                               start = selection_start.line.line_no;
+                               end = selection_end.line.line_no;
+
+                               sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos) + Environment.NewLine);
+
+                               if ((start + 1) < end) {
+                                       for (i = start + 1; i < end; i++) {
+                                               sb.Append(GetLine(i).text.ToString() + Environment.NewLine);
+                                       }
+                               }
+
+                               sb.Append(selection_end.line.text.ToString(0, selection_end.pos));
+
+                               return sb.ToString();
+                       }
+               }
+
+               internal void ReplaceSelection(string s) {
+                       // The easiest is to break the lines where the selection tags are and delete those lines
+                       if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
+                               // Nothing to delete, simply insert
+                               InsertString(selection_start.tag, selection_start.pos, s);
+                       }
+
+                       if (!multiline || (selection_start.line == selection_end.line)) {
+                               DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
+
+                               // The tag might have been removed, we need to recalc it
+                               selection_start.tag = selection_start.line.FindTag(selection_start.pos);
+
+                               InsertString(selection_start.tag, selection_start.pos, s);
+                       } else {
+                               int             i;
+                               int             start;
+                               int             end;
+                               int             base_line;
+                               string[]        ins;
+                               int             insert_lines;
+
+                               start = selection_start.line.line_no;
+                               end = selection_end.line.line_no;
+
+                               // Delete first line
+                               DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
+
+                               start++;
+                               if (start < end) {
+                                       for (i = end - 1; i >= start; i++) {
+                                               Delete(i);
+                                       }
+                               }
+
+                               // Delete last line
+                               DeleteChars(selection_end.line.tags, 0, selection_end.pos);
+
+                               ins = s.Split(new char[] {'\n'});
+
+                               for (int j = 0; j < ins.Length; j++) {
+                                       if (ins[j].EndsWith("\r")) {
+                                               ins[j] = ins[j].Substring(0, ins[j].Length - 1);
+                                       }
+                               }
+
+                               insert_lines = ins.Length;
+
+                               // Bump the text at insertion point a line down if we're inserting more than one line
+                               if (insert_lines > 1) {
+                                       Split(selection_start.line, selection_start.pos);
+
+                                       // Reminder of start line is now in startline+1
+
+                                       // if the last line does not end with a \n we will insert the last line in front of the just moved text
+                                       if (s.EndsWith("\n")) {
+                                               insert_lines--; // We don't want to insert the last line as part of the loop anymore
+
+                                               InsertString(GetLine(selection_start.line.line_no + 1), 0, ins[insert_lines - 1]);
+                                       }
+                               }
+
+                               // Insert the first line
+                               InsertString(selection_start.line, selection_start.pos, ins[0]);
+
+                               if (insert_lines > 1) {
+                                       base_line = selection_start.line.line_no + 1;
+
+                                       for (i = 1; i < insert_lines; i++) {
+                                               Add(base_line + i, ins[i], selection_start.line.alignment, selection_start.tag.font, selection_start.tag.color);
+                                       }
+                               }
+                       }
+                       selection_end.line = selection_start.line;
+                       selection_end.pos = selection_start.pos;
+                       selection_end.tag = selection_start.tag;
+                       selection_visible = false;
+                       PositionCaret(selection_start.line, selection_start.pos);
+                       InvalidateSelectionArea();
+               }
+
+               internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
+                       Line    line;
+                       LineTag tag;
+                       int     i;
+                       int     chars;
+                       int     start;
+
+                       chars = 0;
+
+                       for (i = 1; i < lines; i++) {
+                               line = GetLine(i);
+
+                               start = chars;
+                               chars += line.text.Length + 2;  // We count the trailing \n as a char
+
+                               if (index <= chars) {
+                                       // we found the line
+                                       tag = line.tags;
+
+                                       while (tag != null) {
+                                               if (index < (start + tag.start + tag.length)) {
+                                                       line_out = line;
+                                                       tag_out = tag;
+                                                       pos = index - start;
+                                                       return;
+                                               }
+                                               if (tag.next == null) {
+                                                       Line    next_line;
+
+                                                       next_line = GetLine(line.line_no + 1);
+
+                                                       if (next_line != null) {
+                                                               line_out = next_line;
+                                                               tag_out = next_line.tags;
+                                                               pos = 0;
+                                                               return;
+                                                       } else {
+                                                               line_out = line;
+                                                               tag_out = tag;
+                                                               pos = line_out.text.Length;
+                                                               return;
+                                                       }
+                                               }
+                                               tag = tag.next;
+                                       }
+                               }
+                       }
+
+                       line_out = GetLine(lines);
+                       tag = line_out.tags;
+                       while (tag.next != null) {
+                               tag = tag.next;
+                       }
+                       tag_out = tag;
+                       pos = line_out.text.Length;
+               }
+
+               internal int LineTagToCharIndex(Line line, int pos) {
+                       int     i;
+                       int     length;
+
+                       // Count first and last line
+                       length = 0;
+
+                       // Count the lines in the middle
+
+                       for (i = 1; i < line.line_no; i++) {
+                               length += GetLine(i).text.Length + 2;   // Add one for the \n at the end of the line
+                       }
+
+                       length += pos;
+
+                       return length;
+               }
+
+               internal int SelectionLength() {
+                       if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
+                               return 0;
+                       }
+
+                       if (!multiline || (selection_start.line == selection_end.line)) {
+                               return selection_end.pos - selection_start.pos;
+                       } else {
+                               int     i;
+                               int     start;
+                               int     end;
+                               int     length;
+
+                               // Count first and last line
+                               length = selection_start.line.text.Length - selection_start.pos + selection_end.pos;
+
+                               // Count the lines in the middle
+                               start = selection_start.line.line_no + 1;
+                               end = selection_end.line.line_no;
+
+                               if (start < end) {
+                                       for (i = start; i < end; i++) {
+                                               length += GetLine(i).text.Length;
+                                       }
+                               }
+
+                               return length;
+                       }
+
+                       
                }
 
 
                // Give it a Line number and it returns the Line object at with that line number
-               public Line GetLine(int LineNo) {
+               internal Line GetLine(int LineNo) {
                        Line    line = document;
 
                        while (line != sentinel) {
@@ -1522,7 +2219,7 @@ namespace System.Windows.Forms {
 
                // Give it a Y pixel coordinate and it returns the Line covering that Y coordinate
                ///
-               public Line GetLineByPixel(int y, bool exact) {
+               internal Line GetLineByPixel(int y, bool exact) {
                        Line    line = document;
                        Line    last = null;
 
@@ -1544,7 +2241,7 @@ namespace System.Windows.Forms {
                }
 
                // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
-               public LineTag FindTag(int x, int y, out int index, bool exact) {
+               internal LineTag FindTag(int x, int y, out int index, bool exact) {
                        Line    line;
                        LineTag tag;
 
@@ -1555,6 +2252,9 @@ namespace System.Windows.Forms {
                        }
                        tag = line.tags;
 
+                       // Alignment adjustment
+                       x += line.align_shift;
+
                        while (true) {
                                if (x >= tag.X && x < (tag.X+tag.width)) {
                                        int     end;
@@ -1585,13 +2285,16 @@ namespace System.Windows.Forms {
                }
 
                // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
-               public LineTag FindCursor(int x, int y, out int index) {
+               internal LineTag FindCursor(int x, int y, out int index) {
                        Line    line;
                        LineTag tag;
 
                        line = GetLineByPixel(y, false);
                        tag = line.tags;
 
+                       // Adjust for alignment
+                       x += line.align_shift;
+
                        while (true) {
                                if (x >= tag.X && x < (tag.X+tag.width)) {
                                        int     end;
@@ -1618,23 +2321,45 @@ namespace System.Windows.Forms {
                        }
                }
 
+               internal void RecalculateAlignments() {
+                       Line    line;
+                       int     line_no;
+
+                       line_no = 1;
+
+                       while (line_no <= lines) {
+                               line = GetLine(line_no);
+
+                               if (line.alignment != HorizontalAlignment.Left) {
+                                       if (line.alignment == HorizontalAlignment.Center) {
+                                               line.align_shift = (document_x - (int)line.widths[line.text.Length]) / 2;
+                                       } else {
+                                               line.align_shift = document_x - (int)line.widths[line.text.Length];
+                                       }
+                               }
+
+                               line_no++;
+                       }
+                       return;
+               }
+
                // Calculate formatting for the whole document
-               public bool RecalculateDocument(Graphics g) {
+               internal bool RecalculateDocument(Graphics g) {
                        return RecalculateDocument(g, 1, this.lines, false);
                }
 
                // Calculate formatting starting at a certain line
-               public bool RecalculateDocument(Graphics g, int start) {
+               internal bool RecalculateDocument(Graphics g, int start) {
                        return RecalculateDocument(g, start, this.lines, false);
                }
 
                // Calculate formatting within two given line numbers
-               public bool RecalculateDocument(Graphics g, int start, int end) {
+               internal bool RecalculateDocument(Graphics g, int start, int end) {
                        return RecalculateDocument(g, start, end, false);
                }
 
                // With optimize on, returns true if line heights changed
-               public bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
+               internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
                        Line    line;
                        int     line_no;
                        int     Y;
@@ -1643,8 +2368,10 @@ namespace System.Windows.Forms {
                        line_no = start;
                        if (optimize) {
                                bool    changed;
+                               bool    alignment_recalc;
 
                                changed = false;
+                               alignment_recalc = false;
 
                                while (line_no <= end) {
                                        line = GetLine(line_no++);
@@ -1655,31 +2382,63 @@ namespace System.Windows.Forms {
                                                        // If the height changed, all subsequent lines change
                                                        end = this.lines;
                                                }
+
+                                               if (line.widths[line.text.Length] > this.document_x) {
+                                                       this.document_x = (int)line.widths[line.text.Length];
+                                                       alignment_recalc = true;
+                                               }
+
+                                               // Calculate alignment
+                                               if (line.alignment != HorizontalAlignment.Left) {
+                                                       if (line.alignment == HorizontalAlignment.Center) {
+                                                               line.align_shift = (document_x - (int)line.widths[line.text.Length]) / 2;
+                                                       } else {
+                                                               line.align_shift = document_x - (int)line.widths[line.text.Length];
+                                                       }
+                                               }
                                        }
 
                                        Y += line.height;
                                }
 
+                               if (alignment_recalc) {
+                                       RecalculateAlignments();
+                               }
+
                                return changed;
                        } else {
                                while (line_no <= end) {
                                        line = GetLine(line_no++);
                                        line.Y = Y;
                                        line.RecalculateLine(g);
+                                       if (line.widths[line.text.Length] > this.document_x) {
+                                               this.document_x = (int)line.widths[line.text.Length];
+                                       }
+
+                                       // Calculate alignment
+                                       if (line.alignment != HorizontalAlignment.Left) {
+                                               if (line.alignment == HorizontalAlignment.Center) {
+                                                       line.align_shift = (document_x - (int)line.widths[line.text.Length]) / 2;
+                                               } else {
+                                                       line.align_shift = document_x - (int)line.widths[line.text.Length];
+                                               }
+                                       }
+
                                        Y += line.height;
                                }
+                               RecalculateAlignments();
                                return true;
                        }
                }
 
-               public bool SetCursor(int x, int y) {
+               internal bool SetCursor(int x, int y) {
                        return true;
                }
 
-               public int Size() {
+               internal int Size() {
                        return lines;
                }
-               #endregion      // Public Methods
+               #endregion      // Internal Methods
 
                #region Administrative
                public IEnumerator GetEnumerator() {
@@ -1718,10 +2477,11 @@ namespace System.Windows.Forms {
                #endregion      // Administrative
        }
 
-       public class LineTag {
+       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
 
                // Payload; text
                internal int            start;          // start, in chars; index into Line.text
@@ -1735,6 +2495,8 @@ namespace System.Windows.Forms {
                internal int            ascent;         // Ascent of the font for this tag
                internal int            shift;          // Shift down for this tag, to stay on baseline
 
+               internal int            soft_break;     // Tag is 'broken soft' and continues in the next line
+
                // Administrative
                internal Line           line;           // The line we're on
                internal LineTag        next;           // Next tag on the same line
@@ -1742,7 +2504,7 @@ namespace System.Windows.Forms {
                #endregion;
 
                #region Constructors
-               public LineTag(Line line, int start, int length) {
+               internal LineTag(Line line, int start, int length) {
                        this.line = line;
                        this.start = start;
                        this.length = length;
@@ -1751,19 +2513,16 @@ namespace System.Windows.Forms {
                }
                #endregion      // Constructors
 
-               #region Public Methods
+               #region Internal Methods
                //
                // Applies 'font' to characters starting at 'start' for 'length' chars
                // Removes any previous tags overlapping the same area
                // returns true if lineheight has changed
                //
-               public static bool FormatText(Line line, int start, int length, Font font) {
+               internal static bool FormatText(Line line, int start, int length, Font font, Brush color) {
                        LineTag tag;
                        LineTag start_tag;
-                       LineTag end_tag;
                        int     end;
-                       int     state;
-                       int     left;
                        bool    retval = false;         // Assume line-height doesn't change
 
                        // Too simple?
@@ -1779,19 +2538,20 @@ namespace System.Windows.Forms {
 
                        tag = line.tags;
                        end = start + length;
-                       state = 0;
 
                        // Common special case
                        if ((start == 1) && (length == tag.length)) {
+                               tag.ascent = 0;
                                tag.font = font;
+                               tag.color = color;
                                return retval;
                        }
 
                        start_tag = FindTag(line, start);
-                       end_tag = FindTag(line, end);
 
                        tag = new LineTag(line, start, length);
                        tag.font = font;
+                       tag.color = color;
 
                        if (start == 1) {
                                line.tags = tag;
@@ -1811,6 +2571,7 @@ namespace System.Windows.Forms {
                                } else {
                                        tag.next = new LineTag(line, start_tag.start, start_tag.length);
                                        tag.next.font = start_tag.font;
+                                       tag.next.color = start_tag.color;
 
                                        if (start_tag.next != null) {
                                                tag.next.next = start_tag.next;
@@ -1823,7 +2584,7 @@ namespace System.Windows.Forms {
 
                                tag.previous = start_tag;
                                start_tag.next = tag;
-#if crap
+#if nope
                                if (tag.next.start > (tag.start + tag.length)) {
                                        tag.next.length  += tag.next.start - (tag.start + tag.length);
                                        tag.next.start = tag.start + tag.length;
@@ -1856,7 +2617,7 @@ namespace System.Windows.Forms {
                //
                // Finds the tag that describes the character at position 'pos' on 'line'
                //
-               public static LineTag FindTag(Line line, int pos) {
+               internal static LineTag FindTag(Line line, int pos) {
                        LineTag tag = line.tags;
 
                        // Beginning of line is a bit special
@@ -1878,7 +2639,7 @@ namespace System.Windows.Forms {
                //
                // Combines 'this' tag with 'other' tag.
                //
-               public bool Combine(LineTag other) {
+               internal bool Combine(LineTag other) {
                        if (!this.Equals(other)) {
                                return false;
                        }
@@ -1897,7 +2658,7 @@ namespace System.Windows.Forms {
                //
                // Remove 'this' tag ; to be called when formatting is to be removed
                //
-               public bool Remove() {
+               internal bool Remove() {
                        if ((this.start == 1) && (this.next == null)) {
                                // We cannot remove the only tag
                                return false;
@@ -1938,17 +2699,21 @@ namespace System.Windows.Forms {
 
                        other = (LineTag)obj;
 
-                       if (this.font.Equals(other.font)) {     // FIXME add checking for things like link or type later
+                       if (this.font.Equals(other.font) && this.color.Equals(other.color)) {   // FIXME add checking for things like link or type later
                                return true;
                        }
 
                        return false;
                }
 
+               public override int GetHashCode() {\r
+                       return base.GetHashCode ();\r
+               }\r
+
                public override string ToString() {
                        return "Tag starts at index " + this.start + "length " + this.length + " text: " + this.line.Text.Substring(this.start-1, this.length) + "Font " + this.font.ToString();
                }
 
-               #endregion      // Public Methods
+               #endregion      // Internal Methods
        }
 }