diff options
author | Karl <karl@scowencomputers.co.nz> | 2015-10-01 11:37:14 +0300 |
---|---|---|
committer | Karl <karl@scowencomputers.co.nz> | 2020-01-13 06:42:44 +0300 |
commit | b35c560df3c7eb39b7a70ff290ec41ba35517e7e (patch) | |
tree | 1e05f839fc5babe4b9e59acd90fc1e212391b807 /mcs/class | |
parent | 681b514e888e1af11bb318eb1170ebf651e3ca07 (diff) |
Lots of TextControl stuff, plus others.
* RTF.cs: Add support for JPEG and EMF pictures.
Allow for attribute etc. groups inside the picture group.
* Line.cs: In InsertString, ensure that the tag passed in is currently
in the line – not necessarily valid when the passed tag is the
cursor, for instance.
* LineTag.cs: Only break tags for formatting when needed.
* TextControl.cs: Fix invalidating text that isn't left-aligned.
Fix caret stuff:
Positioning after the line ending no longer happens
Up and down when not left-aligned
Tag used when moving left to start of line
Page up / down
Remove empty tags when navigating away from them
Change tag background draw location – I think it is a correction!
When inserting at a position without being given the tag, choose an
empty tag at that position if one exists.
Change ReplaceSelection order back (corrected some formatting and
similar stuff), and format the new text to match the tag.
Change IncrementLines to transverse the tree (using a new function)
rather than repeatedly call GetLine.
* RichTextBox.cs: Don't streamline the line in case it removes tags we
want to keep – we'll skip them ourselves instead.
Emit pictures.
* TextBoxBase.cs: Remove redundant check – a previous check means
value != acutal_border_style, therefore at least one of them !=
BorderStyle.Fixed3D, therefore the if condition was always true.
Diffstat (limited to 'mcs/class')
9 files changed, 285 insertions, 77 deletions
diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/KeysInit.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/KeysInit.cs index 72188e6daa7..3dbfed122eb 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/KeysInit.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/KeysInit.cs @@ -366,10 +366,12 @@ namespace System.Windows.Forms.RTF { new KeyStruct(Major.StyleAttr, Minor.Next, "snext"), new KeyStruct(Major.PictAttr, Minor.MacQD, "macpict"), new KeyStruct(Major.PictAttr, Minor.PMMetafile, "pmmetafile"), + new KeyStruct(Major.PictAttr, Minor.EnhancedMetafile, "emfblip"), new KeyStruct(Major.PictAttr, Minor.WinMetafile, "wmetafile"), new KeyStruct(Major.PictAttr, Minor.DevIndBitmap, "dibitmap"), new KeyStruct(Major.PictAttr, Minor.WinBitmap, "wbitmap"), new KeyStruct(Major.PictAttr, Minor.PngBlip, "pngblip"), + new KeyStruct(Major.PictAttr, Minor.JpegBlip, "jpgblip"), new KeyStruct(Major.PictAttr, Minor.PixelBits, "wbmbitspixel"), new KeyStruct(Major.PictAttr, Minor.BitmapPlanes, "wbmplanes"), new KeyStruct(Major.PictAttr, Minor.BitmapWid, "wbmwidthbytes"), diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Minor.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Minor.cs index 8b0f1bc6f8d..e057fcfb525 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Minor.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Minor.cs @@ -520,10 +520,12 @@ namespace System.Windows.Forms.RTF { // Major.PictAttr MacQD, PMMetafile, + EnhancedMetafile, WinMetafile, DevIndBitmap, WinBitmap, PngBlip, + JpegBlip, PixelBits, BitmapPlanes, BitmapWid, diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Picture.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Picture.cs index 244bcd9adff..780e8031b3a 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Picture.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/Picture.cs @@ -66,7 +66,7 @@ namespace System.Windows.Forms.RTF { public float Width { get { float w = width; - if (w == -1) { + if (w < 0) { if (image == null) image = ToImage (); w = image.Width; @@ -79,7 +79,7 @@ namespace System.Windows.Forms.RTF { public float Height { get { float h = height; - if (h == -1) { + if (h < 0) { if (image == null) image = ToImage (); h = image.Height; @@ -113,7 +113,9 @@ namespace System.Windows.Forms.RTF { return false; switch (image_type) { case Minor.PngBlip: + case Minor.JpegBlip: case Minor.WinMetafile: + case Minor.EnhancedMetafile: break; default: return false; @@ -126,15 +128,7 @@ namespace System.Windows.Forms.RTF { { if (image == null) image = ToImage (); - - float height = this.height; - float width = this.width; - - if (height == -1) - height = image.Height; - if (width == -1) - width = image.Width; - dc.DrawImage (image, x, y, width, height); + dc.DrawImage(image, x, y, Width, Height); } public Image ToImage () diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/RTF.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/RTF.cs index 1936a638eec..c2c333dc07a 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/RTF.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms.RTF/RTF.cs @@ -943,20 +943,25 @@ SkipCRLF: private void ReadPictGroup(RTF rtf) { bool read_image_data = false; - + int groupDepth = 0; Picture picture = new Picture (); while (true) { rtf.GetToken (); + if (rtf.CheckCM (TokenClass.Group, Major.BeginGroup)) + groupDepth++; + if (rtf.CheckCM (TokenClass.Group, Major.EndGroup)) + groupDepth--; + + if (groupDepth < 0) break; switch (minor) { case Minor.PngBlip: - picture.ImageType = minor; - read_image_data = true; - break; + case Minor.JpegBlip: case Minor.WinMetafile: + case Minor.EnhancedMetafile: picture.ImageType = minor; read_image_data = true; continue; diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms/Line.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms/Line.cs index f93e629df7e..11d835c0ee0 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms/Line.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms/Line.cs @@ -535,6 +535,33 @@ namespace System.Windows.Forms // Insert the text into the StringBuilder
text.Insert (pos, s);
+ // Check that tag is still in use in the line. If not, then we choose the last tag at that position.
+ LineTag t = tags;
+ while (t != null) {
+ if (((t.Start - 1) <= pos) && (pos < (t.End - 1) || (pos == t.End - 1 && t.Length == 0))) {
+ // found the location
+ bool foundTag = false;
+ while (pos < (t.Start + t.Length - 1)) {
+ if (t == tag) {
+ foundTag = true;
+ break;
+ }
+ if (t.Next == null)
+ break;
+ t = t.Next;
+ }
+ if (!foundTag) {
+ if (pos < (t.Start + t.Length - 1)) {
+ tag = t.Previous;
+ } else {
+ tag = t;
+ }
+ }
+ break;
+ }
+ t = t.Next;
+ }
+
// Update the start position of every tag after this one
tag = tag.Next;
diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms/LineTag.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms/LineTag.cs index 70de2ebfd8e..374f8e01d7a 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms/LineTag.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms/LineTag.cs @@ -485,6 +485,11 @@ namespace System.Windows.Forms return null;
}
+ public static bool FormatText (Line line, int formatStart, int length, LineTag copyFrom) {
+ return FormatText(line, formatStart, length, copyFrom.Font, copyFrom.Color, copyFrom.BackColor,
+ copyFrom.TextPosition, copyFrom.CharOffset, copyFrom.Visible, FormatSpecified.All);
+ }
+
public static bool FormatText (Line line, int formatStart, int length, Font font, Color color, Color backColor, FormatSpecified specified)
{
return FormatText (line, formatStart, length, font, color, backColor, TextPositioning.Normal, 0, true, specified);
@@ -550,7 +555,11 @@ namespace System.Windows.Forms return retval;
}
- tag = start_tag.Break (formatStart);
+ // Break the tag if needed -- we don't need to break for the start if we're starting at its start.
+ if (start_tag.Start != formatStart)
+ tag = start_tag.Break(formatStart);
+ else
+ tag = start_tag;
// empty selection style at end of line - its the only situation
// where the rest of the tag would be empty, since we moved to the
@@ -568,16 +577,18 @@ namespace System.Windows.Forms return retval;
}
+ bool atEnd = false;
while (tag != null && tag.End <= end) {
SetFormat (tag, font, color, backColor, text_position, char_offset, visible, specified);
+ atEnd |= tag.End == end;
tag = tag.next;
}
// did the last tag conveniently fit?
- if (tag != null && tag.End == end)
+ if (atEnd || (tag != null && tag.End == end))
return retval;
- /// Now do the last tag
+ // Now do the last tag
end_tag = FindTag (line, end-1);
if (end_tag != null) {
diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms/RichTextBox.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms/RichTextBox.cs index b9a77f613f8..ee34e3d9541 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms/RichTextBox.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms/RichTextBox.cs @@ -2361,6 +2361,59 @@ namespace System.Windows.Forms { return -1; } + private static char[] GetHexChars(byte[] bytes, int length) { + if (length > bytes.Length) { + throw new ArgumentOutOfRangeException("length"); + } + + var chars = new char[length * 2]; + int n; + for (int i = 0; i < length; i++) { + n = bytes[i] >> 4; + chars[i * 2] = (char)('A' - 10 + n + (((n - 10) >> 31) & ('0' - 55))); + n = bytes[i] & 0x0F; + chars[i * 2 + 1] = (char)('A' - 10 + n + (((n - 10) >> 31) & ('0' - 55))); + } + return chars; + } + + private void EmitRtfPicture(PictureTag picture, StringBuilder sb) { + if (!picture.picture.IsValid()) { + return; + } + + int width = (int)((float)picture.picture.Width / document.dpi * 1440f); + int height = (int)((float)picture.picture.Height / document.dpi * 1440f); + string type = ""; + switch (picture.picture.ImageType) { + case RTF.Minor.WinMetafile: + type = "wmetafile1"; // The number should actually vary, but I don't see how it is used here at all. + break; + case RTF.Minor.EnhancedMetafile: + type = "emfblip"; + break; + case RTF.Minor.PngBlip: + type = "pngblip"; + break; + case RTF.Minor.JpegBlip: + type = "jpegblip"; + break; + } + sb.AppendFormat("{{\\pict\\{0}\\picwgoal{1}\\pichgoal{2} ", type, width, height); + + var data = picture.picture.Data; + data.Position = 0; + if (sb.Capacity - sb.Length < data.Length) { + sb.Capacity += (int)data.Length * 2; + } + var buffer = new byte[39]; + int length; + while ((length = data.Read(buffer, 0, buffer.Length)) > 0) { + sb.AppendLine().Append(GetHexChars(buffer, length)); + } + sb.Append("}"); + } + // start_pos and end_pos are 0-based private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) { StringBuilder sb; @@ -2551,7 +2604,6 @@ namespace System.Windows.Forms { while (line_no <= end_line.line_no) { line = document.GetLine(line_no); - line.Streamline(document.Lines); tag = LineTag.FindTag(line, pos); if (line_no != end_line.line_no) { @@ -2688,7 +2740,9 @@ namespace System.Windows.Forms { } // Emit the string itself - if (line_no != end_line.line_no) { + if (tag is PictureTag) { + EmitRtfPicture((PictureTag)tag, sb); + } else if (line_no != end_line.line_no) { EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1)); } else { if (end_pos < (tag.Start + tag.Length - 1)) { @@ -2700,7 +2754,9 @@ namespace System.Windows.Forms { } pos = tag.Start + tag.Length - 1; - tag = tag.Next; + do { + tag = tag.Next; + } while (tag != null && tag.IsTextTag && tag.Length == 0); } if (pos >= line.text.Length) { if (line.ending != LineEnding.Wrap) { diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms/TextBoxBase.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms/TextBoxBase.cs index 53f9b134f5e..d604259553c 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms/TextBoxBase.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms/TextBoxBase.cs @@ -298,8 +298,7 @@ namespace System.Windows.Forms if (value == actual_border_style) return; - if (actual_border_style != BorderStyle.Fixed3D || value != BorderStyle.Fixed3D) - Invalidate (); + Invalidate(); actual_border_style = value; document.UpdateMargins (); diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms/TextControl.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms/TextControl.cs index 307423ee942..2fdac6f5459 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms/TextControl.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms/TextControl.cs @@ -48,6 +48,7 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Drawing; using System.Drawing.Text; using System.Text; @@ -74,7 +75,9 @@ namespace System.Windows.Forms { Color = 8, TextPosition = 16, CharOffset = 32, - Visibility = 64 + Visibility = 64, + + All = 126 } internal enum TextPositioning { @@ -695,12 +698,15 @@ namespace System.Windows.Forms { } private void IncrementLines(int line_no) { - int current; + /*int current; current = this.lines; while (current >= line_no) { GetLine(current).line_no++; current--; + }*/ + foreach (var line in TransverseLines(this.lines, line_no)) { + line.line_no++; } return; } @@ -880,19 +886,27 @@ namespace System.Windows.Forms { return; } + var prev_line_pos = new Point(line.X, line.Y); + var prev_line_bottom = line.Y + line.Height; + var prev_pos_width = line.widths[pos]; + float prev_pos1_width = (line.widths.Length > pos + 1) ? line.widths[pos + 1] : prev_pos_width; + // Optimize invalidation based on Line alignment bool height_changed; using (var graphics = owner.CreateGraphics()) height_changed = RecalculateDocument(graphics, line.line_no, line.line_no, true); + var x = Math.Min(prev_line_pos.X, line.X); + var y = Math.Min(prev_line_pos.Y, line.Y); + var h = Math.Max(prev_line_bottom, line.Y + line.Height) - y; if (height_changed) { // Lineheight changed, invalidate the rest of the document - if ((line.Y - viewport_y) >=0 ) { + if ((y - viewport_y) >=0 ) { // We formatted something that's in view, only draw parts of the screen owner.Invalidate(new Rectangle( offset_x, - line.Y - viewport_y + offset_y, + y - viewport_y + offset_y, viewport_width, - owner.Height - (line.Y - viewport_y))); + owner.Height - (y - viewport_y))); } else { // The tag was above the visible area, draw everything owner.Invalidate(); @@ -901,28 +915,28 @@ namespace System.Windows.Forms { switch(line.alignment) { case HorizontalAlignment.Left: { owner.Invalidate(new Rectangle( - line.X + ((int)line.widths[pos] - viewport_x - 1) + offset_x, - line.Y - viewport_y + offset_y, + x + ((int)Math.Max(line.widths[pos], prev_pos_width) - viewport_x - 1) + offset_x, + y - viewport_y + offset_y, viewport_width, - line.height + 1)); + h + 1)); break; } case HorizontalAlignment.Center: { owner.Invalidate(new Rectangle( - line.X + offset_x, - line.Y - viewport_y + offset_y, + x + offset_x, + y - viewport_y + offset_y, viewport_width, - line.height + 1)); + h + 1)); break; } case HorizontalAlignment.Right: { owner.Invalidate(new Rectangle( - line.X + offset_x, - line.Y - viewport_y + offset_y, - (int)line.widths[pos + 1] - viewport_x + line.X, - line.height + 1)); + x + offset_x, + y - viewport_y + offset_y, + (int)Math.Max(line.widths[pos + ((line.widths.Length > pos + 1) ? 1 : 0)], prev_pos1_width) - viewport_x + line.X, + h + 1)); break; } } @@ -1265,6 +1279,10 @@ namespace System.Windows.Forms { // When we're at a tag boundary we want the cursor in the previous (left) tag // whereas FindTag(pos) gets the next (right) tag. LineTag.Start is 1-based. + if (pos > line.TextLengthWithoutEnding()) + pos = line.TextLengthWithoutEnding(); + // We don't want the caret after the line ending. + MoveCaretToTextTag (); caret.line = line; @@ -1292,6 +1310,11 @@ namespace System.Windows.Forms { } caret.tag = FindCursor(x, y, out caret.pos); + + if (caret.pos > caret.tag.Line.TextLengthWithoutEnding()) + caret.pos = caret.tag.Line.TextLengthWithoutEnding(); + // Don't allow the caret to be positioned after the line ending. + // This was happening with the up and down arrows due to how FindCursor works. MoveCaretToTextTag (); @@ -1435,6 +1458,10 @@ namespace System.Windows.Forms { // FIXME should we use IsWordSeparator to detect whitespace, instead // of looking for actual spaces in the Word move cases? + Line currentLine = caret.line; + int currentPos = caret.pos; + LineTag currentTag = caret.tag; + bool nowrap = false; switch(direction) { case CaretDirection.CharForwardNoWrap: @@ -1462,7 +1489,7 @@ namespace System.Windows.Forms { } } UpdateCaret(); - return; + break; } case CaretDirection.CharBackNoWrap: @@ -1470,13 +1497,13 @@ namespace System.Windows.Forms { goto case CaretDirection.CharBack; case CaretDirection.CharBack: { if (caret.pos > 0) { - // caret.pos--; // folded into the if below + caret.pos--; // folded into the if below - if (--caret.pos > 0) { - if (caret.tag.Start > caret.pos) { - caret.tag = caret.tag.Previous; - } + //if (--caret.pos > 0) { + if (caret.tag.Start > caret.pos && caret.tag.Previous != null) { + caret.tag = caret.tag.Previous; } + //} } else { if (caret.line.line_no > 1 && !nowrap) { caret.line = GetLine(caret.line.line_no - 1); @@ -1485,7 +1512,7 @@ namespace System.Windows.Forms { } } UpdateCaret(); - return; + break; } case CaretDirection.WordForward: { @@ -1511,7 +1538,7 @@ namespace System.Windows.Forms { } } UpdateCaret(); - return; + break; } case CaretDirection.WordBack: { @@ -1543,31 +1570,31 @@ namespace System.Windows.Forms { } } UpdateCaret(); - return; + break; } case CaretDirection.LineUp: { if (caret.line.line_no > 1) { int pixel; - pixel = (int)caret.line.widths[caret.pos]; + pixel = (int)caret.line.widths[caret.pos] + caret.line.align_shift; PositionCaret(pixel, GetLine(caret.line.line_no - 1).Y); DisplayCaret (); } - return; + break; } case CaretDirection.LineDown: { if (caret.line.line_no < lines) { int pixel; - pixel = (int)caret.line.widths[caret.pos]; + pixel = (int)caret.line.widths[caret.pos] + caret.line.align_shift; PositionCaret(pixel, GetLine(caret.line.line_no + 1).Y); DisplayCaret (); } - return; + break; } case CaretDirection.Home: { @@ -1576,7 +1603,7 @@ namespace System.Windows.Forms { caret.tag = caret.line.tags; UpdateCaret(); } - return; + break; } case CaretDirection.End: { @@ -1585,7 +1612,7 @@ namespace System.Windows.Forms { caret.tag = LineTag.FindTag(caret.line, caret.pos); UpdateCaret(); } - return; + break; } case CaretDirection.PgUp: { @@ -1594,17 +1621,20 @@ namespace System.Windows.Forms { owner.vscroll.Value = 0; Line line = GetLine (1); PositionCaret (line, 0); + break; } int y_offset = caret.line.Y + caret.line.height - 1 - viewport_y; + int expected_y = viewport_y - viewport_height; int index; - LineTag top = FindCursor ((int) caret.line.widths [caret.pos], - viewport_y - viewport_height, out index); + LineTag top = FindCursor ((int) caret.line.widths [caret.pos] + caret.line.align_shift, + expected_y, out index); owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height); - PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y); + PositionCaret ((int) caret.line.widths [caret.pos] + caret.line.align_shift, + (expected_y >= 0) ? y_offset + viewport_y : 0); - return; + break; } case CaretDirection.PgDn: { @@ -1613,23 +1643,26 @@ namespace System.Windows.Forms { owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1; Line line = GetLine (lines); PositionCaret (line, line.TextLengthWithoutEnding()); + break; } int y_offset = caret.line.Y - viewport_y; + int expected_y = viewport_y + viewport_height; int index; - LineTag top = FindCursor ((int) caret.line.widths [caret.pos], - viewport_y + viewport_height, out index); + LineTag top = FindCursor ((int) caret.line.widths [caret.pos] + caret.line.align_shift, + expected_y, out index); owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height); - PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y); + PositionCaret ((int) caret.line.widths [caret.pos] + caret.line.align_shift, + (expected_y <= document_y - viewport_height) ? y_offset + viewport_y : document_y); - return; + break; } case CaretDirection.CtrlPgUp: { PositionCaret(0, viewport_y); DisplayCaret (); - return; + break; } case CaretDirection.CtrlPgDn: { @@ -1645,7 +1678,7 @@ namespace System.Windows.Forms { } PositionCaret(line, line.Text.Length); DisplayCaret (); - return; + break; } case CaretDirection.CtrlHome: { @@ -1654,7 +1687,7 @@ namespace System.Windows.Forms { caret.tag = caret.line.tags; UpdateCaret(); - return; + break; } case CaretDirection.CtrlEnd: { @@ -1663,7 +1696,7 @@ namespace System.Windows.Forms { caret.tag = LineTag.FindTag(caret.line, caret.pos); UpdateCaret(); - return; + break; } case CaretDirection.SelectionStart: { @@ -1672,7 +1705,7 @@ namespace System.Windows.Forms { caret.tag = selection_start.tag; UpdateCaret(); - return; + break; } case CaretDirection.SelectionEnd: { @@ -1681,9 +1714,21 @@ namespace System.Windows.Forms { caret.tag = selection_end.tag; UpdateCaret(); - return; + break; } } + + if ((caret.pos != currentPos || caret.line != currentLine) && currentTag.Length == 0) { + // Remove the empty tag it was previously on. + if (currentTag.Previous != null) { + currentTag.Previous.Next = currentTag.Next; + } else if (currentTag.Next != null) { + // update line.tags, but don't set it to null! + currentLine.tags = currentTag.Next; + } + if (currentTag.Next != null) + currentTag.Next.Previous = currentTag.Previous; + } } internal void DumpDoc () @@ -1695,8 +1740,8 @@ namespace System.Windows.Forms { LineTag tag = line.tags; while (tag != null) { - Console.Write ("\t<tag type='{0}' span='{1}->{2}' font='{3}' color='{4}'>", - tag.GetType (), tag.Start, tag.Length, tag.Font, tag.Color); + Console.Write ("\t<tag type='{0}' span='{1}->{2}' font='{3}' color='{4}' position='{5}' charoffset='{6}'>", + tag.GetType (), tag.Start, tag.Length, tag.Font, tag.Color, tag.TextPosition, tag.CharOffset); Console.Write (tag.Text ()); Console.WriteLine ("</tag>"); tag = tag.Next; @@ -1825,8 +1870,9 @@ namespace System.Windows.Forms { continue; } - if (((tag.X + tag.Width) < (clip.Left - viewport_x - offset_x)) && + if (((tag.X + tag.Width) < (clip.Left - viewport_x - offset_x)) && (tag.X > (clip.Right - viewport_x - offset_x))) { + // Don't draw a tag that is horizontally outside the visible region. tag = tag.Next; continue; } @@ -1864,7 +1910,7 @@ namespace System.Windows.Forms { if (current_backcolor != Color.Empty && current_backcolor != owner.BackColor) { g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (current_backcolor), offset_x + line.widths [old_tag_pos - 1] + line.X - viewport_x, - line_y + tag.Shift - tag.CharOffset - line.SpacingBefore, + line_y - line.SpacingBefore, line.widths [Math.Min (tag.Start + tag.Length, tag_pos) - 1] - line.widths [old_tag_pos - 1], line.height); } @@ -1997,7 +2043,21 @@ namespace System.Windows.Forms { internal void Insert (Line line, int pos, bool update_caret, string s) { - Insert (line, pos, update_caret, s, line.FindTag (pos)); + LineTag tag = line.FindTag(pos); + if (tag.Length != 0) { + if (tag.Start == pos + 1) { // pos is zero-based, tag.Start and tag.End are one-based. + // Check for empty tags before this one at the same position + var t = tag.Previous; + while (t != null && t.End == pos + 1) { + if (t.Length == 0) { + tag = t; + break; + } + t = t.Previous; + } + } // There will never be empty tags after this one, because FindTag gets the last tag at the position. + } + Insert (line, pos, update_caret, s, tag); } // Insert text at the given position; use formatting at insertion point for inserted text @@ -3239,12 +3299,9 @@ namespace System.Windows.Forms { int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos); SuspendRecalc (); - if (!String.IsNullOrEmpty(s)) { - Insert(selection_end.line, selection_end.pos, false, s); - undo.RecordInsertString(selection_end.line, selection_end.pos, s); - } + var formatTag = selection_start.tag; - // Then delete any selected text + // Delete any selected text if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) { if (selection_start.line == selection_end.line) { undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); @@ -3286,6 +3343,12 @@ namespace System.Windows.Forms { } } + if (!String.IsNullOrEmpty(s)) { + Insert(selection_start.line, selection_start.pos, false, s); + LineTag.FormatText(selection_start.line, selection_start.pos + 1, s.Length, formatTag); // 0-base to 1-base... + undo.RecordInsertString(selection_start.line, selection_start.pos, s); + } + Line begin_update_line = selection_start.line; @@ -3450,6 +3513,55 @@ namespace System.Windows.Forms { return null; } + internal IEnumerable<Line> TransverseLines(int start, int end) { + Line l, c, r, prev = null; + bool r2l = start > end; + int number; + if (r2l) { + // swap start and end so that start is less than end + int s = start; + start = end; + end = s; + } + c = document; + while (c != null && c != sentinel) { + l = c.left; + r = c.right; + + if (((r2l && c.line_no < end) || (!r2l && c.line_no > start)) && (r2l ? r : l) != sentinel && (r2l ? r : l) != prev) { + // There's no point going further this way if we're just finding lines we don't want! + c = r2l ? r : l; + continue; + } + + number = c.line_no; + if (number >= start && number <= end) + yield return c; + + if ((r2l && number <= start) || (!r2l && number >= end)) + yield break; // We're done here, no need to look further. + + if ((r2l ? l : r) != sentinel) { + c = r2l ? l : r; + } else { + // If we're on the first-side node, the parent is finished with too (continues up tree), otherwise we're only done with the current node. + // We don't want to come back to first-side nodes we've already done when we do the new parent. + // The highest node we discard is going to be a first-side node, because we're discarding all second-side nodes we run into. + // The exception is when we run off the top, which is just fine too, because we're done. + // But the highest node we discard is already visited, so that is the one we're not allowed back to -- anything higher needs visiting still. + prev = c; + c = c.parent; + // The xor inverts the condition when we're going right-to-left, and therefore trims non-right (i.e. left) branches. + // With both forwards and reverse transversal, prev will be given the first-side node. + while (c != null && c.parent != null && c.right == prev ^ r2l) { + prev = c; + c = c.parent; + } + // And prev is now the previous first-side node, unless we happen to have none remaining! + } + } + } + /// <summary>Retrieve the previous tag; walks line boundaries</summary> internal LineTag PreviousTag(LineTag tag) { Line l; |