New tests.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / RichTextBox.cs
index 0e19b1d721db168341bbbb6f87508081e8c632af..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;
                        }
                }
@@ -1082,7 +1099,9 @@ namespace System.Windows.Forms {
                                        }
 
                                        for (i = 1; i < document.Lines; i++) {
-                                               bytes = encoding.GetBytes(document.GetLine(i).text.ToString());
+                                               // Normalize the new lines to the system ones
+                                               string line_text = document.GetLine (i).TextWithoutEnding () + Environment.NewLine;
+                                               bytes = encoding.GetBytes(line_text);
                                                data.Write(bytes, 0, bytes.Length);
                                        }
                                        bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
@@ -1742,11 +1761,11 @@ namespace System.Windows.Forms {
 
                        rtf_chars += rtf_line.Length;
 
-
-
-                       if (rtf_cursor_x == 0) {
-                               if (newline && rtf_line.ToString ().EndsWith ("\n") == false)
-                                       rtf_line.Append ("\n");
+                       // 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);
 
                                document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
                                                                newline ? LineEnding.Rich : LineEnding.Wrap);
@@ -1769,9 +1788,11 @@ namespace System.Windows.Forms {
                                        line = document.GetLine (rtf_cursor_y);
                                        line.ending = LineEnding.Rich;
 
-                                       if (line.Text.EndsWith ("\n") == false)
-                                               line.Text += "\n";
+                                       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) {
@@ -1927,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
@@ -2006,7 +2073,7 @@ namespace System.Windows.Forms {
                        sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
 
                        // Default Language 
-                       sb.Append("\\deflang1033\n");   // FIXME - always 1033?
+                       sb.Append("\\deflang1033" + Environment.NewLine);       // FIXME - always 1033?
 
                        // Emit the font table
                        sb.Append("{\\fonttbl");
@@ -2017,7 +2084,8 @@ namespace System.Windows.Forms {
                                sb.Append((string)fonts[i]);            // Font name
                                sb.Append(";}");                        // }
                        }
-                       sb.Append("}\n");
+                       sb.Append("}");
+                       sb.Append(Environment.NewLine);
 
                        // Emit the color table (if needed)
                        if ((colors.Count > 1) || ((((Color)colors[0]).R != this.ForeColor.R) || (((Color)colors[0]).G != this.ForeColor.G) || (((Color)colors[0]).B != this.ForeColor.B))) {
@@ -2028,7 +2096,8 @@ namespace System.Windows.Forms {
                                        sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
                                        sb.Append(";");
                                }
-                               sb.Append("}\n");
+                               sb.Append("}");
+                               sb.Append(Environment.NewLine);
                        }
 
                        sb.Append("{\\*\\generator Mono RichTextBox;}");
@@ -2087,14 +2156,16 @@ namespace System.Windows.Forms {
                                }
                                if (pos >= line.text.Length) {
                                        if (line.ending != LineEnding.Wrap) {
-                                               sb.Append("\\par\n");
+                                               sb.Append("\\par");
+                                               sb.Append(Environment.NewLine);
                                        }
                                }
                                pos = 0;
                                line_no++;
                        }
 
-                       sb.Append("}\n");
+                       sb.Append("}");
+                       sb.Append(Environment.NewLine);
 
                        return sb;
                }