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)]
44 [Designer ("System.Windows.Forms.Design.RichTextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
46 public class RichTextBox : TextBoxBase {
47 #region Local Variables
48 internal bool auto_word_select;
49 internal int bullet_indent;
50 internal bool detect_urls;
51 internal int margin_right;
53 private StringBuilder rtf_line;
55 private RtfSectionStyle rtf_style; // Replaces individual style
56 // properties so we can revert
57 private Stack rtf_section_stack;
59 private RTF.TextMap rtf_text_map;
60 private int rtf_skip_width;
61 private int rtf_skip_count;
62 private int rtf_cursor_x;
63 private int rtf_cursor_y;
64 private int rtf_chars;
67 private bool enable_auto_drag_drop;
68 private RichTextBoxLanguageOptions language_option;
69 private bool rich_text_shortcuts_enabled;
70 private Color selection_back_color;
72 #endregion // Local Variables
74 #region Public Constructors
75 public RichTextBox() {
76 accepts_return = true;
77 auto_word_select = false;
79 max_length = Int32.MaxValue;
82 base.Multiline = true;
83 document.CRLFSize = 1;
84 shortcuts_enabled = true;
85 disabled_foreground_grey = false;
86 base.EnableLinks = true;
88 rtf_style = new RtfSectionStyle ();
89 rtf_section_stack = null;
91 scrollbars = RichTextBoxScrollBars.Both;
92 alignment = HorizontalAlignment.Left;
93 LostFocus += new EventHandler(RichTextBox_LostFocus);
94 GotFocus += new EventHandler(RichTextBox_GotFocus);
95 BackColor = ThemeEngine.Current.ColorWindow;
97 backcolor_set = false;
98 language_option = RichTextBoxLanguageOptions.AutoFontSizeAdjust;
99 rich_text_shortcuts_enabled = true;
100 selection_back_color = DefaultBackColor;
102 ForeColor = ThemeEngine.Current.ColorWindowText;
104 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
105 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
108 SetStyle (ControlStyles.StandardDoubleClick, false);
111 #endregion // Public Constructors
113 #region Private & Internal Methods
115 internal override void HandleLinkClicked (LinkRectangle link)
117 OnLinkClicked (new LinkClickedEventArgs (link.LinkTag.LinkText));
120 internal override Color ChangeBackColor (Color backColor)
122 if (backColor == Color.Empty) {
124 backcolor_set = false;
126 backColor = SystemColors.Window;
129 backColor = SystemColors.Window;
135 private void RichTextBox_LostFocus(object sender, EventArgs e) {
139 private void RichTextBox_GotFocus(object sender, EventArgs e) {
142 #endregion // Private & Internal Methods
144 #region Public Instance Properties
148 public override bool AllowDrop {
150 return base.AllowDrop;
154 base.AllowDrop = value;
158 [DefaultValue(false)]
160 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
161 [RefreshProperties (RefreshProperties.Repaint)]
162 [EditorBrowsable (EditorBrowsableState.Never)]
167 public override bool AutoSize {
173 base.AutoSize = value;
177 [DefaultValue(false)]
178 public bool AutoWordSelection {
180 return auto_word_select;
184 auto_word_select = true;
189 [EditorBrowsable(EditorBrowsableState.Never)]
190 public override System.Drawing.Image BackgroundImage {
191 get { return base.BackgroundImage; }
192 set { base.BackgroundImage = value; }
197 [EditorBrowsable (EditorBrowsableState.Never)]
198 public override ImageLayout BackgroundImageLayout {
199 get { return base.BackgroundImageLayout; }
200 set { base.BackgroundImageLayout = value; }
206 public int BulletIndent {
208 return bullet_indent;
212 bullet_indent = value;
217 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
218 public bool CanRedo {
220 return document.undo.CanRedo;
225 public bool DetectUrls {
226 get { return base.EnableLinks; }
227 set { base.EnableLinks = value; }
232 [DefaultValue (false)]
233 public bool EnableAutoDragDrop {
234 get { return enable_auto_drag_drop; }
235 set { enable_auto_drag_drop = value; }
239 public override Font Font {
250 if (PreferredHeight != Height) {
251 Height = PreferredHeight;
257 // Font changes always set the whole doc to that font
258 start = document.GetLine(1);
259 end = document.GetLine(document.Lines);
260 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, Color.Empty, Color.Empty, FormatSpecified.Font);
265 public override Color ForeColor {
267 return base.ForeColor;
271 base.ForeColor = value;
278 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
279 public RichTextBoxLanguageOptions LanguageOption {
280 get { return language_option; }
281 set { language_option = value; }
285 [DefaultValue(Int32.MaxValue)]
286 public override int MaxLength {
288 return base.max_length;
292 base.max_length = value;
297 public override bool Multiline {
299 return base.Multiline;
303 base.Multiline = value;
308 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
310 public string RedoActionName {
312 return document.undo.RedoActionName;
319 [DefaultValue (true)]
320 [EditorBrowsable (EditorBrowsableState.Never)]
321 public bool RichTextShortcutsEnabled {
322 get { return rich_text_shortcuts_enabled; }
323 set { rich_text_shortcuts_enabled = value; }
329 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
330 public int RightMargin {
336 margin_right = value;
341 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
343 [RefreshProperties (RefreshProperties.All)]
352 start_line = document.GetLine(1);
353 end_line = document.GetLine(document.Lines);
354 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
361 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
363 InsertRTFFromStream(data, 0, 1);
371 [DefaultValue(RichTextBoxScrollBars.Both)]
373 public RichTextBoxScrollBars ScrollBars {
379 if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
380 throw new InvalidEnumArgumentException ("value", (int) value,
381 typeof (RichTextBoxScrollBars));
383 if (value != scrollbars) {
385 CalculateDocument ();
392 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
393 public string SelectedRtf {
395 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
407 if (document.selection_visible) {
408 document.ReplaceSelection("", false);
411 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
413 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
414 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
417 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
418 document.SetSelection(line, sel_start);
419 document.PositionCaret(line, sel_start);
420 document.DisplayCaret();
422 OnTextChanged(EventArgs.Empty);
428 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
429 public override string SelectedText {
431 return base.SelectedText;
435 base.SelectedText = value;
440 [DefaultValue(HorizontalAlignment.Left)]
441 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
442 public HorizontalAlignment SelectionAlignment {
444 HorizontalAlignment align;
449 start = document.ParagraphStart(document.selection_start.line);
450 align = start.alignment;
452 end = document.ParagraphEnd(document.selection_end.line);
457 if (line.alignment != align) {
458 return HorizontalAlignment.Left;
464 line = document.GetLine(line.line_no + 1);
475 start = document.ParagraphStart(document.selection_start.line);
477 end = document.ParagraphEnd(document.selection_end.line);
482 line.alignment = value;
487 line = document.GetLine(line.line_no + 1);
489 this.CalculateDocument();
496 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
497 public Color SelectionBackColor {
498 get { return selection_back_color; }
499 set { selection_back_color = value; }
504 [DefaultValue(false)]
505 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
507 public bool SelectionBullet {
518 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
520 public int SelectionCharOffset {
530 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
531 public Color SelectionColor {
538 start = document.selection_start.line.FindTag (document.selection_start.pos);
539 end = document.selection_start.line.FindTag (document.selection_end.pos);
543 while (tag != null && tag != end) {
545 if (!color.Equals (tag.Color))
548 tag = document.NextTag (tag);
555 if (value == Color.Empty)
556 value = DefaultForeColor;
561 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
562 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
564 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
565 document.selection_end.line, document.selection_end.pos + 1, null,
566 value, Color.Empty, FormatSpecified.Color);
568 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
569 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
571 document.UpdateView(document.selection_start.line, 0);
572 document.AlignCaret();
577 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
578 public Font SelectionFont {
585 start = document.selection_start.line.FindTag (document.selection_start.pos);
586 end = document.selection_start.line.FindTag (document.selection_end.pos);
589 if (selection_length > 1) {
591 while (tag != null && tag != end) {
593 if (!font.Equals(tag.Font))
596 tag = document.NextTag (tag);
607 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
608 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
610 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
611 document.selection_end.line, document.selection_end.pos + 1, value,
612 Color.Empty, Color.Empty, FormatSpecified.Font);
614 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
615 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
617 document.UpdateView(document.selection_start.line, 0);
618 document.AlignCaret();
625 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
627 public int SelectionHangingIndent {
638 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
640 public int SelectionIndent {
650 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
651 public override int SelectionLength {
653 return base.SelectionLength;
657 base.SelectionLength = value;
662 [DefaultValue(false)]
663 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
665 public bool SelectionProtected {
676 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
678 public int SelectionRightIndent {
688 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
690 public int[] SelectionTabs {
700 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
701 public RichTextBoxSelectionTypes SelectionType {
703 if (document.selection_start == document.selection_end) {
704 return RichTextBoxSelectionTypes.Empty;
708 if (SelectedText.Length > 1) {
709 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
712 return RichTextBoxSelectionTypes.Text;
716 [DefaultValue(false)]
718 public bool ShowSelectionMargin {
729 [RefreshProperties (RefreshProperties.All)]
731 public override string Text {
742 public override int TextLength {
744 return base.TextLength;
749 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
750 public string UndoActionName {
752 return document.undo.UndoActionName;
758 public float ZoomFactor {
767 #endregion // Public Instance Properties
769 #region Protected Instance Properties
770 protected override CreateParams CreateParams {
772 return base.CreateParams;
776 protected override Size DefaultSize {
778 return new Size(100, 96);
781 #endregion // Protected Instance Properties
783 #region Public Instance Methods
784 public bool CanPaste(DataFormats.Format clipFormat) {
785 if ((clipFormat.Name == DataFormats.Rtf) ||
786 (clipFormat.Name == DataFormats.Text) ||
787 (clipFormat.Name == DataFormats.UnicodeText)) {
793 public int Find(char[] characterSet) {
794 return Find(characterSet, -1, -1);
797 public int Find(char[] characterSet, int start) {
798 return Find(characterSet, start, -1);
801 public int Find(char[] characterSet, int start, int end) {
802 Document.Marker start_mark;
803 Document.Marker end_mark;
804 Document.Marker result;
807 document.GetMarker(out start_mark, true);
813 start_mark = new Document.Marker();
815 document.CharIndexToLineTag(start, out line, out tag, out pos);
816 start_mark.line = line;
817 start_mark.tag = tag;
818 start_mark.pos = pos;
822 document.GetMarker(out end_mark, false);
828 end_mark = new Document.Marker();
830 document.CharIndexToLineTag(end, out line, out tag, out pos);
831 end_mark.line = line;
836 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
837 return document.LineTagToCharIndex(result.line, result.pos);
843 public int Find(string str) {
844 return Find(str, -1, -1, RichTextBoxFinds.None);
847 public int Find(string str, int start, int end, RichTextBoxFinds options) {
848 Document.Marker start_mark;
849 Document.Marker end_mark;
850 Document.Marker result;
853 document.GetMarker(out start_mark, true);
859 start_mark = new Document.Marker();
861 document.CharIndexToLineTag(start, out line, out tag, out pos);
863 start_mark.line = line;
864 start_mark.tag = tag;
865 start_mark.pos = pos;
869 document.GetMarker(out end_mark, false);
875 end_mark = new Document.Marker();
877 document.CharIndexToLineTag(end, out line, out tag, out pos);
879 end_mark.line = line;
884 if (document.Find(str, start_mark, end_mark, out result, options)) {
885 return document.LineTagToCharIndex(result.line, result.pos);
891 public int Find(string str, int start, RichTextBoxFinds options) {
892 return Find(str, start, -1, options);
895 public int Find(string str, RichTextBoxFinds options) {
896 return Find(str, -1, -1, options);
901 public char GetCharFromPosition(Point pt) {
905 PointToTagPos(pt, out tag, out pos);
907 if (pos >= tag.Line.text.Length) {
911 return tag.Line.text[pos];
914 internal override char GetCharFromPositionInternal (Point p)
919 PointToTagPos (p, out tag, out pos);
921 if (pos >= tag.Line.text.Length)
924 return tag.Line.text[pos];
932 int GetCharIndexFromPosition(Point pt) {
936 PointToTagPos(pt, out tag, out pos);
938 return document.LineTagToCharIndex(tag.Line, pos);
945 int GetLineFromCharIndex(int index) {
950 document.CharIndexToLineTag(index, out line, out tag, out pos);
952 return line.LineNo - 1;
959 Point GetPositionFromCharIndex(int index) {
964 document.CharIndexToLineTag(index, out line, out tag, out pos);
966 return new Point((int)line.widths[pos] + 1, line.Y + 1);
969 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
973 // FIXME - ignoring unicode
974 if (fileType == RichTextBoxStreamType.PlainText) {
979 sb = new StringBuilder ((int) data.Length);
980 buffer = new char [1024];
982 throw new IOException("Not enough memory to load document");
985 StreamReader sr = new StreamReader (data, Encoding.Default, true);
986 int charsRead = sr.Read (buffer, 0, buffer.Length);
987 while (charsRead > 0) {
988 sb.Append (buffer, 0, charsRead);
989 charsRead = sr.Read (buffer, 0, buffer.Length);
991 base.Text = sb.ToString();
995 InsertRTFFromStream(data, 0, 1);
997 document.PositionCaret (document.GetLine (1), 0);
998 document.SetSelectionToCaret (true);
1002 [MonoTODO("Make smarter RTF detection?")]
1003 public void LoadFile(string path) {
1004 if (path.EndsWith(".rtf")) {
1005 LoadFile(path, RichTextBoxStreamType.RichText);
1007 LoadFile(path, RichTextBoxStreamType.PlainText);
1011 public void LoadFile(string path, RichTextBoxStreamType fileType) {
1018 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
1020 LoadFile(data, fileType);
1023 catch (Exception ex) {
1024 throw new IOException("Could not open file " + path, ex);
1034 public void Paste(DataFormats.Format clipFormat) {
1035 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
1040 document.undo.Redo ();
1043 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
1049 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
1050 encoding = Encoding.Unicode;
1052 encoding = Encoding.ASCII;
1056 case RichTextBoxStreamType.PlainText:
1057 case RichTextBoxStreamType.TextTextOleObjs:
1058 case RichTextBoxStreamType.UnicodePlainText: {
1060 bytes = encoding.GetBytes(document.Root.text.ToString());
1061 data.Write(bytes, 0, bytes.Length);
1065 for (i = 1; i < document.Lines; i++) {
1066 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
1067 data.Write(bytes, 0, bytes.Length);
1069 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
1070 data.Write(bytes, 0, bytes.Length);
1075 // If we're here we're saving RTF
1082 start_line = document.GetLine(1);
1083 end_line = document.GetLine(document.Lines);
1084 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
1086 bytes = new Byte[4096];
1088 // Let's chunk it so we don't use up all memory...
1089 for (i = 0; i < total; i += 1024) {
1090 if ((i + 1024) < total) {
1091 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
1093 current = total - i;
1094 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
1096 data.Write(bytes, 0, current);
1100 public void SaveFile(string path) {
1101 if (path.EndsWith(".rtf")) {
1102 SaveFile(path, RichTextBoxStreamType.RichText);
1104 SaveFile(path, RichTextBoxStreamType.PlainText);
1108 public void SaveFile(string path, RichTextBoxStreamType fileType) {
1114 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
1115 SaveFile(data, fileType);
1119 // throw new IOException("Could not write document to file " + path);
1130 [EditorBrowsable (EditorBrowsableState.Never)]
1131 public new void DrawToBitmap (Bitmap bitmap, Rectangle clip)
1133 Graphics dc = Graphics.FromImage (bitmap);
1135 if (backcolor_set || (Enabled && !read_only)) {
1136 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), clip);
1138 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControl), clip);
1141 // Draw the viewable document
1142 document.Draw (dc, clip);
1146 #endregion // Public Instance Methods
1148 #region Protected Instance Methods
1149 protected virtual object CreateRichEditOleCallback() {
1150 throw new NotImplementedException();
1153 protected override void OnBackColorChanged(EventArgs e) {
1154 base.OnBackColorChanged (e);
1157 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1158 ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
1163 protected override void OnContextMenuChanged(EventArgs e) {
1164 base.OnContextMenuChanged (e);
1167 protected override void OnHandleCreated(EventArgs e) {
1168 base.OnHandleCreated (e);
1171 protected override void OnHandleDestroyed(EventArgs e) {
1172 base.OnHandleDestroyed (e);
1175 protected virtual void OnHScroll(EventArgs e) {
1176 EventHandler eh = (EventHandler)(Events [HScrollEvent]);
1181 [MonoTODO("Determine when to call this")]
1182 protected virtual void OnImeChange(EventArgs e) {
1183 EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
1188 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1189 LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
1194 protected virtual void OnProtected(EventArgs e) {
1195 EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
1200 protected override void OnRightToLeftChanged(EventArgs e) {
1201 base.OnRightToLeftChanged (e);
1204 protected virtual void OnSelectionChanged(EventArgs e) {
1205 EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
1211 protected override void OnSystemColorsChanged(EventArgs e) {
1212 base.OnSystemColorsChanged (e);
1215 protected override void OnTextChanged(EventArgs e) {
1216 base.OnTextChanged (e);
1220 protected virtual void OnVScroll(EventArgs e) {
1221 EventHandler eh = (EventHandler)(Events [VScrollEvent]);
1226 protected override void WndProc(ref Message m) {
1227 base.WndProc (ref m);
1231 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
1233 return base.ProcessCmdKey (ref msg, keyData);
1236 #endregion // Protected Instance Methods
1239 static object ContentsResizedEvent = new object ();
1240 static object HScrollEvent = new object ();
1241 static object ImeChangeEvent = new object ();
1242 static object LinkClickedEvent = new object ();
1243 static object ProtectedEvent = new object ();
1244 static object SelectionChangedEvent = new object ();
1245 static object VScrollEvent = new object ();
1248 [EditorBrowsable(EditorBrowsableState.Never)]
1249 public new event EventHandler BackgroundImageChanged {
1250 add { base.BackgroundImageChanged += value; }
1251 remove { base.BackgroundImageChanged -= value; }
1256 [EditorBrowsable (EditorBrowsableState.Never)]
1257 public new event EventHandler BackgroundImageLayoutChanged {
1258 add { base.BackgroundImageLayoutChanged += value; }
1259 remove { base.BackgroundImageLayoutChanged -= value; }
1263 public event ContentsResizedEventHandler ContentsResized {
1264 add { Events.AddHandler (ContentsResizedEvent, value); }
1265 remove { Events.RemoveHandler (ContentsResizedEvent, value); }
1270 [EditorBrowsable(EditorBrowsableState.Never)]
1271 public new event EventHandler DoubleClick {
1272 add { base.DoubleClick += value; }
1273 remove { base.DoubleClick -= value; }
1279 [EditorBrowsable(EditorBrowsableState.Never)]
1281 public new event DragEventHandler DragDrop {
1282 add { base.DragDrop += value; }
1283 remove { base.DragDrop -= value; }
1288 [EditorBrowsable(EditorBrowsableState.Never)]
1290 public new event DragEventHandler DragEnter {
1291 add { base.DragEnter += value; }
1292 remove { base.DragEnter -= value; }
1296 [EditorBrowsable(EditorBrowsableState.Never)]
1297 public new event EventHandler DragLeave {
1298 add { base.DragLeave += value; }
1299 remove { base.DragLeave -= value; }
1304 [EditorBrowsable(EditorBrowsableState.Never)]
1305 public new event DragEventHandler DragOver {
1306 add { base.DragOver += value; }
1307 remove { base.DragOver -= value; }
1312 [EditorBrowsable(EditorBrowsableState.Never)]
1313 public new event GiveFeedbackEventHandler GiveFeedback {
1314 add { base.GiveFeedback += value; }
1315 remove { base.GiveFeedback -= value; }
1318 public event EventHandler HScroll {
1319 add { Events.AddHandler (HScrollEvent, value); }
1320 remove { Events.RemoveHandler (HScrollEvent, value); }
1323 public event EventHandler ImeChange {
1324 add { Events.AddHandler (ImeChangeEvent, value); }
1325 remove { Events.RemoveHandler (ImeChangeEvent, value); }
1328 public event LinkClickedEventHandler LinkClicked {
1329 add { Events.AddHandler (LinkClickedEvent, value); }
1330 remove { Events.RemoveHandler (LinkClickedEvent, value); }
1333 public event EventHandler Protected {
1334 add { Events.AddHandler (ProtectedEvent, value); }
1335 remove { Events.RemoveHandler (ProtectedEvent, value); }
1339 [EditorBrowsable(EditorBrowsableState.Never)]
1340 public new event QueryContinueDragEventHandler QueryContinueDrag {
1341 add { base.QueryContinueDrag += value; }
1342 remove { base.QueryContinueDrag -= value; }
1345 public event EventHandler SelectionChanged {
1346 add { Events.AddHandler (SelectionChangedEvent, value); }
1347 remove { Events.RemoveHandler (SelectionChangedEvent, value); }
1350 public event EventHandler VScroll {
1351 add { Events.AddHandler (VScrollEvent, value); }
1352 remove { Events.RemoveHandler (VScrollEvent, value); }
1354 #endregion // Events
1356 #region Private Methods
1358 internal override void SelectWord ()
1360 document.ExpandSelection(CaretSelection.Word, false);
1363 private class RtfSectionStyle : ICloneable {
1364 internal Color rtf_color;
1365 internal RTF.Font rtf_rtffont;
1366 internal int rtf_rtffont_size;
1367 internal FontStyle rtf_rtfstyle;
1368 internal HorizontalAlignment rtf_rtfalign;
1369 internal int rtf_par_line_left_indent;
1371 public object Clone ()
1373 RtfSectionStyle new_style = new RtfSectionStyle ();
1375 new_style.rtf_color = rtf_color;
1376 new_style.rtf_par_line_left_indent = rtf_par_line_left_indent;
1377 new_style.rtf_rtfalign = rtf_rtfalign;
1378 new_style.rtf_rtffont = rtf_rtffont;
1379 new_style.rtf_rtffont_size = rtf_rtffont_size;
1380 new_style.rtf_rtfstyle = rtf_rtfstyle;
1386 // To allow us to keep track of the sections and revert formatting
1387 // as we go in and out of sections of the document.
1388 private void HandleGroup (RTF.RTF rtf)
1390 //start group - save the current formatting on to a stack
1391 //end group - go back to the formatting at the current group
1392 if (rtf_section_stack == null) {
1393 rtf_section_stack = new Stack ();
1396 if (rtf.Major == RTF.Major.BeginGroup) {
1397 rtf_section_stack.Push (rtf_style.Clone ());
1398 } else if (rtf.Major == RTF.Major.EndGroup) {
1399 if (rtf_section_stack.Count > 0) {
1400 FlushText (rtf, false);
1402 rtf_style = (RtfSectionStyle) rtf_section_stack.Pop ();
1407 [MonoTODO("Add QuadJust support for justified alignment")]
1408 private void HandleControl(RTF.RTF rtf) {
1410 case RTF.Major.Unicode: {
1412 case RTF.Minor.UnicodeCharBytes: {
1413 rtf_skip_width = rtf.Param;
1417 case RTF.Minor.UnicodeChar: {
1418 rtf_skip_count += rtf_skip_width;
1419 rtf_line.Append((char)rtf.Param);
1426 case RTF.Major.Destination: {
1427 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1432 case RTF.Major.PictAttr:
1433 if (rtf.Picture != null && rtf.Picture.IsValid ()) {
1434 Line line = document.GetLine (rtf_cursor_y);
1435 document.InsertPicture (line, 0, rtf.Picture);
1438 FlushText (rtf, true);
1443 case RTF.Major.CharAttr: {
1445 case RTF.Minor.ForeColor: {
1446 System.Windows.Forms.RTF.Color color;
1448 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1450 if (color != null) {
1451 FlushText(rtf, false);
1452 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1453 this.rtf_style.rtf_color = ForeColor;
1455 this.rtf_style.rtf_color = Color.FromArgb(color.Red, color.Green, color.Blue);
1457 FlushText (rtf, false);
1462 case RTF.Minor.FontSize: {
1463 FlushText(rtf, false);
1464 this.rtf_style.rtf_rtffont_size = rtf.Param / 2;
1468 case RTF.Minor.FontNum: {
1469 System.Windows.Forms.RTF.Font font;
1471 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1473 FlushText(rtf, false);
1474 this.rtf_style.rtf_rtffont = font;
1479 case RTF.Minor.Plain: {
1480 FlushText(rtf, false);
1481 rtf_style.rtf_rtfstyle = FontStyle.Regular;
1485 case RTF.Minor.Bold: {
1486 FlushText(rtf, false);
1487 if (rtf.Param == RTF.RTF.NoParam) {
1488 rtf_style.rtf_rtfstyle |= FontStyle.Bold;
1490 rtf_style.rtf_rtfstyle &= ~FontStyle.Bold;
1495 case RTF.Minor.Italic: {
1496 FlushText(rtf, false);
1497 if (rtf.Param == RTF.RTF.NoParam) {
1498 rtf_style.rtf_rtfstyle |= FontStyle.Italic;
1500 rtf_style.rtf_rtfstyle &= ~FontStyle.Italic;
1505 case RTF.Minor.StrikeThru: {
1506 FlushText(rtf, false);
1507 if (rtf.Param == RTF.RTF.NoParam) {
1508 rtf_style.rtf_rtfstyle |= FontStyle.Strikeout;
1510 rtf_style.rtf_rtfstyle &= ~FontStyle.Strikeout;
1515 case RTF.Minor.Underline: {
1516 FlushText(rtf, false);
1517 if (rtf.Param == RTF.RTF.NoParam) {
1518 rtf_style.rtf_rtfstyle |= FontStyle.Underline;
1520 rtf_style.rtf_rtfstyle = rtf_style.rtf_rtfstyle & ~FontStyle.Underline;
1525 case RTF.Minor.NoUnderline: {
1526 FlushText(rtf, false);
1527 rtf_style.rtf_rtfstyle &= ~FontStyle.Underline;
1534 case RTF.Major.ParAttr: {
1535 switch (rtf.Minor) {
1537 case RTF.Minor.ParDef:
1538 FlushText (rtf, false);
1539 rtf_style.rtf_par_line_left_indent = 0;
1540 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1543 case RTF.Minor.LeftIndent:
1544 rtf_style.rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
1547 case RTF.Minor.QuadCenter:
1548 FlushText (rtf, false);
1549 rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
1552 case RTF.Minor.QuadJust:
1553 FlushText (rtf, false);
1554 rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
1557 case RTF.Minor.QuadLeft:
1558 FlushText (rtf, false);
1559 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1562 case RTF.Minor.QuadRight:
1563 FlushText (rtf, false);
1564 rtf_style.rtf_rtfalign = HorizontalAlignment.Right;
1570 case RTF.Major.SpecialChar: {
1571 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1578 private void SpecialChar(RTF.RTF rtf) {
1580 case RTF.Minor.Page:
1581 case RTF.Minor.Sect:
1583 case RTF.Minor.Line:
1584 case RTF.Minor.Par: {
1585 FlushText(rtf, true);
1589 case RTF.Minor.Cell: {
1594 case RTF.Minor.NoBrkSpace: {
1599 case RTF.Minor.Tab: {
1600 rtf_line.Append ("\t");
1601 // FlushText (rtf, false);
1605 case RTF.Minor.NoReqHyphen:
1606 case RTF.Minor.NoBrkHyphen: {
1607 rtf_line.Append ("-");
1608 // FlushText (rtf, false);
1612 case RTF.Minor.Bullet: {
1613 Console.WriteLine("*");
1617 case RTF.Minor.WidowCtrl:
1620 case RTF.Minor.EmDash: {
1621 rtf_line.Append ("\u2014");
1625 case RTF.Minor.EnDash: {
1626 rtf_line.Append ("\u2013");
1630 case RTF.Minor.LQuote: {
1631 Console.Write("\u2018");
1635 case RTF.Minor.RQuote: {
1636 Console.Write("\u2019");
1640 case RTF.Minor.LDblQuote: {
1641 Console.Write("\u201C");
1645 case RTF.Minor.RDblQuote: {
1646 Console.Write("\u201D");
1651 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1658 private void HandleText(RTF.RTF rtf) {
1659 if (rtf_skip_count > 0) {
1665 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1666 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1668 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1669 rtf_line.Append((char)rtf.Major);
1671 //rtf_line.Append((char)rtf.Major);
1672 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1676 rtf_line.Append (rtf.EncodedText);
1679 private void FlushText(RTF.RTF rtf, bool newline) {
1683 length = rtf_line.Length;
1684 if (!newline && (length == 0)) {
1688 if (rtf_style.rtf_rtffont == null) {
1689 // First font in table is default
1690 rtf_style.rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont (rtf, 0);
1693 font = new Font (rtf_style.rtf_rtffont.Name, rtf_style.rtf_rtffont_size, rtf_style.rtf_rtfstyle);
1695 if (rtf_style.rtf_color == Color.Empty) {
1696 System.Windows.Forms.RTF.Color color;
1698 // First color in table is default
1699 color = System.Windows.Forms.RTF.Color.GetColor (rtf, 0);
1701 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1702 rtf_style.rtf_color = ForeColor;
1704 rtf_style.rtf_color = Color.FromArgb (color.Red, color.Green, color.Blue);
1709 rtf_chars += rtf_line.Length;
1713 if (rtf_cursor_x == 0) {
1714 document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
1715 newline ? LineEnding.Rich : LineEnding.Wrap);
1716 if (rtf_style.rtf_par_line_left_indent != 0) {
1717 Line line = document.GetLine (rtf_cursor_y);
1718 line.indent = rtf_style.rtf_par_line_left_indent;
1723 line = document.GetLine (rtf_cursor_y);
1724 line.indent = rtf_style.rtf_par_line_left_indent;
1725 if (rtf_line.Length > 0) {
1726 document.InsertString (line, rtf_cursor_x, rtf_line.ToString ());
1727 document.FormatText (line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length,
1728 font, rtf_style.rtf_color, Color.Empty,
1729 FormatSpecified.Font | FormatSpecified.Color);
1732 document.Split (line, rtf_cursor_x + length);
1733 line = document.GetLine (rtf_cursor_y);
1734 line.ending = LineEnding.Rich;
1742 rtf_cursor_x += length;
1744 rtf_line.Length = 0; // Empty line
1747 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1752 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1755 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1758 rtf = new RTF.RTF(data);
1761 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1762 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1763 rtf.ClassCallback[RTF.TokenClass.Group] = new RTF.ClassDelegate(HandleGroup);
1767 rtf_line = new StringBuilder();
1768 rtf_style.rtf_color = Color.Empty;
1769 rtf_style.rtf_rtffont_size = (int)this.Font.Size;
1770 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1771 rtf_style.rtf_rtfstyle = FontStyle.Regular;
1772 rtf_style.rtf_rtffont = null;
1773 rtf_cursor_x = cursor_x;
1774 rtf_cursor_y = cursor_y;
1776 rtf.DefaultFont(this.Font.Name);
1778 rtf_text_map = new RTF.TextMap();
1779 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1781 document.SuspendRecalc ();
1784 rtf.Read(); // That's it
1785 FlushText(rtf, false);
1790 catch (RTF.RTFException e) {
1794 // Seems to be plain text or broken RTF
1795 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1798 to_x = rtf_cursor_x;
1799 to_y = rtf_cursor_y;
1802 // clear the section stack if it was used
1803 if (rtf_section_stack != null)
1804 rtf_section_stack.Clear();
1806 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1807 document.ResumeRecalc (true);
1809 document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1812 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1816 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1820 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1825 if (p.X >= document.ViewPortWidth) {
1826 p.X = document.ViewPortWidth - 1;
1827 } else if (p.X < 0) {
1831 if (p.Y >= document.ViewPortHeight) {
1832 p.Y = document.ViewPortHeight - 1;
1833 } else if (p.Y < 0) {
1837 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1840 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1841 if (prev_index != font_index) {
1842 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1845 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1846 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1849 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1853 if (prev_font != null) {
1859 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1863 if (prev_font != null) {
1869 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1870 if (font.Strikeout) {
1871 rtf.Append("\\strike");
1873 if (prev_font != null) {
1874 rtf.Append("\\strike0");
1879 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1880 if (font.Underline) {
1883 if (prev_font != null) {
1884 rtf.Append("\\ul0");
1890 [MonoTODO("Emit unicode and other special characters properly")]
1891 private void EmitRTFText(StringBuilder rtf, string text) {
1895 // start_pos and end_pos are 0-based
1896 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1910 sb = new StringBuilder();
1911 fonts = new ArrayList(10);
1912 colors = new ArrayList(10);
1914 // Two runs, first we parse to determine tables;
1915 // and unlike most of our processing here we work on tags
1918 line_no = start_line.line_no;
1921 // Add default font and color; to optimize document content we don't
1922 // use this.Font and this.ForeColor but the font/color from the first tag
1923 tag = LineTag.FindTag(start_line, pos);
1926 fonts.Add(font.Name);
1929 while (line_no <= end_line.line_no) {
1930 line = document.GetLine(line_no);
1931 tag = LineTag.FindTag(line, pos);
1933 if (line_no != end_line.line_no) {
1934 line_len = line.text.Length;
1939 while (pos < line_len) {
1940 if (tag.Font.Name != font.Name) {
1942 if (!fonts.Contains(font.Name)) {
1943 fonts.Add(font.Name);
1947 if (tag.Color != color) {
1949 if (!colors.Contains(color)) {
1954 pos = tag.Start + tag.Length - 1;
1961 // We have the tables, emit the header
1962 sb.Append("{\\rtf1\\ansi");
1963 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
1966 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1969 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
1971 // Emit the font table
1972 sb.Append("{\\fonttbl");
1973 for (i = 0; i < fonts.Count; i++) {
1974 sb.Append(String.Format("{{\\f{0}", i)); // {Font
1975 sb.Append("\\fnil"); // Family
1976 sb.Append("\\fcharset0 "); // Charset ANSI<space>
1977 sb.Append((string)fonts[i]); // Font name
1978 sb.Append(";}"); // }
1982 // Emit the color table (if needed)
1983 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))) {
1984 sb.Append("{\\colortbl "); // Header and NO! default color
1985 for (i = 0; i < colors.Count; i++) {
1986 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
1987 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
1988 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
1994 sb.Append("{\\*\\generator Mono RichTextBox;}");
1995 // Emit initial paragraph settings
1996 tag = LineTag.FindTag(start_line, start_pos);
1997 sb.Append("\\pard"); // Reset to default paragraph properties
1998 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.Font.Name), null, tag.Font); // Font properties
1999 sb.Append(" "); // Space separator
2002 color = (Color)colors[0];
2004 line_no = start_line.line_no;
2007 while (line_no <= end_line.line_no) {
2008 line = document.GetLine(line_no);
2009 tag = LineTag.FindTag(line, pos);
2011 if (line_no != end_line.line_no) {
2012 line_len = line.text.Length;
2017 while (pos < line_len) {
2020 if (tag.Font != font) {
2021 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.Font.Name), font, tag.Font);
2025 if (tag.Color != color) {
2027 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
2029 if (length != sb.Length) {
2030 sb.Append(" "); // Emit space to separate keywords from text
2033 // Emit the string itself
2034 if (line_no != end_line.line_no) {
2035 EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
2037 if (end_pos < (tag.Start + tag.Length - 1)) {
2038 // Emit partial tag only, end_pos is inside this tag
2039 EmitRTFText(sb, tag.Line.text.ToString(pos, end_pos - pos));
2041 EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
2045 pos = tag.Start + tag.Length - 1;
2048 if (pos >= line.text.Length) {
2049 if (line.ending != LineEnding.Wrap) {
2050 sb.Append("\\par\n");
2061 #endregion // Private Methods