New tests.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / RichTextBox.cs
index 135dd401c722d485561433b19840065d87611200..1db7f29a519e0819d7696faeb9cff1a41f001da9 100644 (file)
@@ -48,6 +48,7 @@ namespace System.Windows.Forms {
                internal bool           auto_word_select;
                internal int            bullet_indent;
                internal bool           detect_urls;
+               private bool            reuse_line;     // Sometimes we are loading text with already available lines
                internal int            margin_right;
                internal float          zoom;
                private StringBuilder   rtf_line;
@@ -407,10 +408,24 @@ namespace System.Windows.Forms {
                                sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
 
                                data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
-                               InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
+                               int cursor_x = document.selection_start.pos;
+                               int cursor_y = document.selection_start.line.line_no;
+
+                               // The RFT parser by default, when finds our x cursor in 0, it thinks if needs to
+                               // add a new line; but in *this* scenario the line is already created, so force it to reuse it.
+                               // Hackish, but works without touching the heart of the buggy parser.
+                               if (cursor_x == 0)
+                                       reuse_line = true;
+
+                               InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
                                data.Close();
 
-                               document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
+                               int nl_length = document.LineEndingLength (XplatUI.RunningOnUnix ? LineEnding.Rich : LineEnding.Hard);
+                               document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * nl_length, 
+                                               out line, out tag, out sel_start);
+                               if (sel_start >= line.text.Length)
+                                       sel_start = line.text.Length -1;
+
                                document.SetSelection(line, sel_start);
                                document.PositionCaret(line, sel_start);
                                document.DisplayCaret();
@@ -428,6 +443,8 @@ namespace System.Windows.Forms {
                        }
 
                        set {
+                               // TextBox/TextBoxBase don't set Modified in this same property
+                               Modified = true;
                                base.SelectedText = value;
                        }
                }
@@ -1744,9 +1761,9 @@ namespace System.Windows.Forms {
 
                        rtf_chars += rtf_line.Length;
 
-
-
-                       if (rtf_cursor_x == 0) {
+                       // Try to re-use if we are told so - this usually happens when we are inserting a flow of rtf text
+                       // with an already alive line.
+                       if (rtf_cursor_x == 0 && !reuse_line) {
                                if (newline && rtf_line.ToString ().EndsWith (Environment.NewLine) == false)
                                        rtf_line.Append (Environment.NewLine);
 
@@ -1774,6 +1791,8 @@ namespace System.Windows.Forms {
                                        if (line.Text.EndsWith (Environment.NewLine) == false)
                                                line.Text += Environment.NewLine;
                                }
+
+                               reuse_line = false; // sanity assignment - in this case we have already re-used one line.
                        }
 
                        if (newline) {
@@ -1929,9 +1948,55 @@ namespace System.Windows.Forms {
                        }
                }
 
-               [MonoInternalNote ("Emit unicode and other special characters properly")]
+               static readonly char [] ReservedRTFChars = new char [] { '\\', '{', '}' };
+
                private void EmitRTFText(StringBuilder rtf, string text) {
-                       rtf.Append(text);
+                       int start = rtf.Length;
+                       int count = text.Length;
+
+                       // First emit simple unicode chars as escaped
+                       EmitEscapedUnicode (rtf, text);
+
+                       // This method emits user text *only*, so it's safe to escape any reserved rtf chars
+                       // Escape '\' first, since it is used later to escape the other chars
+                       if (text.IndexOfAny (ReservedRTFChars) > -1) {
+                               rtf.Replace ("\\", "\\\\", start, count);
+                               rtf.Replace ("{", "\\{", start, count);
+                               rtf.Replace ("}", "\\}", start, count);
+                       }
+               }
+
+               // The chars to be escaped use "\'" + its hexadecimal value.
+               private void EmitEscapedUnicode (StringBuilder sb, string text)
+               {
+                       int pos;
+                       int start = 0;
+
+                       while ((pos = IndexOfNonAscii (text, start)) > -1) {
+                               sb.Append (text, start, pos - start);
+
+                               int n = (int)text [pos];
+                               sb.Append ("\\'");
+                               sb.Append (n.ToString ("X"));
+
+                               start = pos + 1;
+                       }
+
+                       // Append remaining (maybe all) the text value.
+                       if (start < text.Length)
+                               sb.Append (text, start, text.Length - start);
+               }
+
+               // MS seems to be escaping values larger than 0x80
+               private int IndexOfNonAscii (string text, int startIndex)
+               {
+                       for (int i = startIndex; i < text.Length; i++) {
+                               int n = (int)text [i];
+                               if (n < 0 || n >= 0x80)
+                                       return i;
+                       }
+
+                       return -1;
                }
 
                // start_pos and end_pos are 0-based