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;
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();
}
set {
+ // TextBox/TextBoxBase don't set Modified in this same property
+ Modified = true;
base.SelectedText = value;
}
}
}
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());
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);
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) {
}
}
- [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
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");
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))) {
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;}");
}
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;
}