Browse Source

- TextBox and TextBoxBase improvements for Alignment, Selection and
Text property

svn path=/trunk/mcs/; revision=38300

Peter Dennis Bartok 21 years ago
parent
commit
eaf4502ae7

+ 22 - 0
mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog

@@ -1,3 +1,25 @@
+2005-01-03  Peter Bartok  <[email protected]>
+
+	* TextBox.cs:
+	  - set_Text: Tied into TextControl
+	  - set_TextAlignment: Tied into TextControl
+	* TextControl.cs: 
+	  - Added alignment properties and implemented alignment handling
+	    and drawing (still has a bug, not generating proper expose events)
+	  - Added new Line() constructor to allow passing the line alignment
+	  - Fixed selection setting, properly handling end<start now
+	  - Added aligment considerations to RecalculateDocument()
+	* TextBoxBase.cs:
+	  - Now properly enforces control height for single line controls
+	  - Added support for CharacterCasing
+	  - Added IsInputKey override
+	  - Fixed Keys.Enter logic
+	  - Added SetBoundsCore override
+	  - Fixed mouse selection handling
+	* ThemeWin32Classic.cs (DrawMonthCalendarDate): Now cast Math.Floor
+	  argument to double, to allow compiling with csc 2.0 (Atsushi ran
+	  into this)
+
 2005-01-03  Jackson Harper  <[email protected]>
 
 	* TreeView.cs:

+ 29 - 4
mcs/class/Managed.Windows.Forms/System.Windows.Forms/TextBox.cs

@@ -32,17 +32,13 @@ using System.Drawing;
 namespace System.Windows.Forms {
 	public class TextBox : TextBoxBase {
 		#region Local Variables
-		internal bool			accepts_return;
-		internal CharacterCasing	character_casing;
 		internal char			password_char;
 		internal ScrollBars		scrollbars;
-		internal HorizontalAlignment	alignment;
 		#endregion	// Local Variables
 
 		#region Public Constructors
 		public TextBox() {
 			accepts_return = false;
-			character_casing = CharacterCasing.Normal;
 			password_char = '\u25cf';
 			scrollbars = ScrollBars.None;
 			alignment = HorizontalAlignment.Left;
@@ -104,6 +100,29 @@ namespace System.Windows.Forms {
 			}
 
 			set {
+				Line	line;
+
+				if (multiline) {
+					string[]	lines;
+
+					lines = value.Split(new char[] {'\n'});
+					this.Lines = lines;
+
+					line = document.GetLine(1);
+					document.SetSelectionStart(line, 0);
+
+					line = document.GetLine(document.Lines);
+					document.SetSelectionEnd(line, line.text.Length);
+					document.PositionCaret(line, line.text.Length);
+				} else {
+					document.Clear();
+					document.Add(1, CaseAdjust(value), alignment, font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
+					document.RecalculateDocument(CreateGraphics());
+					line = document.GetLine(1);
+					document.SetSelectionStart(line, 0);
+					document.SetSelectionEnd(line, value.Length);
+					document.PositionCaret(line, value.Length);
+				}
 				base.Text = value;
 			}
 		}
@@ -115,7 +134,13 @@ namespace System.Windows.Forms {
 
 			set {
 				if (value != alignment) {
+					Line	line;
+
 					alignment = value;
+					for (int i = 1; i <= document.Lines; i++) {
+						document.GetLine(i).Alignment = value;
+					}
+					document.RecalculateDocument(CreateGraphics());
 					OnTextAlignChanged(EventArgs.Empty);
 				}
 			}

+ 122 - 47
mcs/class/Managed.Windows.Forms/System.Windows.Forms/TextBoxBase.cs

@@ -35,25 +35,29 @@ using System.Text;
 namespace System.Windows.Forms {
 	public abstract class TextBoxBase : Control {
 		#region Local Variables
-		internal bool		accepts_tab;
-		internal bool		auto_size;
-		internal BorderStyle	border_style;
-		internal bool		undo;
-		internal bool		hide_selection;
-		internal int		max_length;
-		internal bool		modified;
-		internal bool		multiline;
-		internal bool		read_only;
-		internal bool		word_wrap;
-		internal Document	document;
-		internal LineTag	caret_tag;		// tag our cursor is in
-		internal int		caret_pos;		// position on the line our cursor is in (can be 0 = beginning of line)
-		internal int		viewport_x;		// left visible pixel
-		internal int		viewport_y;		// top visible pixel
-		internal HScrollBar	hscroll;
-		internal VScrollBar	vscroll;
-		internal ScrollBars	scrollbars;
-		internal bool		grabbed;
+		internal HorizontalAlignment	alignment;
+		internal bool			accepts_tab;
+		internal bool			accepts_return;
+		internal bool			auto_size;
+		internal BorderStyle		border_style;
+		internal CharacterCasing	character_casing;
+		internal bool			undo;
+		internal bool			hide_selection;
+		internal int			max_length;
+		internal bool			modified;
+		internal bool			multiline;
+		internal bool			read_only;
+		internal bool			word_wrap;
+		internal Document		document;
+		internal LineTag		caret_tag;		// tag our cursor is in
+		internal int			caret_pos;		// position on the line our cursor is in (can be 0 = beginning of line)
+		internal int			viewport_x;		// left visible pixel
+		internal int			viewport_y;		// top visible pixel
+		internal HScrollBar		hscroll;
+		internal VScrollBar		vscroll;
+		internal ScrollBars		scrollbars;
+		internal bool			grabbed;
+		internal bool			richtext;
 
 		#if Debug
 		internal static bool	draw_lines = false;
@@ -64,9 +68,12 @@ namespace System.Windows.Forms {
 		#region Private Constructor
 		// Constructor will go when complete, only for testing - pdb
 		public TextBoxBase() {
+			alignment = HorizontalAlignment.Left;
+			accepts_return = false;
 			accepts_tab = false;
 			auto_size = true;
 			border_style = BorderStyle.Fixed3D;
+			character_casing = CharacterCasing.Normal;
 			undo = false;
 			hide_selection = true;
 			max_length = 32767;
@@ -74,11 +81,14 @@ namespace System.Windows.Forms {
 			multiline = false;
 			read_only = false;
 			word_wrap = true;
+			richtext = false;
 			document = new Document(this);
-			this.MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
-			this.MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
-			this.MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
-			this.SizeChanged +=new EventHandler(TextBoxBase_SizeChanged);
+			MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
+			MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
+			MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
+			SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
+			FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
+			ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
 
 			
 			scrollbars = ScrollBars.None;
@@ -100,6 +110,19 @@ namespace System.Windows.Forms {
 		}
 		#endregion	// Private Constructor
 
+		#region Private and Internal Methods
+		internal string CaseAdjust(string s) {
+			if (character_casing == CharacterCasing.Normal) {
+				return s;
+			}
+			if (character_casing == CharacterCasing.Lower) {
+				return s.ToLower();
+			} else {
+				return s.ToUpper();
+			}
+		}
+		#endregion	// Private and Internal Methods
+
 		#region Public Instance Properties
 		public bool AcceptsTab {
 			get {
@@ -220,8 +243,9 @@ namespace System.Windows.Forms {
 				brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.BackColor);
 
 				for (i = 0; i < l; i++) {
-					document.Add(i+1, value[i], font, brush);
+					document.Add(i+1, CaseAdjust(value[i]), alignment, font, brush);
 				}
+				document.RecalculateDocument(CreateGraphics());
 			}
 		}
 
@@ -267,7 +291,7 @@ namespace System.Windows.Forms {
 
 		public int PreferredHeight {
 			get {
-				return this.font.Height + 7;	// FIXME - consider border style as well
+				return this.Font.Height + 7;	// FIXME - consider border style as well
 			}
 		}
 
@@ -290,7 +314,7 @@ namespace System.Windows.Forms {
 			}
 
 			set {
-				document.ReplaceSelection(value);
+				document.ReplaceSelection(CaseAdjust(value));
 			}
 		}
 
@@ -406,6 +430,26 @@ namespace System.Windows.Forms {
 			base.CreateHandle ();
 		}
 
+		protected override bool IsInputKey(Keys keyData) {
+			switch (keyData) {
+				case Keys.Enter: {
+					if (multiline && (accepts_return || ((keyData & Keys.Control) != 0))) {
+						return true;
+					}
+					return false;
+				}
+
+				case Keys.Tab: {
+					if (accepts_tab) {
+						return true;
+					}
+					return false;
+				}
+			}
+			return false;
+		}
+
+
 		protected virtual void OnAcceptsTabChanged(EventArgs e) {
 			if (AcceptsTabChanged != null) {
 				AcceptsTabChanged(this, e);
@@ -465,6 +509,14 @@ namespace System.Windows.Forms {
 		}
 
 		protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
+			// Make sure we don't get sized bigger than we want to be
+			if (!richtext) {
+				if (!multiline) {
+					if (height > PreferredHeight) {
+						height = PreferredHeight;
+					}
+				}
+			}
 			base.SetBoundsCore (x, y, width, height, specified);
 		}
 
@@ -550,14 +602,17 @@ Console.WriteLine("Destroying caret");
 						}
 
 						case Keys.Enter: {
-							document.Split(document.CaretLine, document.CaretTag, document.CaretPosition);
-							document.UpdateView(document.CaretLine, 2, 0);
-							document.MoveCaret(CaretDirection.CharForward);
+							
+							if (multiline && (accepts_return || ((Control.ModifierKeys & Keys.Control) != 0))) {
+								document.Split(document.CaretLine, document.CaretTag, document.CaretPosition);
+								document.UpdateView(document.CaretLine, 2, 0);
+								document.MoveCaret(CaretDirection.CharForward);
+							}
 							return;
 						}
 
 						case Keys.Tab: {
-							if (this.accepts_tab) {
+							if (accepts_tab) {
 								document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
 							}
 							return;
@@ -619,7 +674,22 @@ Console.WriteLine("Destroying caret");
 
 				case Msg.WM_CHAR: {
 					if (m.WParam.ToInt32() >= 32) {	// FIXME, tabs should probably go through
-						document.InsertCharAtCaret((char)m.WParam, true);
+						switch (character_casing) {
+							case CharacterCasing.Normal: {
+								document.InsertCharAtCaret((char)m.WParam, true);
+								return;
+							}
+
+							case CharacterCasing.Lower: {
+								document.InsertCharAtCaret(Char.ToLower((char)m.WParam), true);
+								return;
+							}
+
+							case CharacterCasing.Upper: {
+								document.InsertCharAtCaret(Char.ToUpper((char)m.WParam), true);
+								return;
+							}
+						}
 					}
 						
 					return;
@@ -660,7 +730,6 @@ Console.WriteLine("Destroying caret");
 static int current;
 
 		private void PaintControl(PaintEventArgs pevent) {
-Console.WriteLine("Received expose: {0}", pevent.ClipRectangle);
 			// Fill background
 			pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
 			pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
@@ -764,13 +833,8 @@ Console.WriteLine("Received expose: {0}", pevent.ClipRectangle);
 			this.Capture = false;
 			this.grabbed = false;
 			if (e.Button == MouseButtons.Left) {
-				document.PositionCaret(e.X, e.Y);
-				if ((Control.ModifierKeys & Keys.Shift) == 0) {
-					document.SetSelectionToCaret(true);
-				} else {
-					document.SetSelectionToCaret(false);
-				}
-
+				document.PositionCaret(e.X + viewport_x, e.Y + viewport_y);
+				document.SetSelectionToCaret(false);
 				document.DisplayCaret();
 				return;
 			}
@@ -796,14 +860,25 @@ Console.WriteLine("Received expose: {0}", pevent.ClipRectangle);
 		private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
 			// FIXME - handle auto-scrolling if mouse is to the right/left of the window
 			if (grabbed) {
-				Line	line;
-				int	pos;
-//mcs bug requires this:
-pos = 0;
-				line = document.FindCursor(e.X + viewport_x, e.Y + viewport_y, out pos).line;
-				document.SetSelectionEnd(line, pos);
-				Invalidate();
+				document.PositionCaret(e.X + viewport_x, e.Y + viewport_y);
+				document.SetSelectionToCaret(false);
+				document.DisplayCaret();
 			}
-		}
+		}
+
+		private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
+			if (!richtext) {
+				Line	line;
+
+				// Font changes apply to the whole document
+				for (int i = 1; i <= document.Lines; i++) {
+					line = document.GetLine(i);
+					LineTag.FormatText(line, 1, line.text.Length, font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
+					document.UpdateView(line, 0);
+				}
+				// Make sure the caret height is matching the new font height
+				document.AlignCaret();
+			}
+		}
 	}
 }

+ 129 - 30
mcs/class/Managed.Windows.Forms/System.Windows.Forms/TextControl.cs

@@ -78,6 +78,8 @@ 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
@@ -97,6 +99,7 @@ namespace System.Windows.Forms {
 			parent = null;
 			text = null;
 			recalc = true;
+			alignment = HorizontalAlignment.Left;
 
 			if (string_format == null) {
 				string_format = new StringFormat(StringFormat.GenericTypographic);
@@ -117,6 +120,19 @@ namespace System.Windows.Forms {
 			tags.color = color;
 		}
 
+		public 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;
+		}
+
 		public Line(int LineNo, string Text, LineTag tag) : this() {
 			space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
 
@@ -159,6 +175,19 @@ namespace System.Windows.Forms {
 				text = new StringBuilder(value, value.Length > DEFAULT_TEXT_LEN ? value.Length : DEFAULT_TEXT_LEN);
 			}
 		}
+
+		public HorizontalAlignment Alignment {
+			get {
+				return alignment;
+			}
+
+			set {
+				if (alignment != value) {
+					alignment = value;
+					recalc = true;
+				}
+			}
+		}
 #if no
 		public StringBuilder Text {
 			get {
@@ -895,7 +924,7 @@ 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] - viewport_x, caret.tag.line.Y + caret.tag.shift - viewport_y);
+			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) {
@@ -905,13 +934,13 @@ 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] - viewport_x, caret.tag.line.Y + caret.tag.shift - viewport_y);
+			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() {
 			if (caret.tag != null) {
 				XplatUI.CreateCaret(owner.Handle, 2, caret.height);
-				XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] - viewport_x, caret.tag.line.Y + caret.tag.shift - viewport_y);
+				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);
 			}
 		}
@@ -925,7 +954,7 @@ namespace System.Windows.Forms {
 			caret.height = caret.tag.height;
 
 			XplatUI.CreateCaret(owner.Handle, 2, caret.height);
-			XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] - viewport_x, caret.tag.line.Y + caret.tag.shift - viewport_y);
+			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);
 		}
 
@@ -934,7 +963,7 @@ namespace System.Windows.Forms {
 				caret.height = caret.tag.height;
 				XplatUI.CreateCaret(owner.Handle, 2, caret.height);
 			}
-			XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] - viewport_x, caret.tag.line.Y + caret.tag.shift - viewport_y);
+			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);
 		}
 
@@ -1151,12 +1180,12 @@ namespace System.Windows.Forms {
 						// Check for selection
 						if ((!selection_visible) || (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 - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+							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(tag.color, tag.X - 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), tag.X - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+								g.FillRectangle(tag.color, 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), tag.X + line.align_shift - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
 							} else {
 								int	middle;
 								bool	highlight;
@@ -1170,32 +1199,32 @@ namespace System.Windows.Forms {
 									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 - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+									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(tag.color, line.widths[selection_start.pos], 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), line.widths[selection_start.pos], line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+									g.FillRectangle(tag.color, 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), 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.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+									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(tag.color, line.widths[selection_start.pos], line.Y + tag.shift - viewport_y, line.widths[tag.start + tag.length - 1], tag.height);
-									g.DrawString(s.Substring(selection_start.pos, tag.length - selection_start.pos), tag.font, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), line.widths[selection_start.pos], line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+									g.FillRectangle(tag.color, line.widths[selection_start.pos] + line.align_shift, line.Y + tag.shift - viewport_y, line.widths[tag.start + tag.length - 1], tag.height);
+									g.DrawString(s.Substring(selection_start.pos, tag.length - selection_start.pos), tag.font, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), 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 - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+									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(tag.color, tag.X - 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), tag.X - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+									g.FillRectangle(tag.color, 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), 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.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
+									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) {
@@ -1212,10 +1241,10 @@ namespace System.Windows.Forms {
 
 								if (!partial) {
 									if (highlight) {
-										g.FillRectangle(tag.color, tag.X - 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), tag.X - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+										g.FillRectangle(tag.color, 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, ThemeEngine.Current.ResPool.GetSolidBrush(owner.BackColor), 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 - viewport_x, line.Y + tag.shift  - viewport_y, StringFormat.GenericTypographic);
+										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);
 									}
 								}
 							}
@@ -1535,12 +1564,12 @@ namespace System.Windows.Forms {
 
 			// cover the easy case first
 			if (pos == line.text.Length) {
-				Add(line.line_no + 1, "", tag.font, tag.color);
+				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, tag.color);
+			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);
@@ -1602,6 +1631,10 @@ 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, Brush color) {
+			Add(LineNo, Text, HorizontalAlignment.Left, font, color);
+		}
+
+		public void Add(int LineNo, string Text, HorizontalAlignment align, Font font, Brush color) {
 			Line	add;
 			Line	line;
 			int	line_no;
@@ -1614,7 +1647,7 @@ namespace System.Windows.Forms {
 				}
 			}
 
-			add = new Line(LineNo, Text, font, color);
+			add = new Line(LineNo, Text, align, font, color);
 
 			line = document;
 			while (line != sentinel) {
@@ -1803,9 +1836,21 @@ namespace System.Windows.Forms {
 				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 (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;
+				}
 			}
 
 
@@ -1879,6 +1924,7 @@ if (end != null) {
 				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;
 			}
 
@@ -1892,7 +1938,7 @@ if (end != null) {
 
 			FixupSelection();
 
-			if ((selection_end.line != selection_start.line) && (selection_end.pos != selection_start.pos)) {
+			if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
 				selection_visible = true;
 			}
 
@@ -2031,12 +2077,11 @@ if (end != null) {
 				// 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.tag.font, selection_start.tag.color);
+						Add(base_line + i, ins[i], selection_start.line.alignment, selection_start.tag.font, selection_start.tag.color);
 					}
 				}
 			}
@@ -2133,6 +2178,9 @@ if (end != null) {
 			}
 			tag = line.tags;
 
+			// Alignment adjustment
+			x += line.align_shift;
+
 			while (true) {
 				if (x >= tag.X && x < (tag.X+tag.width)) {
 					int	end;
@@ -2170,6 +2218,9 @@ if (end != null) {
 			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;
@@ -2196,6 +2247,28 @@ if (end != null) {
 			}
 		}
 
+		public 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) {
 			return RecalculateDocument(g, 1, this.lines, false);
@@ -2221,8 +2294,10 @@ if (end != null) {
 			line_no = start;
 			if (optimize) {
 				bool	changed;
+				bool	alignment_recalc;
 
 				changed = false;
+				alignment_recalc = false;
 
 				while (line_no <= end) {
 					line = GetLine(line_no++);
@@ -2233,14 +2308,27 @@ if (end != null) {
 							// 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;
 				}
 
+				RecalculateAlignments();
+
 				return changed;
 			} else {
 				while (line_no <= end) {
@@ -2250,8 +2338,19 @@ if (end != null) {
 					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;
 			}
 		}