Browse Source

* TextBoxBase.cs: Use the new SuspendRecalc/ResumeRecalc methods
that allow us to have nested recalc = false blocks.
- Add paste support for images in the RichTextBox
* RichTextBox.cs: flush the text after the color is changed, so
the change takes effect.
- Use SuspendRecalc
- Some extra debugging info
* TextControl.cs: Tags no longer track their length, it is just
computed from the next tags length, this makes things a little
simpler and reduces places that we have to track length changes.
- Refactored the linetag class a little so we could make it
a base class for different kinds of tags
- Created a image tag, a tag that can have a single image
inserted
into it
- Replace the norecalc flag with a Suspend/Resume Recalc pair,
so
that we can call suspend multiple times.
- Add some debugging methods



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

Jackson Harper 19 years ago
parent
commit
8bc19671ee

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

@@ -1,3 +1,23 @@
+2007-01-16  Jackson Harper  <[email protected]>
+
+	* TextBoxBase.cs: Use the new SuspendRecalc/ResumeRecalc methods
+	that allow us to have nested recalc = false blocks.
+	- Add paste support for images in the RichTextBox
+	* RichTextBox.cs: flush the text after the color is changed, so
+	the change takes effect.
+	- Use SuspendRecalc
+	- Some extra debugging info
+	* TextControl.cs: Tags no longer track their length, it is just
+	computed from the next tags length, this makes things a little
+	simpler and reduces places that we have to track length changes.
+	- Refactored the linetag class a little so we could make it
+	a base class for different kinds of tags
+	- Created a image tag, a tag that can have a single image inserted
+	into it
+	- Replace the norecalc flag with a Suspend/Resume Recalc pair, so
+	that we can call suspend multiple times.
+	- Add some debugging methods
+
 2007-01-16  Rolf Bjarne Kvinge  <[email protected]>
 
 	* MdiClient.cs: Add ActivatePreviousChild for 

+ 19 - 9
mcs/class/Managed.Windows.Forms/System.Windows.Forms/RichTextBox.cs

@@ -26,6 +26,8 @@
 
 // NOT COMPLETE
 
+// #define DEBUG
+
 using System;
 using System.Collections;
 using System.ComponentModel;
@@ -857,9 +859,7 @@ namespace System.Windows.Forms {
 				try {
 					sb = new StringBuilder((int)data.Length);
 					buffer = new byte[1024];
-				}
-
-				catch {
+				} catch {
 					throw new IOException("Not enough memory to load document");
 				}
 
@@ -893,15 +893,17 @@ namespace System.Windows.Forms {
 
 			data = null;
 
+
 			try {
 				data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
+
 				LoadFile(data, fileType);
 			}
-
+#if !DEBUG
 			catch {
 				throw new IOException("Could not open file " + path);
 			}
-
+#endif
 			finally {
 				if (data != null) {
 					data.Close();
@@ -1227,6 +1229,7 @@ namespace System.Windows.Forms {
 							System.Windows.Forms.RTF.Color	color;
 
 							color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
+							
 							if (color != null) {
 								FlushText(rtf, false);
 								if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
@@ -1234,6 +1237,7 @@ namespace System.Windows.Forms {
 								} else {
 									this.rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
 								}
+								FlushText (rtf, false);
 							}
 							break;
 						}
@@ -1443,6 +1447,7 @@ namespace System.Windows.Forms {
 				} else {
 					rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
 				}
+				
 			}
 
 			rtf_chars += rtf_line.Length;
@@ -1504,26 +1509,31 @@ namespace System.Windows.Forms {
 			rtf_text_map = new RTF.TextMap();
 			RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
 
-			document.NoRecalc = true;
+			document.SuspendRecalc ();
 
 			try {
 				rtf.Read();	// That's it
 				FlushText(rtf, false);
+
 			}
 
+
 			catch (RTF.RTFException e) {
+#if DEBUG
+				throw e;
+#endif
 				// Seems to be plain text or broken RTF
 				Console.WriteLine("RTF Parsing failure: {0}", e.Message);
-			}
+			}                     
 
 			to_x = rtf_cursor_x;
 			to_y = rtf_cursor_y;
 			chars = rtf_chars;
 
 			document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
-			document.NoRecalc = false;
+			document.ResumeRecalc (true);
 
-			document.Invalidate(document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
+			document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
 		}
 
 		private void RichTextBox_HScrolled(object sender, EventArgs e) {

+ 15 - 8
mcs/class/Managed.Windows.Forms/System.Windows.Forms/TextBoxBase.cs

@@ -351,7 +351,7 @@ namespace System.Windows.Forms {
 				l = value.Length;
 				brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
 
-				document.NoRecalc = true;
+				document.SuspendRecalc ();
 				for (i = 0; i < l; i++) {
 
 					// Don't add the last line if it is just an empty line feed
@@ -372,7 +372,7 @@ namespace System.Windows.Forms {
 					}
 				}
 
-				document.NoRecalc = false;
+				document.ResumeRecalc (true);
 
 				// CalculateDocument();
 				OnTextChanged(EventArgs.Empty);
@@ -703,8 +703,8 @@ namespace System.Windows.Forms {
 			if (this is RichTextBox) {
 				o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
 			}
-			Clipboard.SetDataObject(o);
-			document.ReplaceSelection("", false);
+			Clipboard.SetDataObject (o);
+			document.ReplaceSelection ("", false);
 		}
 
 		public void Paste() {
@@ -1477,7 +1477,7 @@ namespace System.Windows.Forms {
 		private void TextBoxBase_MouseDown (object sender, MouseEventArgs e)
 		{
 			if (e.Button == MouseButtons.Left) {
-				
+
 				document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
 
 				if (IsDoubleClick (e)) {
@@ -1803,7 +1803,7 @@ namespace System.Windows.Forms {
 			if (!richtext) {
 				Line	line;
 
-				document.NoRecalc = true;
+				document.SuspendRecalc ();
 				// Font changes apply to the whole document
 				for (int i = 1; i <= document.Lines; i++) {
 					line = document.GetLine(i);
@@ -1812,7 +1812,8 @@ namespace System.Windows.Forms {
 							null, FormatSpecified.Font | FormatSpecified.Color);
 				}
 				document.UpdateView (document.GetLine (1), 0);
-				document.NoRecalc = false;
+				document.ResumeRecalc (true);
+
 				// Make sure the caret height is matching the new font height
 				document.AlignCaret();
 			}
@@ -1921,10 +1922,12 @@ namespace System.Windows.Forms {
 
 			if (clip == null)
 				return false;
-			
+
 			if (format == null) {
 				if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
 					format = DataFormats.GetFormat(DataFormats.Rtf);
+				} else if ((this is RichTextBox) && clip.GetDataPresent (DataFormats.Bitmap)) {
+					format = DataFormats.GetFormat (DataFormats.Bitmap);
 				} else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
 					format = DataFormats.GetFormat(DataFormats.UnicodeText);
 				} else if (clip.GetDataPresent(DataFormats.Text)) {
@@ -1945,6 +1948,10 @@ namespace System.Windows.Forms {
 			if (format.Name == DataFormats.Rtf) {
 				((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
 				return true;
+			} else if (format.Name == DataFormats.Bitmap) {
+				document.InsertImage (document.caret.tag, document.caret.pos, (Image) clip.GetData (DataFormats.Bitmap));
+				document.MoveCaret (CaretDirection.CharForward);
+				return true;
 			} else if (format.Name == DataFormats.UnicodeText) {
 				s = (string)clip.GetData(DataFormats.UnicodeText);
 			} else if (format.Name == DataFormats.Text) {

+ 193 - 119
mcs/class/Managed.Windows.Forms/System.Windows.Forms/TextControl.cs

@@ -129,7 +129,6 @@ namespace System.Windows.Forms {
 		internal Line			right;			// Line with higher line number
 		internal LineColor		color;			// We're doing a black/red tree. this is the node color
 		internal int			DEFAULT_TEXT_LEN;	// 
-		internal static StringFormat	string_format;		// For calculating widths/heights
 		internal bool			recalc;			// Line changed
 		#endregion	// Local Variables
 
@@ -143,12 +142,6 @@ namespace System.Windows.Forms {
 			recalc = true;
 			soft_break = false;
 			alignment = HorizontalAlignment.Left;
-
-			if (string_format == null) {
-				string_format = new StringFormat(StringFormat.GenericTypographic);
-				string_format.Trimming = StringTrimming.None;
-				string_format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
-			}
 		}
 
 		internal Line(int LineNo, string Text, Font font, SolidBrush color) : this() {
@@ -158,7 +151,7 @@ namespace System.Windows.Forms {
 			line_no = LineNo;
 
 			widths = new float[space + 1];
-			tags = new LineTag(this, 1, text.Length);
+			tags = new LineTag(this, 1);
 			tags.font = font;
 			tags.color = color;
 		}
@@ -171,7 +164,7 @@ namespace System.Windows.Forms {
 			alignment = align;
 
 			widths = new float[space + 1];
-			tags = new LineTag(this, 1, text.Length);
+			tags = new LineTag(this, 1);
 			tags.font = font;
 			tags.color = color;
 		}
@@ -389,7 +382,7 @@ namespace System.Windows.Forms {
 			widths[0] = indent;
 			tag.X = indent;
 
-			w = g.MeasureString(doc.password_char, tags.font, 10000, string_format).Width;
+			w = g.MeasureString(doc.password_char, tags.font, 10000, Document.string_format).Width;
 
 			if (this.height != (int)tag.font.Height) {
 				ret = true;
@@ -426,7 +419,6 @@ namespace System.Windows.Forms {
 			bool	wrapped;
 			Line	line;
 			int	wrap_pos;
-			float	wrap_width;
 
 			pos = 0;
 			len = this.text.Length;
@@ -447,10 +439,8 @@ namespace System.Windows.Forms {
 			wrapped = false;
 
 			wrap_pos = 0;
-			wrap_width = 0;
 
 			while (pos < len) {
-				size = g.MeasureString(this.text.ToString(pos, 1), tag.font, 10000, string_format);
 
 				while (tag.length == 0) {	// We should always have tags after a tag.length==0 unless len==0
 					tag.ascent = 0;
@@ -463,11 +453,11 @@ namespace System.Windows.Forms {
 					tag.shift = 0;
 				}
 
+				size = tag.SizeOfPosition (g, pos);
 				w = size.Width;
 
 				if (Char.IsWhiteSpace(text[pos])) {
 					wrap_pos = pos + 1;
-					wrap_width = tag.width + w;
 				}
 
 				if (doc.wrap) {
@@ -476,9 +466,11 @@ namespace System.Windows.Forms {
 						widths [pos + 1] = widths [pos] + w;
 
 						pos = wrap_pos;
+						len = text.Length;
 						doc.Split(this, tag, pos, this.soft_break);
 						this.soft_break = true;
 						len = this.text.Length;
+						
 						retval = true;
 						wrapped = true;
 					}  else if (pos > 1 && (widths[pos] + w) > (doc.viewport_width - this.right_indent)) {
@@ -514,7 +506,7 @@ namespace System.Windows.Forms {
 
 				if (pos == (tag.start-1 + tag.length)) {
 					// We just found the end of our current tag
-					tag.height = (int)tag.font.Height;
+					tag.height = tag.MaxHeight ();
 
 					// Check if we're the tallest on the line (so far)
 					if (tag.height > this.height) {
@@ -554,7 +546,6 @@ namespace System.Windows.Forms {
 					if (tag != null) {
 						tag.shift = 0;
 						wrap_pos = pos;
-						wrap_width = tag.width;
 					}
 				}
 			}
@@ -738,9 +729,12 @@ namespace System.Windows.Forms {
 		private bool		calc_pass;
 		private int		char_count;
 
-		private bool		no_recalc;
+		// For calculating widths/heights
+		public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic);
+
+		private int 		recalc_suspended;
 		private bool		recalc_pending;
-		private int		recalc_start;
+		private int		recalc_start = 1;   // This starts at one, since lines are 1 based
 		private int		recalc_end;
 		private bool		recalc_optimize;
 
@@ -783,7 +777,6 @@ namespace System.Windows.Forms {
 			multiline = true;
 			password_char = "";
 			calc_pass = false;
-			no_recalc = false;
 			recalc_pending = false;
 
 			// Tree related stuff
@@ -824,6 +817,9 @@ namespace System.Windows.Forms {
 			// Default selection is empty
 
 			document_id = random.Next();
+
+			string_format.Trimming = StringTrimming.None;
+			string_format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
 		}
 		#endregion
 
@@ -935,22 +931,6 @@ namespace System.Windows.Forms {
 			}
 		}
 
-		///<summary>Setting NoRecalc to true will prevent the document from being recalculated.
-		///This ensures that coordinates of added text are predictable after adding the text even with wrapped view</summary>
-		internal bool NoRecalc {
-			get {
-				return no_recalc;
-			}
-
-			set {
-				no_recalc = value;
-				if (!no_recalc && recalc_pending) {
-					RecalculateDocument(owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
-					recalc_pending = false;
-				}
-			}
-		}
-
 		internal int ViewPortY {
 			get {
 				return viewport_y;
@@ -1013,6 +993,23 @@ namespace System.Windows.Forms {
 		#endregion	// Internal Properties
 
 		#region Private Methods
+
+		internal void SuspendRecalc ()
+		{
+			recalc_suspended++;
+		}
+
+		internal void ResumeRecalc (bool immediate_update)
+		{
+			if (recalc_suspended > 0)
+				recalc_suspended--;
+
+			if (immediate_update && recalc_suspended == 0 && recalc_pending) {
+				RecalculateDocument (owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
+				recalc_pending = false;
+			}
+		}
+
 		// For debugging
 		internal int DumpTree(Line line, bool with_tags) {
 			int	total;
@@ -1047,7 +1044,7 @@ namespace System.Windows.Forms {
 				length = 0;
 				Console.Write("   Tags: ");
 				while (tag != null) {
-					Console.Write("{0} <{1}>-<{2}>", count++, tag.start, tag.start + tag.length
+					Console.Write("{0} <{1}>-<{2}>", count++, tag.start, tag.end
 							/*line.text.ToString (tag.start - 1, tag.length)*/);
 					length += tag.length;
 
@@ -1302,9 +1299,9 @@ namespace System.Windows.Forms {
 				return;
 			}
 
-			if (no_recalc) {
-				recalc_start = line.line_no;
-				recalc_end = line.line_no;
+			if (recalc_suspended > 0) {
+				recalc_start = Math.Min (recalc_start, line.line_no);
+				recalc_end = Math.Max (recalc_end, line.line_no);
 				recalc_optimize = true;
 				recalc_pending = true;
 				return;
@@ -1347,9 +1344,9 @@ namespace System.Windows.Forms {
 				return;
 			}
 
-			if (no_recalc) {
-				recalc_start = line.line_no;
-				recalc_end = line.line_no + line_count - 1;
+			if (recalc_suspended > 0) {
+				recalc_start = Math.Min (recalc_start, line.line_no);
+				recalc_end = Math.Max (recalc_end, line.line_no + line_count - 1);
 				recalc_optimize = true;
 				recalc_pending = true;
 				return;
@@ -1811,6 +1808,25 @@ namespace System.Windows.Forms {
 			}
 		}
 
+		internal void DumpDoc ()
+		{
+			Console.WriteLine ("<doc>");
+			for (int i = 1; i < lines; i++) {
+				Line line = GetLine (i);
+				Console.WriteLine ("<line no='{0}'>", line.line_no);
+
+				LineTag tag = line.tags;
+				while (tag != null) {
+					Console.Write ("\t<tag color='{0}'>", tag.color.Color);
+					Console.Write (tag.Text ());
+					Console.WriteLine ("\t</tag>");
+					tag = tag.next;
+				}
+				Console.WriteLine ("</line>");
+			}
+			Console.WriteLine ("</doc>");
+		}
+
 		internal void Draw (Graphics g, Rectangle clip)
 		{
 			Line line;		// Current line being drawn
@@ -1836,6 +1852,7 @@ namespace System.Windows.Forms {
 
 			line_no = start;
 
+			g.FillRectangle (new SolidBrush (Color.White), clip);
 			#if Debug
 				DateTime	n = DateTime.Now;
 				Console.WriteLine ("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
@@ -1892,7 +1909,6 @@ namespace System.Windows.Forms {
 
 				current_brush = line.tags.color;
 				while (tag != null) {
-					
 
 					// Skip empty tags
 					if (tag.length == 0) {
@@ -1929,22 +1945,19 @@ namespace System.Windows.Forms {
 
 						if (tag_pos >= line_selection_start && tag_pos < line_selection_end) {
 							current_brush = hilight_text;
-							tag_pos = Math.Min (tag.start + tag.length, line_selection_end);
+							tag_pos = Math.Min (tag.end, line_selection_end);
 						} else if (tag_pos < line_selection_start) {
 							current_brush = tag.color;
-							tag_pos = Math.Min (tag.start + tag.length, line_selection_start);
+							tag_pos = Math.Min (tag.end, line_selection_start);
 						} else {
 							current_brush = tag.color;
-							tag_pos = tag.start + tag.length;
+							tag_pos = tag.end;
 						}
 
-						
-						g.DrawString (text.ToString (old_tag_pos - 1,
-									      Math.Min (tag.length, tag_pos - old_tag_pos)),
-								tag.font, current_brush,
+						tag.Draw (g, current_brush,
 								line.widths [old_tag_pos - 1] + line.align_shift - viewport_x,
 								line.Y + tag.shift  - viewport_y,
-								StringFormat.GenericTypographic);
+								old_tag_pos - 1, Math.Min (tag.length, tag_pos - old_tag_pos));
 					}
 					tag = tag.next;
 				}
@@ -1977,7 +1990,7 @@ namespace System.Windows.Forms {
 			int count = 1;
 			LineTag tag = LineTag.FindTag (line, pos);
 			
-			NoRecalc = true;
+			SuspendRecalc ();
 			undo.BeginCompoundAction ();
 			
 			base_line = line.line_no;
@@ -2033,7 +2046,7 @@ namespace System.Windows.Forms {
 				break_index = next_break + 1;
 			}
 
-			NoRecalc = false;
+			ResumeRecalc (true);
 
 			UpdateView(line, lines - old_line_count + 1, pos);
 
@@ -2063,9 +2076,7 @@ namespace System.Windows.Forms {
 
 			line = tag.line;
 			line.text.Insert(pos, s);
-			tag.length += len;
 
-			// TODO: sometimes getting a null tag here when pasting ???
 			tag = tag.next;
 			while (tag != null) {
 				tag.start += len;
@@ -2079,29 +2090,12 @@ namespace System.Windows.Forms {
 
 		// Inserts a string at the caret position
 		internal void InsertStringAtCaret(string s, bool move_caret) {
-			LineTag	tag;
-			int	len;
-
-			len = s.Length;
 
-			CharCount += len;
-
-			caret.line.text.Insert(caret.pos, s);
-			caret.tag.length += len;
-			
-			if (caret.tag.next != null) {
-				tag = caret.tag.next;
-				while (tag != null) {
-					tag.start += len;
-					tag = tag.next;
-				}
-			}
-			caret.line.Grow(len);
-			caret.line.recalc = true;
+			InsertString (caret.tag, caret.pos, s);
 
 			UpdateView(caret.line, caret.pos);
 			if (move_caret) {
-				caret.pos += len;
+				caret.pos += s.Length;
 				UpdateCaret();
 			}
 		}
@@ -2121,7 +2115,7 @@ namespace System.Windows.Forms {
 
 			line = tag.line;
 			line.text.Insert(pos, ch);
-			tag.length++;
+			//	tag.length++;
 
 			tag = tag.next;
 			while (tag != null) {
@@ -2136,6 +2130,7 @@ namespace System.Windows.Forms {
 
 		// Inserts a character at the current caret position
 		internal void InsertCharAtCaret(char ch, bool move_caret) {
+			/*
 			LineTag	tag;
 
 			CharCount++;
@@ -2152,6 +2147,8 @@ namespace System.Windows.Forms {
 			}
 			caret.line.Grow(1);
 			caret.line.recalc = true;
+			*/
+			InsertChar (caret.tag, caret.pos, ch);
 
 			UpdateView(caret.line, caret.pos);
 			if (move_caret) {
@@ -2160,6 +2157,36 @@ namespace System.Windows.Forms {
 				SetSelectionToCaret(true);
 			}
 		}
+		
+		internal void InsertImage (LineTag tag, int pos, Image image)
+		{
+			Line line;
+			int len;
+
+			len = 1;
+
+			line = tag.line;
+			line.text.Insert (pos, "I");
+
+			LineTag next_tag = tag.Break (pos);
+			ImageTag image_tag = new ImageTag (line, pos, image);
+			image_tag.CopyFormattingFrom (tag);
+			image_tag.next = next_tag;
+			image_tag.previous = tag;
+			tag.next = image_tag;
+			
+			tag = image_tag.next;
+			while (tag != null) {
+				tag.start += len;
+				tag = tag.next;
+			}
+
+			line.Grow (len);
+			line.recalc = true;
+
+			DumpDoc ();
+			UpdateView (line, pos);
+		}
 
 		internal void DeleteMultiline (Line start_line, int pos, int length)
 		{
@@ -2233,26 +2260,22 @@ namespace System.Windows.Forms {
 				left = count;
 
 				left -= tag.start + tag.length - pos - 1;
-				tag.length -= tag.start + tag.length - pos - 1;
 
 				tag = tag.next;
 				while ((tag != null) && (left > 0)) {
 					tag.start -= count - left;
+
 					if (tag.length > left) {
-						tag.length -= left;
 						left = 0;
 					} else {
 						left -= tag.length;
-						tag.length = 0;
-	
 						tag = tag.next;
 					}
+
 				}
 			} else {
 				// We got off easy, same tag
 
-				tag.length -= count;
-
 				if (tag.length == 0) {
 					streamline = true;
 				}
@@ -2311,7 +2334,7 @@ namespace System.Windows.Forms {
 					return;
 				}
 
-				tag.length--;
+				//	tag.length--;
 
 				if (tag.length == 0) {
 					streamline = true;
@@ -2320,12 +2343,12 @@ namespace System.Windows.Forms {
 				pos--;
 				line.text.Remove(pos, 1);
 				if (pos >= (tag.start - 1)) {
-					tag.length--;
+					//		tag.length--;
 					if (tag.length == 0) {
 						streamline = true;
 					}
 				} else if (tag.previous != null) {
-					tag.previous.length--;
+					//		tag.previous.length--;
 					if (tag.previous.length == 0) {
 						streamline = true;
 					}
@@ -2374,11 +2397,11 @@ namespace System.Windows.Forms {
 				last = last.next;
 			}
 
+			// need to get the shift before setting the next tag since that effects length
+			shift = last.start + last.length - 1;
 			last.next = second.tags;
 			last.next.previous = last;
 
-			shift = last.start + last.length - 1;
-
 			// Fix up references within the chain
 			last = last.next;
 			while (last != null) {
@@ -2510,7 +2533,7 @@ namespace System.Windows.Forms {
 			}
 
 			// We need to move the rest of the text into the new line
-			Add(line.line_no + 1, line.text.ToString(pos, line.text.Length - pos), line.alignment, tag.font, tag.color);
+			Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.font, tag.color);
 
 			// Now transfer our tags from this line to the next
 			new_line = GetLine(line.line_no + 1);
@@ -2527,7 +2550,7 @@ namespace System.Windows.Forms {
 
 				// We can simply break the chain and move the tag into the next line
 				if (tag == line.tags) {
-					new_tag = new LineTag(line, 1, 0);
+					new_tag = new LineTag(line, 1);
 					new_tag.CopyFormattingFrom (tag);
 					line.tags = new_tag;
 				}
@@ -2551,7 +2574,7 @@ namespace System.Windows.Forms {
 			} else {
 				int	shift;
 
-				new_tag = new LineTag(new_line, 1, tag.start - 1 + tag.length - pos);
+				new_tag = new LineTag (new_line, 1);			
 				new_tag.next = tag.next;
 				new_tag.CopyFormattingFrom (tag);
 				new_line.tags = new_tag;
@@ -2559,7 +2582,6 @@ namespace System.Windows.Forms {
 					new_tag.next.previous = new_tag;
 				}
 				tag.next = null;
-				tag.length = pos - tag.start + 1;
 
 				shift = pos;
 				new_tag = new_tag.next;
@@ -2854,6 +2876,7 @@ namespace System.Windows.Forms {
 			owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.align_shift - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height));
 			#if Debug
 				Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} End    => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l2.widths[0] + l2.align_shift - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height);
+
 			#endif
 		}
 
@@ -3263,10 +3286,8 @@ namespace System.Windows.Forms {
 
 			undo.BeginCompoundAction ();
 
-			InvalidateSelectionArea ();
-
 			int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos);
-			NoRecalc = true;
+			SuspendRecalc ();
 
 			// First, delete any selected text
 			if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) {
@@ -3309,9 +3330,8 @@ namespace System.Windows.Forms {
 
 
 			Insert(selection_start.line, selection_start.pos, false, s);
-			NoRecalc = false;
-
 			undo.RecordInsertString (selection_start.line, selection_start.pos, s);
+			ResumeRecalc (false);
 
 			if (!select_new) {
 				CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line,
@@ -3340,6 +3360,7 @@ namespace System.Windows.Forms {
 			}
 
 			PositionCaret (selection_start.line, selection_start.pos);
+			UpdateView (selection_start.line, selection_start.pos);
 
 			undo.EndCompoundAction ();
 		}
@@ -3743,14 +3764,18 @@ namespace System.Windows.Forms {
 			bool	changed;
 			int	shift;
 
-			if (no_recalc) {
+			if (recalc_suspended > 0) {
 				recalc_pending = true;
-				recalc_start = start;
-				recalc_end = end;
+				recalc_start = Math.Min (recalc_start, start);
+				recalc_end = Math.Max (recalc_end, end);
 				recalc_optimize = optimize;
 				return false;
 			}
 
+			// Fixup the positions, they can go kinda nuts
+			start = Math.Max (start, 1);
+			end = Math.Min (end, lines);
+
 			Y = GetLine(start).Y;
 			line_no = start;
 			new_width = 0;
@@ -4172,6 +4197,31 @@ namespace System.Windows.Forms {
 		#endregion	// Administrative
 	}
 
+	internal class ImageTag : LineTag {
+
+		internal Image image;
+
+		internal ImageTag (Line line, int start, Image image) : base (line, start)
+		{
+			this.image = image;
+		}
+
+		internal override SizeF SizeOfPosition (Graphics dc, int pos)
+		{
+			return image.Size;
+		}
+
+		internal override int MaxHeight ()
+		{
+			return image.Height;
+		}
+
+		internal override void Draw (Graphics dc, Brush brush, float x, float y, int start, int end)
+		{
+			dc.DrawImage (image, x, y);
+		}
+	}
+
 	internal class LineTag {
 		#region	Local Variables;
 		// Payload; formatting
@@ -4184,13 +4234,12 @@ namespace System.Windows.Forms {
 
 		// Payload; text
 		internal int		start;		// start, in chars; index into Line.text
-		internal int		length;		// length, in chars
 		internal bool		r_to_l;		// Which way is the font
 
 		// Drawing support
 		internal int		height;		// Height in pixels of the text this tag describes
 		internal int		X;		// X location of the text this tag describes
-		//	internal float		width;		// Width in pixels of the text this tag describes
+
 		internal int		ascent;		// Ascent of the font for this tag
 		internal int		shift;		// Shift down for this tag, to stay on baseline
 
@@ -4201,17 +4250,16 @@ namespace System.Windows.Forms {
 		#endregion;
 
 		#region Constructors
-		internal LineTag(Line line, int start, int length) {
+		internal LineTag(Line line, int start) {
 			this.line = line;
 			this.start = start;
-			this.length = length;
 			this.X = 0;
 		}
 		#endregion	// Constructors
 
 		#region Internal Methods
 
-		public int End {
+		public int end {
 			get { return start + length; }
 		}
 
@@ -4222,6 +4270,34 @@ namespace System.Windows.Forms {
 				return line.widths [start + length - 1];
 			}
 		}
+
+		public int length {
+			get {
+				int res = 0;
+				if (next != null)
+					res = next.start - start;
+				else
+					res = line.text.Length - (start - 1);
+
+				return res > 0 ? res : 0;
+			}
+		}
+
+		internal virtual SizeF SizeOfPosition (Graphics dc, int pos)
+		{
+			return dc.MeasureString (line.text.ToString (pos, 1), font, 10000, Document.string_format);
+		}
+
+		internal virtual int MaxHeight ()
+		{
+			return font.Height;
+		}
+
+		internal virtual void Draw (Graphics dc, Brush brush, float x, float y, int start, int end)
+		{
+			dc.DrawString (line.text.ToString (start, end), font, brush, x, y, StringFormat.GenericTypographic);
+		}
+
 		///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at &gt;pos&lt; or null if end-of-line</summary>
 		internal LineTag Break(int pos) {
 
@@ -4234,12 +4310,13 @@ namespace System.Windows.Forms {
 				return null;
 			}
 
-			new_tag = new LineTag(line, pos, start + length - pos);
+			new_tag = new LineTag(line, pos);
 			new_tag.CopyFormattingFrom (this);
-			this.length -= new_tag.length;
+
 			new_tag.next = this.next;
 			this.next = new_tag;
 			new_tag.previous = this;
+
 			if (new_tag.next != null) {
 				new_tag.next.previous = new_tag;
 			}
@@ -4247,6 +4324,11 @@ namespace System.Windows.Forms {
 			return new_tag;
 		}
 
+		public string Text ()
+		{
+			return line.text.ToString (start - 1, length);
+		}
+
 		public void CopyFormattingFrom (LineTag other)
 		{
 			height = other.height;
@@ -4335,7 +4417,7 @@ namespace System.Windows.Forms {
 
 			tag = start_tag.Break (start);
 
-			while (tag != null && tag.End < end) {
+			while (tag != null && tag.end <= end) {
 				SetFormat (tag, font, color, back_color, specified);
 				tag = tag.next;
 			}
@@ -4362,6 +4444,7 @@ namespace System.Windows.Forms {
 			if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor) {
 				tag.back_color = back_color;
 			}
+			// Console.WriteLine ("setting format:   {0}  {1}   new color {2}", color.Color, specified, tag.color.Color);
 		}
 
 		/// <summary>Applies font attributes specified to characters starting at 'start' for 'length' chars; 
@@ -4396,7 +4479,7 @@ namespace System.Windows.Forms {
 				if (length == 0) {
 					// We are 'starting' after all valid tags; create a new tag with the right attributes
 					start_tag = FindTag(line, line.text.Length - 1);
-					start_tag.next = new LineTag(line, line.text.Length + 1, 0);
+					start_tag.next = new LineTag(line, line.text.Length + 1);
 					start_tag.next.CopyFormattingFrom (start_tag);
 					start_tag.next.previous = start_tag;
 					start_tag = start_tag.next;
@@ -4436,7 +4519,7 @@ namespace System.Windows.Forms {
 			}
 
 			while (tag != null) {
-				if ((tag.start <= pos) && (pos < (tag.start+tag.length))) {
+				if ((tag.start <= pos) && (pos <= tag.end)) {
 					return GetFinalTag (tag);
 				}
 
@@ -4463,7 +4546,6 @@ namespace System.Windows.Forms {
 				return false;
 			}
 
-			this.length += other.length;
 			this.next = other.next;
 			if (this.next != null) {
 				this.next.previous = this;
@@ -4480,12 +4562,10 @@ namespace System.Windows.Forms {
 				return false;
 			}
 			if (this.start != 1) {
-				this.previous.length += this.length;
 				this.previous.next = this.next;
 				this.next.previous = this.previous;
 			} else {
 				this.next.start = 1;
-				this.next.length += this.length;
 				this.line.tags = this.next;
 				this.next.previous = null;
 			}
@@ -4524,7 +4604,7 @@ namespace System.Windows.Forms {
 
 		public override string ToString() {
 			if (length > 0)
-				return "Tag starts at index " + this.start + " length " + this.length + " text: " + this.line.Text.Substring(this.start-1, this.length) + "Font " + this.font.ToString();
+				return "Tag starts at index " + this.start + " length " + this.length + " text: " + Text () + "Font " + this.font.ToString();
 			return "Zero Lengthed tag at index " + this.start;
 		}
 
@@ -4803,7 +4883,6 @@ namespace System.Windows.Forms {
 			int	start;
 			int	end;
 			int	tag_start;
-			int	tag_length;
 
 			line = new Line();
 			ret = line;
@@ -4836,12 +4915,7 @@ namespace System.Windows.Forms {
 						tag_start = current_tag.start;
 					}
 
-					if (end < (current_tag.start + current_tag.length)) {
-						tag_length = end - tag_start + 1;
-					} else {
-						tag_length = current_tag.start + current_tag.length - tag_start;
-					}
-					tag = new LineTag(line, tag_start - start + 1, tag_length);
+					tag = new LineTag(line, tag_start - start + 1);
 					tag.CopyFormattingFrom (current_tag);
 
 					current_tag = current_tag.next;