1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2005-2006 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok <pbartok@novell.com>
30 using System.Collections;
31 using System.ComponentModel;
33 using System.Drawing.Imaging;
36 using System.Runtime.InteropServices;
37 using RTF=System.Windows.Forms.RTF;
39 namespace System.Windows.Forms {
41 [ClassInterface (ClassInterfaceType.AutoDispatch)]
42 [Docking (DockingBehavior.Ask)]
45 public class RichTextBox : TextBoxBase {
46 #region Local Variables
47 internal bool auto_word_select;
48 internal int bullet_indent;
49 internal bool detect_urls;
50 internal int margin_right;
53 private RTF.TextMap rtf_text_map;
54 private int rtf_skip_width;
55 private int rtf_skip_count;
56 private StringBuilder rtf_line;
57 private SolidBrush rtf_color;
58 private RTF.Font rtf_rtffont;
59 private int rtf_rtffont_size;
60 private FontStyle rtf_rtfstyle;
61 private HorizontalAlignment rtf_rtfalign;
62 private int rtf_cursor_x;
63 private int rtf_cursor_y;
64 private int rtf_chars;
65 private int rtf_par_line_left_indent;
66 #endregion // Local Variables
68 #region Public Constructors
69 public RichTextBox() {
70 accepts_return = true;
71 auto_word_select = false;
74 max_length = Int32.MaxValue;
77 base.Multiline = true;
78 document.CRLFSize = 1;
79 shortcuts_enabled = true;
81 scrollbars = RichTextBoxScrollBars.Both;
82 alignment = HorizontalAlignment.Left;
83 LostFocus += new EventHandler(RichTextBox_LostFocus);
84 GotFocus += new EventHandler(RichTextBox_GotFocus);
85 BackColor = ThemeEngine.Current.ColorWindow;
86 backcolor_set = false;
87 ForeColor = ThemeEngine.Current.ColorWindowText;
89 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
90 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
93 SetStyle (ControlStyles.StandardDoubleClick, false);
96 #endregion // Public Constructors
98 #region Private & Internal Methods
99 private void RichTextBox_LostFocus(object sender, EventArgs e) {
103 private void RichTextBox_GotFocus(object sender, EventArgs e) {
106 #endregion // Private & Internal Methods
108 #region Public Instance Properties
112 public override bool AllowDrop {
114 return base.AllowDrop;
118 base.AllowDrop = value;
122 [DefaultValue(false)]
124 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
125 [RefreshProperties (RefreshProperties.Repaint)]
126 [EditorBrowsable (EditorBrowsableState.Never)]
131 public override bool AutoSize {
137 base.AutoSize = value;
141 [DefaultValue(false)]
142 public bool AutoWordSelection {
144 return auto_word_select;
148 auto_word_select = true;
153 [EditorBrowsable(EditorBrowsableState.Never)]
154 public override System.Drawing.Image BackgroundImage {
155 get { return base.BackgroundImage; }
156 set { base.BackgroundImage = value; }
161 public int BulletIndent {
163 return bullet_indent;
167 bullet_indent = value;
172 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
173 public bool CanRedo {
175 return document.undo.CanRedo;
180 public bool DetectUrls {
190 public override Font Font {
201 if (PreferredHeight != Height) {
202 Height = PreferredHeight;
208 // Font changes always set the whole doc to that font
209 start = document.GetLine(1);
210 end = document.GetLine(document.Lines);
211 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, null, null, FormatSpecified.Font);
216 public override Color ForeColor {
218 return base.ForeColor;
222 base.ForeColor = value;
226 [DefaultValue(Int32.MaxValue)]
227 public override int MaxLength {
229 return base.max_length;
233 base.max_length = value;
238 public override bool Multiline {
240 return base.Multiline;
244 base.Multiline = value;
249 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
251 public string RedoActionName {
253 return document.undo.RedoActionName;
259 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
260 public int RightMargin {
266 margin_right = value;
271 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
273 [RefreshProperties (RefreshProperties.All)]
282 start_line = document.GetLine(1);
283 end_line = document.GetLine(document.Lines);
284 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
291 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
293 InsertRTFFromStream(data, 0, 1);
301 [DefaultValue(RichTextBoxScrollBars.Both)]
303 public RichTextBoxScrollBars ScrollBars {
315 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
316 public string SelectedRtf {
318 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
330 if (document.selection_visible) {
331 document.ReplaceSelection("", false);
334 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
336 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
337 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
340 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
341 document.SetSelection(line, sel_start);
342 document.PositionCaret(line, sel_start);
343 document.DisplayCaret();
345 OnTextChanged(EventArgs.Empty);
351 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
352 public override string SelectedText {
354 return base.SelectedText;
358 base.SelectedText = value;
363 [DefaultValue(HorizontalAlignment.Left)]
364 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
365 public HorizontalAlignment SelectionAlignment {
367 HorizontalAlignment align;
372 start = document.ParagraphStart(document.selection_start.line);
373 align = start.alignment;
375 end = document.ParagraphEnd(document.selection_end.line);
380 if (line.alignment != align) {
381 return HorizontalAlignment.Left;
387 line = document.GetLine(line.line_no + 1);
398 start = document.ParagraphStart(document.selection_start.line);
400 end = document.ParagraphEnd(document.selection_end.line);
405 line.alignment = value;
410 line = document.GetLine(line.line_no + 1);
412 this.CalculateDocument();
417 [DefaultValue(false)]
418 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
420 public bool SelectionBullet {
431 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
433 public int SelectionCharOffset {
443 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
444 public Color SelectionColor {
451 start = document.selection_start.line.FindTag (document.selection_start.pos);
452 end = document.selection_start.line.FindTag (document.selection_end.pos);
453 color = start.color.Color;
456 while (tag != null && tag != end) {
458 if (!color.Equals (tag.color.Color))
461 tag = document.NextTag (tag);
468 FontDefinition attributes;
472 attributes = new FontDefinition();
473 attributes.color = value;
475 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
476 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
478 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
480 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
481 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
483 document.UpdateView(document.selection_start.line, 0);
484 document.AlignCaret();
489 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
490 public Font SelectionFont {
497 start = document.selection_start.line.FindTag (document.selection_start.pos);
498 end = document.selection_start.line.FindTag (document.selection_end.pos);
501 if (selection_length > 1) {
503 while (tag != null && tag != end) {
505 if (!font.Equals(tag.font))
508 tag = document.NextTag (tag);
516 FontDefinition attributes;
520 attributes = new FontDefinition();
521 attributes.font_obj = value;
523 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
524 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
526 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
528 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
529 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
531 document.UpdateView(document.selection_start.line, 0);
532 document.AlignCaret();
539 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
541 public int SelectionHangingIndent {
552 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
554 public int SelectionIndent {
564 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
565 public override int SelectionLength {
567 return base.SelectionLength;
571 base.SelectionLength = value;
576 [DefaultValue(false)]
577 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
579 public bool SelectionProtected {
590 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
592 public int SelectionRightIndent {
602 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
604 public int[] SelectionTabs {
614 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
615 public RichTextBoxSelectionTypes SelectionType {
617 if (document.selection_start == document.selection_end) {
618 return RichTextBoxSelectionTypes.Empty;
622 if (SelectedText.Length > 1) {
623 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
626 return RichTextBoxSelectionTypes.Text;
630 [DefaultValue(false)]
632 public bool ShowSelectionMargin {
643 [RefreshProperties (RefreshProperties.All)]
645 public override string Text {
656 public override int TextLength {
658 return base.TextLength;
663 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
664 public string UndoActionName {
666 return document.undo.UndoActionName;
672 public float ZoomFactor {
681 #endregion // Public Instance Properties
683 #region Protected Instance Properties
684 protected override CreateParams CreateParams {
686 return base.CreateParams;
690 protected override Size DefaultSize {
692 return new Size(100, 96);
695 #endregion // Protected Instance Properties
697 #region Public Instance Methods
698 public bool CanPaste(DataFormats.Format clipFormat) {
699 if ((clipFormat.Name == DataFormats.Rtf) ||
700 (clipFormat.Name == DataFormats.Text) ||
701 (clipFormat.Name == DataFormats.UnicodeText)) {
707 public int Find(char[] characterSet) {
708 return Find(characterSet, -1, -1);
711 public int Find(char[] characterSet, int start) {
712 return Find(characterSet, start, -1);
715 public int Find(char[] characterSet, int start, int end) {
716 Document.Marker start_mark;
717 Document.Marker end_mark;
718 Document.Marker result;
721 document.GetMarker(out start_mark, true);
727 start_mark = new Document.Marker();
729 document.CharIndexToLineTag(start, out line, out tag, out pos);
730 start_mark.line = line;
731 start_mark.tag = tag;
732 start_mark.pos = pos;
736 document.GetMarker(out end_mark, false);
742 end_mark = new Document.Marker();
744 document.CharIndexToLineTag(end, out line, out tag, out pos);
745 end_mark.line = line;
750 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
751 return document.LineTagToCharIndex(result.line, result.pos);
757 public int Find(string str) {
758 return Find(str, -1, -1, RichTextBoxFinds.None);
761 public int Find(string str, int start, int end, RichTextBoxFinds options) {
762 Document.Marker start_mark;
763 Document.Marker end_mark;
764 Document.Marker result;
767 document.GetMarker(out start_mark, true);
773 start_mark = new Document.Marker();
775 document.CharIndexToLineTag(start, out line, out tag, out pos);
777 start_mark.line = line;
778 start_mark.tag = tag;
779 start_mark.pos = pos;
783 document.GetMarker(out end_mark, false);
789 end_mark = new Document.Marker();
791 document.CharIndexToLineTag(end, out line, out tag, out pos);
793 end_mark.line = line;
798 if (document.Find(str, start_mark, end_mark, out result, options)) {
799 return document.LineTagToCharIndex(result.line, result.pos);
805 public int Find(string str, int start, RichTextBoxFinds options) {
806 return Find(str, start, -1, options);
809 public int Find(string str, RichTextBoxFinds options) {
810 return Find(str, -1, -1, options);
813 public char GetCharFromPosition(Point pt) {
817 PointToTagPos(pt, out tag, out pos);
819 if (pos >= tag.line.text.Length) {
823 return tag.line.text[pos];
827 public int GetCharIndexFromPosition(Point pt) {
831 PointToTagPos(pt, out tag, out pos);
833 return document.LineTagToCharIndex(tag.line, pos);
836 public int GetLineFromCharIndex(int index) {
841 document.CharIndexToLineTag(index, out line, out tag, out pos);
843 return line.LineNo - 1;
846 public Point GetPositionFromCharIndex(int index) {
851 document.CharIndexToLineTag(index, out line, out tag, out pos);
853 return new Point((int)line.widths[pos] + 1, line.Y + 1);
856 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
860 // FIXME - ignoring unicode
861 if (fileType == RichTextBoxStreamType.PlainText) {
867 sb = new StringBuilder((int)data.Length);
868 buffer = new byte[1024];
870 throw new IOException("Not enough memory to load document");
874 while (count < data.Length) {
875 count += data.Read(buffer, count, 1024);
878 base.Text = sb.ToString();
882 InsertRTFFromStream(data, 0, 1);
884 document.PositionCaret (document.GetLine (1), 0);
885 document.SetSelectionToCaret (true);
889 [MonoTODO("Make smarter RTF detection?")]
890 public void LoadFile(string path) {
891 if (path.EndsWith(".rtf")) {
892 LoadFile(path, RichTextBoxStreamType.RichText);
894 LoadFile(path, RichTextBoxStreamType.PlainText);
898 public void LoadFile(string path, RichTextBoxStreamType fileType) {
905 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
907 LoadFile(data, fileType);
911 throw new IOException("Could not open file " + path);
921 public void Paste(DataFormats.Format clipFormat) {
922 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
927 document.undo.Redo ();
930 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
936 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
937 encoding = Encoding.Unicode;
939 encoding = Encoding.ASCII;
943 case RichTextBoxStreamType.PlainText:
944 case RichTextBoxStreamType.TextTextOleObjs:
945 case RichTextBoxStreamType.UnicodePlainText: {
947 bytes = encoding.GetBytes(document.Root.text.ToString());
948 data.Write(bytes, 0, bytes.Length);
952 for (i = 1; i < document.Lines; i++) {
953 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
954 data.Write(bytes, 0, bytes.Length);
956 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
957 data.Write(bytes, 0, bytes.Length);
962 // If we're here we're saving RTF
969 start_line = document.GetLine(1);
970 end_line = document.GetLine(document.Lines);
971 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
973 bytes = new Byte[4096];
975 // Let's chunk it so we don't use up all memory...
976 for (i = 0; i < total; i += 1024) {
977 if ((i + 1024) < total) {
978 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
981 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
983 data.Write(bytes, 0, current);
987 public void SaveFile(string path) {
988 if (path.EndsWith(".rtf")) {
989 SaveFile(path, RichTextBoxStreamType.RichText);
991 SaveFile(path, RichTextBoxStreamType.PlainText);
995 public void SaveFile(string path, RichTextBoxStreamType fileType) {
1001 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
1002 SaveFile(data, fileType);
1006 // throw new IOException("Could not write document to file " + path);
1016 #endregion // Public Instance Methods
1018 #region Protected Instance Methods
1019 protected virtual object CreateRichEditOleCallback() {
1020 throw new NotImplementedException();
1023 protected override void OnBackColorChanged(EventArgs e) {
1024 base.OnBackColorChanged (e);
1027 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1028 ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
1033 protected override void OnContextMenuChanged(EventArgs e) {
1034 base.OnContextMenuChanged (e);
1037 protected override void OnHandleCreated(EventArgs e) {
1038 base.OnHandleCreated (e);
1041 protected override void OnHandleDestroyed(EventArgs e) {
1042 base.OnHandleDestroyed (e);
1045 protected virtual void OnHScroll(EventArgs e) {
1046 EventHandler eh = (EventHandler)(Events [HScrollEvent]);
1051 [MonoTODO("Determine when to call this")]
1052 protected virtual void OnImeChange(EventArgs e) {
1053 EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
1058 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1059 LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
1064 protected virtual void OnProtected(EventArgs e) {
1065 EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
1070 protected override void OnRightToLeftChanged(EventArgs e) {
1071 base.OnRightToLeftChanged (e);
1074 protected virtual void OnSelectionChanged(EventArgs e) {
1075 EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
1080 protected override void OnSystemColorsChanged(EventArgs e) {
1081 base.OnSystemColorsChanged (e);
1084 protected override void OnTextChanged(EventArgs e) {
1085 base.OnTextChanged (e);
1088 protected virtual void OnVScroll(EventArgs e) {
1089 EventHandler eh = (EventHandler)(Events [VScrollEvent]);
1094 protected override void WndProc(ref Message m) {
1095 base.WndProc (ref m);
1097 #endregion // Protected Instance Methods
1100 static object ContentsResizedEvent = new object ();
1101 static object HScrollEvent = new object ();
1102 static object ImeChangeEvent = new object ();
1103 static object LinkClickedEvent = new object ();
1104 static object ProtectedEvent = new object ();
1105 static object SelectionChangedEvent = new object ();
1106 static object VScrollEvent = new object ();
1109 [EditorBrowsable(EditorBrowsableState.Never)]
1110 public new event EventHandler BackgroundImageChanged {
1111 add { base.BackgroundImageChanged += value; }
1112 remove { base.BackgroundImageChanged -= value; }
1115 public event ContentsResizedEventHandler ContentsResized {
1116 add { Events.AddHandler (ContentsResizedEvent, value); }
1117 remove { Events.RemoveHandler (ContentsResizedEvent, value); }
1121 [EditorBrowsable(EditorBrowsableState.Never)]
1122 public new event EventHandler DoubleClick {
1123 add { base.DoubleClick += value; }
1124 remove { base.DoubleClick -= value; }
1129 [EditorBrowsable(EditorBrowsableState.Never)]
1131 public new event DragEventHandler DragDrop {
1132 add { base.DragDrop += value; }
1133 remove { base.DragDrop -= value; }
1138 [EditorBrowsable(EditorBrowsableState.Never)]
1140 public new event DragEventHandler DragEnter {
1141 add { base.DragEnter += value; }
1142 remove { base.DragEnter -= value; }
1146 [EditorBrowsable(EditorBrowsableState.Never)]
1147 public new event EventHandler DragLeave {
1148 add { base.DragLeave += value; }
1149 remove { base.DragLeave -= value; }
1154 [EditorBrowsable(EditorBrowsableState.Never)]
1155 public new event DragEventHandler DragOver {
1156 add { base.DragOver += value; }
1157 remove { base.DragOver -= value; }
1162 [EditorBrowsable(EditorBrowsableState.Never)]
1163 public new event GiveFeedbackEventHandler GiveFeedback {
1164 add { base.GiveFeedback += value; }
1165 remove { base.GiveFeedback -= value; }
1168 public event EventHandler HScroll {
1169 add { Events.AddHandler (HScrollEvent, value); }
1170 remove { Events.RemoveHandler (HScrollEvent, value); }
1173 public event EventHandler ImeChange {
1174 add { Events.AddHandler (ImeChangeEvent, value); }
1175 remove { Events.RemoveHandler (ImeChangeEvent, value); }
1178 public event LinkClickedEventHandler LinkClicked {
1179 add { Events.AddHandler (LinkClickedEvent, value); }
1180 remove { Events.RemoveHandler (LinkClickedEvent, value); }
1183 public event EventHandler Protected {
1184 add { Events.AddHandler (ProtectedEvent, value); }
1185 remove { Events.RemoveHandler (ProtectedEvent, value); }
1189 [EditorBrowsable(EditorBrowsableState.Never)]
1190 public new event QueryContinueDragEventHandler QueryContinueDrag {
1191 add { base.QueryContinueDrag += value; }
1192 remove { base.QueryContinueDrag -= value; }
1195 public event EventHandler SelectionChanged {
1196 add { Events.AddHandler (SelectionChangedEvent, value); }
1197 remove { Events.RemoveHandler (SelectionChangedEvent, value); }
1200 public event EventHandler VScroll {
1201 add { Events.AddHandler (VScrollEvent, value); }
1202 remove { Events.RemoveHandler (VScrollEvent, value); }
1204 #endregion // Events
1206 #region Private Methods
1208 internal override void SelectWord ()
1210 document.ExpandSelection(CaretSelection.Word, false);
1213 private void HandleControl(RTF.RTF rtf) {
1215 case RTF.Major.Unicode: {
1217 case Minor.UnicodeCharBytes: {
1218 rtf_skip_width = rtf.Param;
1222 case Minor.UnicodeChar: {
1223 rtf_skip_count += rtf_skip_width;
1224 rtf_line.Append((char)rtf.Param);
1231 case RTF.Major.Destination: {
1232 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1237 case RTF.Major.PictAttr:
1238 if (rtf.Picture != null && rtf.Picture.IsValid ()) {
1239 Line line = document.GetLine (rtf_cursor_y);
1240 document.InsertPicture (line, 0, rtf.Picture);
1243 FlushText (rtf, true);
1248 case RTF.Major.CharAttr: {
1250 case Minor.ForeColor: {
1251 System.Windows.Forms.RTF.Color color;
1253 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1255 if (color != null) {
1256 FlushText(rtf, false);
1257 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1258 this.rtf_color = new SolidBrush(ForeColor);
1260 this.rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1262 FlushText (rtf, false);
1267 case Minor.FontSize: {
1268 FlushText(rtf, false);
1269 this.rtf_rtffont_size = rtf.Param / 2;
1273 case Minor.FontNum: {
1274 System.Windows.Forms.RTF.Font font;
1276 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1278 FlushText(rtf, false);
1279 this.rtf_rtffont = font;
1285 FlushText(rtf, false);
1286 rtf_rtfstyle = FontStyle.Regular;
1291 FlushText(rtf, false);
1292 if (rtf.Param == RTF.RTF.NoParam) {
1293 rtf_rtfstyle |= FontStyle.Bold;
1295 rtf_rtfstyle &= ~FontStyle.Bold;
1300 case Minor.Italic: {
1301 FlushText(rtf, false);
1302 if (rtf.Param == RTF.RTF.NoParam) {
1303 rtf_rtfstyle |= FontStyle.Italic;
1305 rtf_rtfstyle &= ~FontStyle.Italic;
1310 case Minor.StrikeThru: {
1311 FlushText(rtf, false);
1312 if (rtf.Param == RTF.RTF.NoParam) {
1313 rtf_rtfstyle |= FontStyle.Strikeout;
1315 rtf_rtfstyle &= ~FontStyle.Strikeout;
1320 case Minor.Underline: {
1321 FlushText(rtf, false);
1322 if (rtf.Param == RTF.RTF.NoParam) {
1323 rtf_rtfstyle |= FontStyle.Underline;
1325 rtf_rtfstyle = rtf_rtfstyle & ~FontStyle.Underline;
1330 case Minor.NoUnderline: {
1331 FlushText(rtf, false);
1332 rtf_rtfstyle &= ~FontStyle.Underline;
1339 case RTF.Major.ParAttr: {
1340 switch (rtf.Minor) {
1341 case Minor.LeftIndent:
1342 rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
1348 case RTF.Major.SpecialChar: {
1349 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1356 private void SpecialChar(RTF.RTF rtf) {
1363 FlushText(rtf, true);
1372 case Minor.NoBrkSpace: {
1378 rtf_line.Append ("\t");
1379 // FlushText (rtf, false);
1383 case Minor.NoReqHyphen:
1384 case Minor.NoBrkHyphen: {
1385 rtf_line.Append ("-");
1386 // FlushText (rtf, false);
1390 case Minor.Bullet: {
1391 Console.WriteLine("*");
1395 case Minor.WidowCtrl:
1398 case Minor.EmDash: {
1399 rtf_line.Append ("\u2014");
1403 case Minor.EnDash: {
1404 rtf_line.Append ("\u2013");
1408 case Minor.LQuote: {
1409 Console.Write("\u2018");
1413 case Minor.RQuote: {
1414 Console.Write("\u2019");
1418 case Minor.LDblQuote: {
1419 Console.Write("\u201C");
1423 case Minor.RDblQuote: {
1424 Console.Write("\u201D");
1429 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1436 private void HandleText(RTF.RTF rtf) {
1437 if (rtf_skip_count > 0) {
1443 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1444 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1446 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1447 rtf_line.Append((char)rtf.Major);
1449 //rtf_line.Append((char)rtf.Major);
1450 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1454 rtf_line.Append (rtf.EncodedText);
1457 private void FlushText(RTF.RTF rtf, bool newline) {
1461 length = rtf_line.Length;
1462 if (!newline && (length == 0)) {
1466 if (rtf_rtffont == null) {
1467 // First font in table is default
1468 rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont(rtf, 0);
1471 font = new Font(rtf_rtffont.Name, rtf_rtffont_size, rtf_rtfstyle);
1473 if (rtf_color == null) {
1474 System.Windows.Forms.RTF.Color color;
1476 // First color in table is default
1477 color = System.Windows.Forms.RTF.Color.GetColor(rtf, 0);
1479 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1480 rtf_color = new SolidBrush(ForeColor);
1482 rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1487 rtf_chars += rtf_line.Length;
1489 if (rtf_cursor_x == 0) {
1490 document.Add(rtf_cursor_y, rtf_line.ToString(), rtf_rtfalign, font, rtf_color, LineEnding.Wrap);
1491 if (rtf_par_line_left_indent != 0) {
1492 Line line = document.GetLine (rtf_cursor_y);
1493 line.indent = rtf_par_line_left_indent;
1498 line = document.GetLine(rtf_cursor_y);
1499 line.indent = rtf_par_line_left_indent;
1500 if (rtf_line.Length > 0) {
1501 document.InsertString(line, rtf_cursor_x, rtf_line.ToString());
1502 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
1505 document.Split(line, rtf_cursor_x + length);
1513 rtf_cursor_x += length;
1515 rtf_line.Length = 0; // Empty line
1518 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1523 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1526 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1529 rtf = new RTF.RTF(data);
1532 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1533 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1537 rtf_line = new StringBuilder();
1539 rtf_rtffont_size = (int)this.Font.Size;
1540 rtf_rtfalign = HorizontalAlignment.Left;
1541 rtf_rtfstyle = FontStyle.Regular;
1543 rtf_cursor_x = cursor_x;
1544 rtf_cursor_y = cursor_y;
1546 rtf.DefaultFont(this.Font.Name);
1548 rtf_text_map = new RTF.TextMap();
1549 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1551 document.SuspendRecalc ();
1554 rtf.Read(); // That's it
1555 FlushText(rtf, false);
1560 catch (RTF.RTFException e) {
1564 // Seems to be plain text or broken RTF
1565 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1568 to_x = rtf_cursor_x;
1569 to_y = rtf_cursor_y;
1572 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1573 document.ResumeRecalc (true);
1575 document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1578 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1582 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1586 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1591 if (p.X >= document.ViewPortWidth) {
1592 p.X = document.ViewPortWidth - 1;
1593 } else if (p.X < 0) {
1597 if (p.Y >= document.ViewPortHeight) {
1598 p.Y = document.ViewPortHeight - 1;
1599 } else if (p.Y < 0) {
1603 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1606 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1607 if (prev_index != font_index) {
1608 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1611 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1612 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1615 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1619 if (prev_font != null) {
1625 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1629 if (prev_font != null) {
1635 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1636 if (font.Strikeout) {
1637 rtf.Append("\\strike");
1639 if (prev_font != null) {
1640 rtf.Append("\\strike0");
1645 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1646 if (font.Underline) {
1649 if (prev_font != null) {
1650 rtf.Append("\\ul0");
1656 [MonoTODO("Emit unicode and other special characters properly")]
1657 private void EmitRTFText(StringBuilder rtf, string text) {
1661 // start_pos and end_pos are 0-based
1662 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1676 sb = new StringBuilder();
1677 fonts = new ArrayList(10);
1678 colors = new ArrayList(10);
1680 // Two runs, first we parse to determine tables;
1681 // and unlike most of our processing here we work on tags
1684 line_no = start_line.line_no;
1687 // Add default font and color; to optimize document content we don't
1688 // use this.Font and this.ForeColor but the font/color from the first tag
1689 tag = LineTag.FindTag(start_line, pos);
1691 color = ((SolidBrush)tag.color).Color;
1692 fonts.Add(font.Name);
1695 while (line_no <= end_line.line_no) {
1696 line = document.GetLine(line_no);
1697 tag = LineTag.FindTag(line, pos);
1699 if (line_no != end_line.line_no) {
1700 line_len = line.text.Length;
1705 while (pos < line_len) {
1706 if (tag.font.Name != font.Name) {
1708 if (!fonts.Contains(font.Name)) {
1709 fonts.Add(font.Name);
1713 if (((SolidBrush)tag.color).Color != color) {
1714 color = ((SolidBrush)tag.color).Color;
1715 if (!colors.Contains(color)) {
1720 pos = tag.start + tag.length - 1;
1727 // We have the tables, emit the header
1728 sb.Append("{\\rtf1\\ansi");
1729 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
1732 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1735 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
1737 // Emit the font table
1738 sb.Append("{\\fonttbl");
1739 for (i = 0; i < fonts.Count; i++) {
1740 sb.Append(String.Format("{{\\f{0}", i)); // {Font
1741 sb.Append("\\fnil"); // Family
1742 sb.Append("\\fcharset0 "); // Charset ANSI<space>
1743 sb.Append((string)fonts[i]); // Font name
1744 sb.Append(";}"); // }
1748 // Emit the color table (if needed)
1749 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))) {
1750 sb.Append("{\\colortbl "); // Header and NO! default color
1751 for (i = 0; i < colors.Count; i++) {
1752 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
1753 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
1754 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
1760 sb.Append("{\\*\\generator Mono RichTextBox;}");
1761 // Emit initial paragraph settings
1762 tag = LineTag.FindTag(start_line, start_pos);
1763 sb.Append("\\pard"); // Reset to default paragraph properties
1764 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.font.Name), null, tag.font); // Font properties
1765 sb.Append(" "); // Space separator
1768 color = (Color)colors[0];
1770 line_no = start_line.line_no;
1773 while (line_no <= end_line.line_no) {
1774 line = document.GetLine(line_no);
1775 tag = LineTag.FindTag(line, pos);
1777 if (line_no != end_line.line_no) {
1778 line_len = line.text.Length;
1783 while (pos < line_len) {
1786 if (tag.font != font) {
1787 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.font.Name), font, tag.font);
1791 if (((SolidBrush)tag.color).Color != color) {
1792 color = ((SolidBrush)tag.color).Color;
1793 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
1795 if (length != sb.Length) {
1796 sb.Append(" "); // Emit space to separate keywords from text
1799 // Emit the string itself
1800 if (line_no != end_line.line_no) {
1801 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1803 if (end_pos < (tag.start + tag.length - 1)) {
1804 // Emit partial tag only, end_pos is inside this tag
1805 EmitRTFText(sb, tag.line.text.ToString(pos, end_pos - pos));
1807 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1811 pos = tag.start + tag.length - 1;
1814 if (pos >= line.text.Length) {
1815 if (line.ending != LineEnding.Wrap) {
1816 sb.Append("\\par\n");
1827 #endregion // Private Methods