[ClassInterface (ClassInterfaceType.AutoDispatch)]
[Docking (DockingBehavior.Ask)]
[ComVisible (true)]
+ [Designer ("System.Windows.Forms.Design.RichTextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
#endif
public class RichTextBox : TextBoxBase {
#region Local Variables
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 RTF.TextMap rtf_text_map;
- private int rtf_skip_width;
- private int rtf_skip_count;
private StringBuilder rtf_line;
- private SolidBrush rtf_color;
- private RTF.Font rtf_rtffont;
- private int rtf_rtffont_size;
- private FontStyle rtf_rtfstyle;
- private HorizontalAlignment rtf_rtfalign;
+
+ private RtfSectionStyle rtf_style; // Replaces individual style
+ // properties so we can revert
+ private Stack rtf_section_stack;
+
+ private RTF.TextMap rtf_text_map;
+ private int rtf_skip_count;
private int rtf_cursor_x;
private int rtf_cursor_y;
private int rtf_chars;
- private int rtf_par_line_left_indent;
+
+#if NET_2_0
+ private bool enable_auto_drag_drop;
+ private RichTextBoxLanguageOptions language_option;
+ private bool rich_text_shortcuts_enabled;
+ private Color selection_back_color;
+#endif
#endregion // Local Variables
#region Public Constructors
public RichTextBox() {
accepts_return = true;
+ auto_size = false;
auto_word_select = false;
bullet_indent = 0;
- detect_urls = true;
- max_length = Int32.MaxValue;
+ base.MaxLength = Int32.MaxValue;
margin_right = 0;
zoom = 1;
base.Multiline = true;
document.CRLFSize = 1;
+ shortcuts_enabled = true;
+ base.EnableLinks = true;
+ richtext = true;
+
+ rtf_style = new RtfSectionStyle ();
+ rtf_section_stack = null;
scrollbars = RichTextBoxScrollBars.Both;
alignment = HorizontalAlignment.Left;
LostFocus += new EventHandler(RichTextBox_LostFocus);
GotFocus += new EventHandler(RichTextBox_GotFocus);
BackColor = ThemeEngine.Current.ColorWindow;
+#if NET_2_0
+ backcolor_set = false;
+ language_option = RichTextBoxLanguageOptions.AutoFontSizeAdjust;
+ rich_text_shortcuts_enabled = true;
+ selection_back_color = DefaultBackColor;
+#endif
ForeColor = ThemeEngine.Current.ColorWindowText;
+
base.HScrolled += new EventHandler(RichTextBox_HScrolled);
base.VScrolled += new EventHandler(RichTextBox_VScrolled);
#endregion // Public Constructors
#region Private & Internal Methods
+
+ internal override void HandleLinkClicked (LinkRectangle link)
+ {
+ OnLinkClicked (new LinkClickedEventArgs (link.LinkTag.LinkText));
+ }
+
+ internal override Color ChangeBackColor (Color backColor)
+ {
+ if (backColor == Color.Empty) {
+#if NET_2_0
+ backcolor_set = false;
+ if (!ReadOnly) {
+ backColor = SystemColors.Window;
+ }
+#else
+ backColor = SystemColors.Window;
+#endif
+ }
+ return backColor;
+ }
+
+ internal override void RaiseSelectionChanged()
+ {
+ OnSelectionChanged (EventArgs.Empty);
+ }
+
private void RichTextBox_LostFocus(object sender, EventArgs e) {
Invalidate();
}
}
}
+ [MonoTODO ("Value not respected, always true")]
[DefaultValue(false)]
public bool AutoWordSelection {
- get {
- return auto_word_select;
- }
-
- set {
- auto_word_select = true;
- }
+ get { return auto_word_select; }
+ set { auto_word_select = value; }
}
[Browsable(false)]
set { base.BackgroundImage = value; }
}
+#if NET_2_0
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+#endif
+
[DefaultValue(0)]
[Localizable(true)]
public int BulletIndent {
[DefaultValue(true)]
public bool DetectUrls {
- get {
- return detect_urls;
- }
+ get { return base.EnableLinks; }
+ set { base.EnableLinks = value; }
+ }
- set {
- detect_urls = true;
- }
+#if NET_2_0
+ [MonoTODO ("Stub, does nothing")]
+ [DefaultValue (false)]
+ public bool EnableAutoDragDrop {
+ get { return enable_auto_drag_drop; }
+ set { enable_auto_drag_drop = value; }
}
+#endif
public override Font Font {
get {
// Font changes always set the whole doc to that font
start = document.GetLine(1);
end = document.GetLine(document.Lines);
- document.FormatText(start, 1, end, end.text.Length + 1, base.Font, null, null, FormatSpecified.Font);
+ document.FormatText(start, 1, end, end.text.Length + 1, base.Font, Color.Empty, Color.Empty, FormatSpecified.Font);
}
}
}
}
}
+#if NET_2_0
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public RichTextBoxLanguageOptions LanguageOption {
+ get { return language_option; }
+ set { language_option = value; }
+ }
+#endif
+
[DefaultValue(Int32.MaxValue)]
public override int MaxLength {
- get {
- return base.max_length;
- }
-
- set {
- base.max_length = value;
- }
+ get { return base.MaxLength; }
+ set { base.MaxLength = value; }
}
[DefaultValue(true)]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
public string RedoActionName {
get {
return document.undo.RedoActionName;
}
}
+#if NET_2_0
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DefaultValue (true)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public bool RichTextShortcutsEnabled {
+ get { return rich_text_shortcuts_enabled; }
+ set { rich_text_shortcuts_enabled = value; }
+ }
+#endif
+
[DefaultValue(0)]
[Localizable(true)]
- [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
+ [MonoTODO ("Stub, does nothing")]
+ [MonoInternalNote ("Teach TextControl.RecalculateLine to consider the right margin as well")]
public int RightMargin {
get {
return margin_right;
}
set {
- scrollbars = value;
+ if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
+ throw new InvalidEnumArgumentException ("value", (int) value,
+ typeof (RichTextBoxScrollBars));
+
+ if (value != scrollbars) {
+ scrollbars = value;
+ CalculateDocument ();
+ }
}
}
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;
}
}
}
}
+#if NET_2_0
+ [MonoTODO ("Stub, does nothing")]
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Color SelectionBackColor {
+ get { return selection_back_color; }
+ set { selection_back_color = value; }
+ }
+#endif
+
[Browsable(false)]
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public bool SelectionBullet {
get {
return false;
[Browsable(false)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public int SelectionCharOffset {
get {
return 0;
LineTag end;
LineTag tag;
- start = document.selection_start.tag;
- end = document.selection_end.tag;
- color = ((SolidBrush)document.selection_start.tag.color).Color;
+ if (selection_length > 0) {
+ start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
+ end = document.selection_start.line.FindTag (document.selection_end.pos);
+ } else {
+ start = document.selection_start.line.FindTag (document.selection_start.pos);
+ end = start;
+ }
+
+ color = start.Color;
tag = start;
- while (true) {
- if (!color.Equals(((SolidBrush)tag.color).Color)) {
+ while (tag != null) {
+
+ if (!color.Equals (tag.Color))
return Color.Empty;
- }
- if (tag == end) {
+ if (tag == end)
break;
- }
-
- tag = document.NextTag(tag);
- if (tag == null) {
- break;
- }
+ tag = document.NextTag (tag);
}
return color;
}
set {
- FontDefinition attributes;
- int sel_start;
- int sel_end;
-
- attributes = new FontDefinition();
- attributes.color = value;
+ if (value == Color.Empty)
+ value = DefaultForeColor;
+
+ int sel_start;
+ int sel_end;
sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
- document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
+ document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
+ document.selection_end.line, document.selection_end.pos + 1, null,
+ value, Color.Empty, FormatSpecified.Color);
document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
document.UpdateView(document.selection_start.line, 0);
- document.AlignCaret();
+
+ //Re-Align the caret in case its changed size or position
+ //probably not necessary here
+ document.AlignCaret(false);
}
}
LineTag end;
LineTag tag;
- start = document.selection_start.tag;
- end = document.selection_end.tag;
- font = document.selection_start.tag.font;
+ if (selection_length > 0) {
+ start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
+ end = document.selection_start.line.FindTag (document.selection_end.pos);
+ } else {
+ start = document.selection_start.line.FindTag (document.selection_start.pos);
+ end = start;
+ }
+
+ font = start.Font;
if (selection_length > 1) {
tag = start;
- while (true) {
+ while (tag != null) {
- if (!font.Equals(tag.font)) {
+ if (!font.Equals(tag.Font))
return null;
- }
- if (tag == end) {
+ if (tag == end)
break;
- }
-
- tag = document.NextTag(tag);
- if (tag == null) {
- break;
- }
+ tag = document.NextTag (tag);
}
}
}
set {
- FontDefinition attributes;
int sel_start;
int sel_end;
- attributes = new FontDefinition();
- attributes.font_obj = value;
-
sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
- document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
+ document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
+ document.selection_end.line, document.selection_end.pos + 1, value,
+ Color.Empty, Color.Empty, FormatSpecified.Font);
document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
document.UpdateView(document.selection_start.line, 0);
- document.AlignCaret();
+ //Re-Align the caret in case its changed size or position
+ Document.AlignCaret (false);
}
}
[Browsable(false)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public int SelectionHangingIndent {
get {
return 0;
[Browsable(false)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public int SelectionIndent {
get {
return 0;
[Browsable(false)]
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public bool SelectionProtected {
get {
return false;
[Browsable(false)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public int SelectionRightIndent {
get {
return 0;
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public int[] SelectionTabs {
get {
return new int[0];
}
[DefaultValue(false)]
- [MonoTODO]
+ [MonoTODO ("Stub, does nothing")]
public bool ShowSelectionMargin {
get {
return false;
return Find(str, -1, -1, options);
}
+
+#if !NET_2_0
public char GetCharFromPosition(Point pt) {
LineTag tag;
int pos;
PointToTagPos(pt, out tag, out pos);
- if (pos >= tag.line.text.Length) {
+ if (pos >= tag.Line.text.Length) {
return '\n';
}
- return tag.line.text[pos];
-
+ return tag.Line.text[pos];
+ }
+#else
+ internal override char GetCharFromPositionInternal (Point p)
+ {
+ LineTag tag;
+ int pos;
+
+ PointToTagPos (p, out tag, out pos);
+
+ if (pos >= tag.Line.text.Length)
+ return '\n';
+
+ return tag.Line.text[pos];
}
+#endif
- public int GetCharIndexFromPosition(Point pt) {
+ public
+#if NET_2_0
+ override
+#endif
+ int GetCharIndexFromPosition(Point pt) {
LineTag tag;
int pos;
PointToTagPos(pt, out tag, out pos);
- return document.LineTagToCharIndex(tag.line, pos);
+ return document.LineTagToCharIndex(tag.Line, pos);
}
- public int GetLineFromCharIndex(int index) {
+ public
+#if NET_2_0
+ override
+#endif
+ int GetLineFromCharIndex(int index) {
Line line;
LineTag tag;
int pos;
return line.LineNo - 1;
}
- public Point GetPositionFromCharIndex(int index) {
+ public
+#if NET_2_0
+ override
+#endif
+ Point GetPositionFromCharIndex(int index) {
Line line;
LineTag tag;
int pos;
document.CharIndexToLineTag(index, out line, out tag, out pos);
-
- return new Point((int)line.widths[pos] + 1, line.Y + 1);
+ return new Point(line.X + (int)line.widths[pos] + document.OffsetX - document.ViewPortX,
+ line.Y + document.OffsetY - document.ViewPortY);
}
public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
// FIXME - ignoring unicode
if (fileType == RichTextBoxStreamType.PlainText) {
- StringBuilder sb;
- int count;
- byte[] buffer;
+ StringBuilder sb;
+ char[] buffer;
try {
- sb = new StringBuilder((int)data.Length);
- buffer = new byte[1024];
+ sb = new StringBuilder ((int) data.Length);
+ buffer = new char [1024];
} catch {
throw new IOException("Not enough memory to load document");
}
- count = 0;
- while (count < data.Length) {
- count += data.Read(buffer, count, 1024);
- sb.Append(buffer);
+ StreamReader sr = new StreamReader (data, Encoding.Default, true);
+ int charsRead = sr.Read (buffer, 0, buffer.Length);
+ while (charsRead > 0) {
+ sb.Append (buffer, 0, charsRead);
+ charsRead = sr.Read (buffer, 0, buffer.Length);
}
+
+ // Remove the EOF converted to an extra EOL by the StreamReader
+ if (sb.Length > 0 && sb [sb.Length - 1] == '\n')
+ sb.Remove (sb.Length - 1, 1);
+
base.Text = sb.ToString();
return;
}
ScrollToCaret ();
}
- [MonoTODO("Make smarter RTF detection?")]
public void LoadFile(string path) {
- if (path.EndsWith(".rtf")) {
- LoadFile(path, RichTextBoxStreamType.RichText);
- } else {
- LoadFile(path, RichTextBoxStreamType.PlainText);
- }
+ LoadFile (path, RichTextBoxStreamType.RichText);
}
public void LoadFile(string path, RichTextBoxStreamType fileType) {
LoadFile(data, fileType);
}
#if !DEBUG
- catch {
- throw new IOException("Could not open file " + path);
+ catch (Exception ex) {
+ throw new IOException("Could not open file " + path, ex);
}
#endif
finally {
public void Redo()
{
- document.undo.Redo ();
+ if (document.undo.Redo ())
+ OnTextChanged (EventArgs.Empty);
}
public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
}
for (i = 1; i < document.Lines; i++) {
- bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
+ // 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());
// }
}
+#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new void DrawToBitmap (Bitmap bitmap, Rectangle targetBounds)
+ {
+ Graphics dc = Graphics.FromImage (bitmap);
+
+ Draw (dc, targetBounds);
+ }
+#endif
+
#endregion // Public Instance Methods
#region Protected Instance Methods
eh (this, e);
}
- [MonoTODO("Determine when to call this")]
+ [MonoTODO ("Stub, never called")]
protected virtual void OnImeChange(EventArgs e) {
EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
if (eh != null)
eh (this, e);
}
+#if !NET_2_0
protected override void OnSystemColorsChanged(EventArgs e) {
base.OnSystemColorsChanged (e);
}
protected override void OnTextChanged(EventArgs e) {
base.OnTextChanged (e);
}
+#endif
protected virtual void OnVScroll(EventArgs e) {
EventHandler eh = (EventHandler)(Events [VScrollEvent]);
protected override void WndProc(ref Message m) {
base.WndProc (ref m);
}
+
+#if NET_2_0
+ protected override bool ProcessCmdKey (ref Message m, Keys keyData)
+ {
+ return base.ProcessCmdKey (ref m, keyData);
+ }
+#endif
#endregion // Protected Instance Methods
#region Events
remove { base.BackgroundImageChanged -= value; }
}
+#if NET_2_0
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+#endif
+
public event ContentsResizedEventHandler ContentsResized {
add { Events.AddHandler (ContentsResizedEvent, value); }
remove { Events.RemoveHandler (ContentsResizedEvent, value); }
}
+#if !NET_2_0
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler DoubleClick {
add { base.DoubleClick += value; }
remove { base.DoubleClick -= value; }
}
+#endif
[Browsable(false)]
#if !NET_2_0
remove { base.QueryContinueDrag -= value; }
}
+ [MonoTODO ("Event never raised")]
public event EventHandler SelectionChanged {
add { Events.AddHandler (SelectionChangedEvent, value); }
remove { Events.RemoveHandler (SelectionChangedEvent, value); }
document.ExpandSelection(CaretSelection.Word, false);
}
+ private class RtfSectionStyle : ICloneable {
+ internal Color rtf_color;
+ internal RTF.Font rtf_rtffont;
+ internal int rtf_rtffont_size;
+ internal FontStyle rtf_rtfstyle;
+ internal HorizontalAlignment rtf_rtfalign;
+ internal int rtf_par_line_left_indent;
+ internal bool rtf_visible;
+ internal int rtf_skip_width;
+
+ public object Clone ()
+ {
+ RtfSectionStyle new_style = new RtfSectionStyle ();
+
+ new_style.rtf_color = rtf_color;
+ new_style.rtf_par_line_left_indent = rtf_par_line_left_indent;
+ new_style.rtf_rtfalign = rtf_rtfalign;
+ new_style.rtf_rtffont = rtf_rtffont;
+ new_style.rtf_rtffont_size = rtf_rtffont_size;
+ new_style.rtf_rtfstyle = rtf_rtfstyle;
+ new_style.rtf_visible = rtf_visible;
+ new_style.rtf_skip_width = rtf_skip_width;
+
+ return new_style;
+ }
+ }
+
+ // To allow us to keep track of the sections and revert formatting
+ // as we go in and out of sections of the document.
+ private void HandleGroup (RTF.RTF rtf)
+ {
+ //start group - save the current formatting on to a stack
+ //end group - go back to the formatting at the current group
+ if (rtf_section_stack == null) {
+ rtf_section_stack = new Stack ();
+ }
+
+ if (rtf.Major == RTF.Major.BeginGroup) {
+ rtf_section_stack.Push (rtf_style.Clone ());
+ //spec specifies resetting unicode ignore at begin group as an attempt at error
+ //recovery.
+ rtf_skip_count = 0;
+ } else if (rtf.Major == RTF.Major.EndGroup) {
+ if (rtf_section_stack.Count > 0) {
+ FlushText (rtf, false);
+
+ rtf_style = (RtfSectionStyle) rtf_section_stack.Pop ();
+ }
+ }
+ }
+
+ [MonoInternalNote ("Add QuadJust support for justified alignment")]
private void HandleControl(RTF.RTF rtf) {
switch(rtf.Major) {
case RTF.Major.Unicode: {
switch(rtf.Minor) {
- case Minor.UnicodeCharBytes: {
- rtf_skip_width = rtf.Param;
+ case RTF.Minor.UnicodeCharBytes: {
+ rtf_style.rtf_skip_width = rtf.Param;
break;
}
- case Minor.UnicodeChar: {
- rtf_skip_count += rtf_skip_width;
+ case RTF.Minor.UnicodeChar: {
+ FlushText (rtf, false);
+ rtf_skip_count += rtf_style.rtf_skip_width;
rtf_line.Append((char)rtf.Param);
break;
}
case RTF.Major.CharAttr: {
switch(rtf.Minor) {
- case Minor.ForeColor: {
+ case RTF.Minor.ForeColor: {
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) {
- this.rtf_color = new SolidBrush(ForeColor);
+ this.rtf_style.rtf_color = ForeColor;
} else {
- this.rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
+ this.rtf_style.rtf_color = Color.FromArgb(color.Red, color.Green, color.Blue);
}
FlushText (rtf, false);
}
break;
}
- case Minor.FontSize: {
+ case RTF.Minor.FontSize: {
FlushText(rtf, false);
- this.rtf_rtffont_size = rtf.Param / 2;
+ this.rtf_style.rtf_rtffont_size = rtf.Param / 2;
break;
}
- case Minor.FontNum: {
+ case RTF.Minor.FontNum: {
System.Windows.Forms.RTF.Font font;
font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
if (font != null) {
FlushText(rtf, false);
- this.rtf_rtffont = font;
+ this.rtf_style.rtf_rtffont = font;
}
break;
}
- case Minor.Plain: {
+ case RTF.Minor.Plain: {
FlushText(rtf, false);
- rtf_rtfstyle = FontStyle.Regular;
+ rtf_style.rtf_rtfstyle = FontStyle.Regular;
break;
}
- case Minor.Bold: {
+ case RTF.Minor.Bold: {
FlushText(rtf, false);
if (rtf.Param == RTF.RTF.NoParam) {
- rtf_rtfstyle |= FontStyle.Bold;
+ rtf_style.rtf_rtfstyle |= FontStyle.Bold;
} else {
- rtf_rtfstyle &= ~FontStyle.Bold;
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Bold;
}
break;
}
- case Minor.Italic: {
+ case RTF.Minor.Italic: {
FlushText(rtf, false);
if (rtf.Param == RTF.RTF.NoParam) {
- rtf_rtfstyle |= FontStyle.Italic;
+ rtf_style.rtf_rtfstyle |= FontStyle.Italic;
} else {
- rtf_rtfstyle &= ~FontStyle.Italic;
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Italic;
}
break;
}
- case Minor.StrikeThru: {
+ case RTF.Minor.StrikeThru: {
FlushText(rtf, false);
if (rtf.Param == RTF.RTF.NoParam) {
- rtf_rtfstyle |= FontStyle.Strikeout;
+ rtf_style.rtf_rtfstyle |= FontStyle.Strikeout;
} else {
- rtf_rtfstyle &= ~FontStyle.Strikeout;
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Strikeout;
}
break;
}
- case Minor.Underline: {
+ case RTF.Minor.Underline: {
FlushText(rtf, false);
if (rtf.Param == RTF.RTF.NoParam) {
- rtf_rtfstyle |= FontStyle.Underline;
+ rtf_style.rtf_rtfstyle |= FontStyle.Underline;
} else {
- rtf_rtfstyle = rtf_rtfstyle & ~FontStyle.Underline;
+ rtf_style.rtf_rtfstyle = rtf_style.rtf_rtfstyle & ~FontStyle.Underline;
}
break;
}
- case Minor.NoUnderline: {
+ case RTF.Minor.Invisible: {
+ FlushText (rtf, false);
+ rtf_style.rtf_visible = false;
+ break;
+ }
+
+ case RTF.Minor.NoUnderline: {
FlushText(rtf, false);
- rtf_rtfstyle &= ~FontStyle.Underline;
+ rtf_style.rtf_rtfstyle &= ~FontStyle.Underline;
break;
}
}
case RTF.Major.ParAttr: {
switch (rtf.Minor) {
- case Minor.LeftIndent:
- rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
+
+ case RTF.Minor.ParDef:
+ FlushText (rtf, false);
+ rtf_style.rtf_par_line_left_indent = 0;
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ break;
+
+ case RTF.Minor.LeftIndent:
+ rtf_style.rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
+ break;
+
+ case RTF.Minor.QuadCenter:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
+ break;
+
+ case RTF.Minor.QuadJust:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
+ break;
+
+ case RTF.Minor.QuadLeft:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ break;
+
+ case RTF.Minor.QuadRight:
+ FlushText (rtf, false);
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Right;
break;
}
break;
}
-
- case RTF.Major.SpecialChar: {
+
+ case RTF.Major.SpecialChar: {
//Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
- SpecialChar(rtf);
+ SpecialChar (rtf);
break;
}
}
private void SpecialChar(RTF.RTF rtf) {
switch(rtf.Minor) {
- case Minor.Page:
- case Minor.Sect:
- case Minor.Row:
- case Minor.Line:
- case Minor.Par: {
+ case RTF.Minor.Page:
+ case RTF.Minor.Sect:
+ case RTF.Minor.Row:
+ case RTF.Minor.Line:
+ case RTF.Minor.Par: {
FlushText(rtf, true);
break;
}
- case Minor.Cell: {
+ case RTF.Minor.Cell: {
Console.Write(" ");
break;
}
- case Minor.NoBrkSpace: {
+ case RTF.Minor.NoBrkSpace: {
Console.Write(" ");
break;
}
- case Minor.Tab: {
+ case RTF.Minor.Tab: {
rtf_line.Append ("\t");
// FlushText (rtf, false);
break;
}
- case Minor.NoReqHyphen:
- case Minor.NoBrkHyphen: {
+ case RTF.Minor.NoReqHyphen:
+ case RTF.Minor.NoBrkHyphen: {
rtf_line.Append ("-");
// FlushText (rtf, false);
break;
}
- case Minor.Bullet: {
+ case RTF.Minor.Bullet: {
Console.WriteLine("*");
break;
}
- case Minor.WidowCtrl:
+ case RTF.Minor.WidowCtrl:
break;
- case Minor.EmDash: {
+ case RTF.Minor.EmDash: {
rtf_line.Append ("\u2014");
break;
}
- case Minor.EnDash: {
+ case RTF.Minor.EnDash: {
rtf_line.Append ("\u2013");
break;
}
/*
- case Minor.LQuote: {
+ case RTF.Minor.LQuote: {
Console.Write("\u2018");
break;
}
- case Minor.RQuote: {
+ case RTF.Minor.RQuote: {
Console.Write("\u2019");
break;
}
- case Minor.LDblQuote: {
+ case RTF.Minor.LDblQuote: {
Console.Write("\u201C");
break;
}
- case Minor.RDblQuote: {
+ case RTF.Minor.RDblQuote: {
Console.Write("\u201D");
break;
}
}
private void HandleText(RTF.RTF rtf) {
- if (rtf_skip_count > 0) {
- rtf_skip_count--;
- return;
+ string str = rtf.EncodedText;
+
+ //todo - simplistically skips characters, should skip bytes?
+ if (rtf_skip_count > 0 && str.Length > 0) {
+ int iToRemove = Math.Min (rtf_skip_count, str.Length);
+
+ str = str.Substring (iToRemove);
+ rtf_skip_count-=iToRemove;
}
+ /*
if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
} else {
Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
}
}
+ */
+
+ if (rtf_style.rtf_visible)
+ rtf_line.Append (str);
}
private void FlushText(RTF.RTF rtf, bool newline) {
return;
}
- if (rtf_rtffont == null) {
+ if (rtf_style.rtf_rtffont == null) {
// First font in table is default
- rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont(rtf, 0);
+ rtf_style.rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont (rtf, 0);
}
- font = new Font(rtf_rtffont.Name, rtf_rtffont_size, rtf_rtfstyle);
+ font = new Font (rtf_style.rtf_rtffont.Name, rtf_style.rtf_rtffont_size, rtf_style.rtf_rtfstyle);
- if (rtf_color == null) {
+ if (rtf_style.rtf_color == Color.Empty) {
System.Windows.Forms.RTF.Color color;
// First color in table is default
- color = System.Windows.Forms.RTF.Color.GetColor(rtf, 0);
+ color = System.Windows.Forms.RTF.Color.GetColor (rtf, 0);
if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
- rtf_color = new SolidBrush(ForeColor);
+ rtf_style.rtf_color = ForeColor;
} else {
- rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
+ rtf_style.rtf_color = Color.FromArgb (color.Red, color.Green, color.Blue);
}
-
+
}
rtf_chars += rtf_line.Length;
- if (rtf_cursor_x == 0) {
- document.Add(rtf_cursor_y, rtf_line.ToString(), rtf_rtfalign, font, rtf_color);
- if (rtf_par_line_left_indent != 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);
+
+ document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
+ newline ? LineEnding.Rich : LineEnding.Wrap);
+ if (rtf_style.rtf_par_line_left_indent != 0) {
Line line = document.GetLine (rtf_cursor_y);
- line.indent = rtf_par_line_left_indent;
+ line.indent = rtf_style.rtf_par_line_left_indent;
}
} else {
- Line line;
+ Line line;
- line = document.GetLine(rtf_cursor_y);
- line.indent = rtf_par_line_left_indent;
+ line = document.GetLine (rtf_cursor_y);
+ line.indent = rtf_style.rtf_par_line_left_indent;
if (rtf_line.Length > 0) {
- document.InsertString(line, rtf_cursor_x, rtf_line.ToString());
- document.FormatText(line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length, font, rtf_color, null, FormatSpecified.Font | FormatSpecified.Color); // FormatText is 1-based
+ document.InsertString (line, rtf_cursor_x, rtf_line.ToString ());
+ document.FormatText (line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length,
+ font, rtf_style.rtf_color, Color.Empty,
+ FormatSpecified.Font | FormatSpecified.Color);
}
if (newline) {
- document.Split(line, rtf_cursor_x + length);
+ line = document.GetLine (rtf_cursor_y);
+ line.ending = LineEnding.Rich;
+
+ 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) {
// Prepare
rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
+ rtf.ClassCallback[RTF.TokenClass.Group] = new RTF.ClassDelegate(HandleGroup);
- rtf_skip_width = 0;
rtf_skip_count = 0;
rtf_line = new StringBuilder();
- rtf_color = null;
- rtf_rtffont_size = (int)this.Font.Size;
- rtf_rtfalign = HorizontalAlignment.Left;
- rtf_rtfstyle = FontStyle.Regular;
- rtf_rtffont = null;
+ rtf_style.rtf_color = Color.Empty;
+ rtf_style.rtf_rtffont_size = (int)this.Font.Size;
+ rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
+ rtf_style.rtf_rtfstyle = FontStyle.Regular;
+ rtf_style.rtf_rtffont = null;
+ rtf_style.rtf_visible = true;
+ rtf_style.rtf_skip_width = 1;
rtf_cursor_x = cursor_x;
rtf_cursor_y = cursor_y;
rtf_chars = 0;
to_y = rtf_cursor_y;
chars = rtf_chars;
+ // clear the section stack if it was used
+ if (rtf_section_stack != null)
+ rtf_section_stack.Clear();
+
document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
document.ResumeRecalc (true);
}
}
- [MonoTODO("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
// Add default font and color; to optimize document content we don't
// use this.Font and this.ForeColor but the font/color from the first tag
tag = LineTag.FindTag(start_line, pos);
- font = tag.font;
- color = ((SolidBrush)tag.color).Color;
+ font = tag.Font;
+ color = tag.Color;
fonts.Add(font.Name);
colors.Add(color);
}
while (pos < line_len) {
- if (tag.font.Name != font.Name) {
- font = tag.font;
+ if (tag.Font.Name != font.Name) {
+ font = tag.Font;
if (!fonts.Contains(font.Name)) {
fonts.Add(font.Name);
}
}
- if (((SolidBrush)tag.color).Color != color) {
- color = ((SolidBrush)tag.color).Color;
+ if (tag.Color != color) {
+ color = tag.Color;
if (!colors.Contains(color)) {
colors.Add(color);
}
}
- pos = tag.start + tag.length - 1;
- tag = tag.next;
+ pos = tag.Start + tag.Length - 1;
+ tag = tag.Next;
}
pos = 0;
line_no++;
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;}");
// Emit initial paragraph settings
tag = LineTag.FindTag(start_line, start_pos);
sb.Append("\\pard"); // Reset to default paragraph properties
- EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.font.Name), null, tag.font); // Font properties
+ EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.Font.Name), null, tag.Font); // Font properties
sb.Append(" "); // Space separator
- font = tag.font;
+ font = tag.Font;
color = (Color)colors[0];
line = start_line;
line_no = start_line.line_no;
while (pos < line_len) {
length = sb.Length;
- if (tag.font != font) {
- EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.font.Name), font, tag.font);
- font = tag.font;
+ if (tag.Font != font) {
+ EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.Font.Name), font, tag.Font);
+ font = tag.Font;
}
- if (((SolidBrush)tag.color).Color != color) {
- color = ((SolidBrush)tag.color).Color;
+ if (tag.Color != color) {
+ color = tag.Color;
sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
}
if (length != sb.Length) {
// Emit the string itself
if (line_no != end_line.line_no) {
- EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
+ EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
} else {
- if (end_pos < (tag.start + tag.length - 1)) {
+ if (end_pos < (tag.Start + tag.Length - 1)) {
// Emit partial tag only, end_pos is inside this tag
- EmitRTFText(sb, tag.line.text.ToString(pos, end_pos - pos));
+ EmitRTFText(sb, tag.Line.text.ToString(pos, end_pos - pos));
} else {
- EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
+ EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
}
}
- pos = tag.start + tag.length - 1;
- tag = tag.next;
+ pos = tag.Start + tag.Length - 1;
+ tag = tag.Next;
}
if (pos >= line.text.Length) {
- if (!line.soft_break) {
- sb.Append("\\par\n");
+ if (line.ending != LineEnding.Wrap) {
+ sb.Append("\\par");
+ sb.Append(Environment.NewLine);
}
}
pos = 0;
line_no++;
}
- sb.Append("}\n");
+ sb.Append("}");
+ sb.Append(Environment.NewLine);
return sb;
}