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;
87 backcolor_set = false;
89 ForeColor = ThemeEngine.Current.ColorWindowText;
91 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
92 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
95 SetStyle (ControlStyles.StandardDoubleClick, false);
98 #endregion // Public Constructors
100 #region Private & Internal Methods
101 internal override Color ChangeBackColor (Color backColor)
103 if (backColor == Color.Empty) {
105 backcolor_set = false;
107 backColor = SystemColors.Window;
110 backColor = SystemColors.Window;
116 private void RichTextBox_LostFocus(object sender, EventArgs e) {
120 private void RichTextBox_GotFocus(object sender, EventArgs e) {
123 #endregion // Private & Internal Methods
125 #region Public Instance Properties
129 public override bool AllowDrop {
131 return base.AllowDrop;
135 base.AllowDrop = value;
139 [DefaultValue(false)]
141 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
142 [RefreshProperties (RefreshProperties.Repaint)]
143 [EditorBrowsable (EditorBrowsableState.Never)]
148 public override bool AutoSize {
154 base.AutoSize = value;
158 [DefaultValue(false)]
159 public bool AutoWordSelection {
161 return auto_word_select;
165 auto_word_select = true;
170 [EditorBrowsable(EditorBrowsableState.Never)]
171 public override System.Drawing.Image BackgroundImage {
172 get { return base.BackgroundImage; }
173 set { base.BackgroundImage = value; }
178 public int BulletIndent {
180 return bullet_indent;
184 bullet_indent = value;
189 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
190 public bool CanRedo {
192 return document.undo.CanRedo;
197 public bool DetectUrls {
207 public override Font Font {
218 if (PreferredHeight != Height) {
219 Height = PreferredHeight;
225 // Font changes always set the whole doc to that font
226 start = document.GetLine(1);
227 end = document.GetLine(document.Lines);
228 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, null, null, FormatSpecified.Font);
233 public override Color ForeColor {
235 return base.ForeColor;
239 base.ForeColor = value;
243 [DefaultValue(Int32.MaxValue)]
244 public override int MaxLength {
246 return base.max_length;
250 base.max_length = value;
255 public override bool Multiline {
257 return base.Multiline;
261 base.Multiline = value;
266 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
268 public string RedoActionName {
270 return document.undo.RedoActionName;
276 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
277 public int RightMargin {
283 margin_right = value;
288 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
290 [RefreshProperties (RefreshProperties.All)]
299 start_line = document.GetLine(1);
300 end_line = document.GetLine(document.Lines);
301 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
308 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
310 InsertRTFFromStream(data, 0, 1);
318 [DefaultValue(RichTextBoxScrollBars.Both)]
320 public RichTextBoxScrollBars ScrollBars {
326 if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
327 throw new InvalidEnumArgumentException ("value", (int) value,
328 typeof (RichTextBoxScrollBars));
330 if (value != scrollbars) {
332 CalculateDocument ();
339 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
340 public string SelectedRtf {
342 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
354 if (document.selection_visible) {
355 document.ReplaceSelection("", false);
358 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
360 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
361 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
364 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
365 document.SetSelection(line, sel_start);
366 document.PositionCaret(line, sel_start);
367 document.DisplayCaret();
369 OnTextChanged(EventArgs.Empty);
375 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
376 public override string SelectedText {
378 return base.SelectedText;
382 base.SelectedText = value;
387 [DefaultValue(HorizontalAlignment.Left)]
388 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
389 public HorizontalAlignment SelectionAlignment {
391 HorizontalAlignment align;
396 start = document.ParagraphStart(document.selection_start.line);
397 align = start.alignment;
399 end = document.ParagraphEnd(document.selection_end.line);
404 if (line.alignment != align) {
405 return HorizontalAlignment.Left;
411 line = document.GetLine(line.line_no + 1);
422 start = document.ParagraphStart(document.selection_start.line);
424 end = document.ParagraphEnd(document.selection_end.line);
429 line.alignment = value;
434 line = document.GetLine(line.line_no + 1);
436 this.CalculateDocument();
441 [DefaultValue(false)]
442 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
444 public bool SelectionBullet {
455 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
457 public int SelectionCharOffset {
467 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
468 public Color SelectionColor {
475 start = document.selection_start.line.FindTag (document.selection_start.pos);
476 end = document.selection_start.line.FindTag (document.selection_end.pos);
477 color = start.color.Color;
480 while (tag != null && tag != end) {
482 if (!color.Equals (tag.color.Color))
485 tag = document.NextTag (tag);
495 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
496 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
498 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
499 document.selection_end.line, document.selection_end.pos + 1, null,
500 new SolidBrush (value), null, FormatSpecified.Color);
502 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
503 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
505 document.UpdateView(document.selection_start.line, 0);
506 document.AlignCaret();
511 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
512 public Font SelectionFont {
519 start = document.selection_start.line.FindTag (document.selection_start.pos);
520 end = document.selection_start.line.FindTag (document.selection_end.pos);
523 if (selection_length > 1) {
525 while (tag != null && tag != end) {
527 if (!font.Equals(tag.font))
530 tag = document.NextTag (tag);
541 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
542 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
544 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
545 document.selection_end.line, document.selection_end.pos + 1, value,
546 null, null, FormatSpecified.Font);
548 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
549 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
551 document.UpdateView(document.selection_start.line, 0);
552 document.AlignCaret();
559 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
561 public int SelectionHangingIndent {
572 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
574 public int SelectionIndent {
584 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
585 public override int SelectionLength {
587 return base.SelectionLength;
591 base.SelectionLength = value;
596 [DefaultValue(false)]
597 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
599 public bool SelectionProtected {
610 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
612 public int SelectionRightIndent {
622 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
624 public int[] SelectionTabs {
634 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
635 public RichTextBoxSelectionTypes SelectionType {
637 if (document.selection_start == document.selection_end) {
638 return RichTextBoxSelectionTypes.Empty;
642 if (SelectedText.Length > 1) {
643 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
646 return RichTextBoxSelectionTypes.Text;
650 [DefaultValue(false)]
652 public bool ShowSelectionMargin {
663 [RefreshProperties (RefreshProperties.All)]
665 public override string Text {
676 public override int TextLength {
678 return base.TextLength;
683 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
684 public string UndoActionName {
686 return document.undo.UndoActionName;
692 public float ZoomFactor {
701 #endregion // Public Instance Properties
703 #region Protected Instance Properties
704 protected override CreateParams CreateParams {
706 return base.CreateParams;
710 protected override Size DefaultSize {
712 return new Size(100, 96);
715 #endregion // Protected Instance Properties
717 #region Public Instance Methods
718 public bool CanPaste(DataFormats.Format clipFormat) {
719 if ((clipFormat.Name == DataFormats.Rtf) ||
720 (clipFormat.Name == DataFormats.Text) ||
721 (clipFormat.Name == DataFormats.UnicodeText)) {
727 public int Find(char[] characterSet) {
728 return Find(characterSet, -1, -1);
731 public int Find(char[] characterSet, int start) {
732 return Find(characterSet, start, -1);
735 public int Find(char[] characterSet, int start, int end) {
736 Document.Marker start_mark;
737 Document.Marker end_mark;
738 Document.Marker result;
741 document.GetMarker(out start_mark, true);
747 start_mark = new Document.Marker();
749 document.CharIndexToLineTag(start, out line, out tag, out pos);
750 start_mark.line = line;
751 start_mark.tag = tag;
752 start_mark.pos = pos;
756 document.GetMarker(out end_mark, false);
762 end_mark = new Document.Marker();
764 document.CharIndexToLineTag(end, out line, out tag, out pos);
765 end_mark.line = line;
770 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
771 return document.LineTagToCharIndex(result.line, result.pos);
777 public int Find(string str) {
778 return Find(str, -1, -1, RichTextBoxFinds.None);
781 public int Find(string str, int start, int end, RichTextBoxFinds options) {
782 Document.Marker start_mark;
783 Document.Marker end_mark;
784 Document.Marker result;
787 document.GetMarker(out start_mark, true);
793 start_mark = new Document.Marker();
795 document.CharIndexToLineTag(start, out line, out tag, out pos);
797 start_mark.line = line;
798 start_mark.tag = tag;
799 start_mark.pos = pos;
803 document.GetMarker(out end_mark, false);
809 end_mark = new Document.Marker();
811 document.CharIndexToLineTag(end, out line, out tag, out pos);
813 end_mark.line = line;
818 if (document.Find(str, start_mark, end_mark, out result, options)) {
819 return document.LineTagToCharIndex(result.line, result.pos);
825 public int Find(string str, int start, RichTextBoxFinds options) {
826 return Find(str, start, -1, options);
829 public int Find(string str, RichTextBoxFinds options) {
830 return Find(str, -1, -1, options);
837 char GetCharFromPosition(Point pt) {
841 PointToTagPos(pt, out tag, out pos);
843 if (pos >= tag.line.text.Length) {
847 return tag.line.text[pos];
855 int GetCharIndexFromPosition(Point pt) {
859 PointToTagPos(pt, out tag, out pos);
861 return document.LineTagToCharIndex(tag.line, pos);
868 int GetLineFromCharIndex(int index) {
873 document.CharIndexToLineTag(index, out line, out tag, out pos);
875 return line.LineNo - 1;
882 Point GetPositionFromCharIndex(int index) {
887 document.CharIndexToLineTag(index, out line, out tag, out pos);
889 return new Point((int)line.widths[pos] + 1, line.Y + 1);
892 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
896 // FIXME - ignoring unicode
897 if (fileType == RichTextBoxStreamType.PlainText) {
902 sb = new StringBuilder ((int) data.Length);
903 buffer = new char [1024];
905 throw new IOException("Not enough memory to load document");
908 StreamReader sr = new StreamReader (data, Encoding.Default, true);
909 int charsRead = sr.Read (buffer, 0, buffer.Length);
910 while (charsRead > 0) {
911 sb.Append (buffer, 0, charsRead);
912 charsRead = sr.Read (buffer, 0, buffer.Length);
914 base.Text = sb.ToString();
918 InsertRTFFromStream(data, 0, 1);
920 document.PositionCaret (document.GetLine (1), 0);
921 document.SetSelectionToCaret (true);
925 [MonoTODO("Make smarter RTF detection?")]
926 public void LoadFile(string path) {
927 if (path.EndsWith(".rtf")) {
928 LoadFile(path, RichTextBoxStreamType.RichText);
930 LoadFile(path, RichTextBoxStreamType.PlainText);
934 public void LoadFile(string path, RichTextBoxStreamType fileType) {
941 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
943 LoadFile(data, fileType);
946 catch (Exception ex) {
947 throw new IOException("Could not open file " + path, ex);
957 public void Paste(DataFormats.Format clipFormat) {
958 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
963 document.undo.Redo ();
966 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
972 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
973 encoding = Encoding.Unicode;
975 encoding = Encoding.ASCII;
979 case RichTextBoxStreamType.PlainText:
980 case RichTextBoxStreamType.TextTextOleObjs:
981 case RichTextBoxStreamType.UnicodePlainText: {
983 bytes = encoding.GetBytes(document.Root.text.ToString());
984 data.Write(bytes, 0, bytes.Length);
988 for (i = 1; i < document.Lines; i++) {
989 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
990 data.Write(bytes, 0, bytes.Length);
992 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
993 data.Write(bytes, 0, bytes.Length);
998 // If we're here we're saving RTF
1005 start_line = document.GetLine(1);
1006 end_line = document.GetLine(document.Lines);
1007 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
1009 bytes = new Byte[4096];
1011 // Let's chunk it so we don't use up all memory...
1012 for (i = 0; i < total; i += 1024) {
1013 if ((i + 1024) < total) {
1014 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
1016 current = total - i;
1017 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
1019 data.Write(bytes, 0, current);
1023 public void SaveFile(string path) {
1024 if (path.EndsWith(".rtf")) {
1025 SaveFile(path, RichTextBoxStreamType.RichText);
1027 SaveFile(path, RichTextBoxStreamType.PlainText);
1031 public void SaveFile(string path, RichTextBoxStreamType fileType) {
1037 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
1038 SaveFile(data, fileType);
1042 // throw new IOException("Could not write document to file " + path);
1053 public void DrawToBitmap (Bitmap bitmap, Rectangle clip)
1055 Graphics dc = Graphics.FromImage (bitmap);
1057 if (backcolor_set || (Enabled && !read_only)) {
1058 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), clip);
1060 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControl), clip);
1063 // Draw the viewable document
1064 document.Draw (dc, clip);
1068 #endregion // Public Instance Methods
1070 #region Protected Instance Methods
1071 protected virtual object CreateRichEditOleCallback() {
1072 throw new NotImplementedException();
1075 protected override void OnBackColorChanged(EventArgs e) {
1076 base.OnBackColorChanged (e);
1079 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1080 ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
1085 protected override void OnContextMenuChanged(EventArgs e) {
1086 base.OnContextMenuChanged (e);
1089 protected override void OnHandleCreated(EventArgs e) {
1090 base.OnHandleCreated (e);
1093 protected override void OnHandleDestroyed(EventArgs e) {
1094 base.OnHandleDestroyed (e);
1097 protected virtual void OnHScroll(EventArgs e) {
1098 EventHandler eh = (EventHandler)(Events [HScrollEvent]);
1103 [MonoTODO("Determine when to call this")]
1104 protected virtual void OnImeChange(EventArgs e) {
1105 EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
1110 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1111 LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
1116 protected virtual void OnProtected(EventArgs e) {
1117 EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
1122 protected override void OnRightToLeftChanged(EventArgs e) {
1123 base.OnRightToLeftChanged (e);
1126 protected virtual void OnSelectionChanged(EventArgs e) {
1127 EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
1132 protected override void OnSystemColorsChanged(EventArgs e) {
1133 base.OnSystemColorsChanged (e);
1136 protected override void OnTextChanged(EventArgs e) {
1137 base.OnTextChanged (e);
1140 protected virtual void OnVScroll(EventArgs e) {
1141 EventHandler eh = (EventHandler)(Events [VScrollEvent]);
1146 protected override void WndProc(ref Message m) {
1147 base.WndProc (ref m);
1151 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
1153 return base.ProcessCmdKey (ref msg, keyData);
1156 #endregion // Protected Instance Methods
1159 static object ContentsResizedEvent = new object ();
1160 static object HScrollEvent = new object ();
1161 static object ImeChangeEvent = new object ();
1162 static object LinkClickedEvent = new object ();
1163 static object ProtectedEvent = new object ();
1164 static object SelectionChangedEvent = new object ();
1165 static object VScrollEvent = new object ();
1168 [EditorBrowsable(EditorBrowsableState.Never)]
1169 public new event EventHandler BackgroundImageChanged {
1170 add { base.BackgroundImageChanged += value; }
1171 remove { base.BackgroundImageChanged -= value; }
1174 public event ContentsResizedEventHandler ContentsResized {
1175 add { Events.AddHandler (ContentsResizedEvent, value); }
1176 remove { Events.RemoveHandler (ContentsResizedEvent, value); }
1180 [EditorBrowsable(EditorBrowsableState.Never)]
1181 public new event EventHandler DoubleClick {
1182 add { base.DoubleClick += value; }
1183 remove { base.DoubleClick -= value; }
1188 [EditorBrowsable(EditorBrowsableState.Never)]
1190 public new event DragEventHandler DragDrop {
1191 add { base.DragDrop += value; }
1192 remove { base.DragDrop -= value; }
1197 [EditorBrowsable(EditorBrowsableState.Never)]
1199 public new event DragEventHandler DragEnter {
1200 add { base.DragEnter += value; }
1201 remove { base.DragEnter -= value; }
1205 [EditorBrowsable(EditorBrowsableState.Never)]
1206 public new event EventHandler DragLeave {
1207 add { base.DragLeave += value; }
1208 remove { base.DragLeave -= value; }
1213 [EditorBrowsable(EditorBrowsableState.Never)]
1214 public new event DragEventHandler DragOver {
1215 add { base.DragOver += value; }
1216 remove { base.DragOver -= value; }
1221 [EditorBrowsable(EditorBrowsableState.Never)]
1222 public new event GiveFeedbackEventHandler GiveFeedback {
1223 add { base.GiveFeedback += value; }
1224 remove { base.GiveFeedback -= value; }
1227 public event EventHandler HScroll {
1228 add { Events.AddHandler (HScrollEvent, value); }
1229 remove { Events.RemoveHandler (HScrollEvent, value); }
1232 public event EventHandler ImeChange {
1233 add { Events.AddHandler (ImeChangeEvent, value); }
1234 remove { Events.RemoveHandler (ImeChangeEvent, value); }
1237 public event LinkClickedEventHandler LinkClicked {
1238 add { Events.AddHandler (LinkClickedEvent, value); }
1239 remove { Events.RemoveHandler (LinkClickedEvent, value); }
1242 public event EventHandler Protected {
1243 add { Events.AddHandler (ProtectedEvent, value); }
1244 remove { Events.RemoveHandler (ProtectedEvent, value); }
1248 [EditorBrowsable(EditorBrowsableState.Never)]
1249 public new event QueryContinueDragEventHandler QueryContinueDrag {
1250 add { base.QueryContinueDrag += value; }
1251 remove { base.QueryContinueDrag -= value; }
1254 public event EventHandler SelectionChanged {
1255 add { Events.AddHandler (SelectionChangedEvent, value); }
1256 remove { Events.RemoveHandler (SelectionChangedEvent, value); }
1259 public event EventHandler VScroll {
1260 add { Events.AddHandler (VScrollEvent, value); }
1261 remove { Events.RemoveHandler (VScrollEvent, value); }
1263 #endregion // Events
1265 #region Private Methods
1267 internal override void SelectWord ()
1269 document.ExpandSelection(CaretSelection.Word, false);
1272 private void HandleControl(RTF.RTF rtf) {
1274 case RTF.Major.Unicode: {
1276 case RTF.Minor.UnicodeCharBytes: {
1277 rtf_skip_width = rtf.Param;
1281 case RTF.Minor.UnicodeChar: {
1282 rtf_skip_count += rtf_skip_width;
1283 rtf_line.Append((char)rtf.Param);
1290 case RTF.Major.Destination: {
1291 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1296 case RTF.Major.PictAttr:
1297 if (rtf.Picture != null && rtf.Picture.IsValid ()) {
1298 Line line = document.GetLine (rtf_cursor_y);
1299 document.InsertPicture (line, 0, rtf.Picture);
1302 FlushText (rtf, true);
1307 case RTF.Major.CharAttr: {
1309 case RTF.Minor.ForeColor: {
1310 System.Windows.Forms.RTF.Color color;
1312 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1314 if (color != null) {
1315 FlushText(rtf, false);
1316 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1317 this.rtf_color = new SolidBrush(ForeColor);
1319 this.rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1321 FlushText (rtf, false);
1326 case RTF.Minor.FontSize: {
1327 FlushText(rtf, false);
1328 this.rtf_rtffont_size = rtf.Param / 2;
1332 case RTF.Minor.FontNum: {
1333 System.Windows.Forms.RTF.Font font;
1335 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1337 FlushText(rtf, false);
1338 this.rtf_rtffont = font;
1343 case RTF.Minor.Plain: {
1344 FlushText(rtf, false);
1345 rtf_rtfstyle = FontStyle.Regular;
1349 case RTF.Minor.Bold: {
1350 FlushText(rtf, false);
1351 if (rtf.Param == RTF.RTF.NoParam) {
1352 rtf_rtfstyle |= FontStyle.Bold;
1354 rtf_rtfstyle &= ~FontStyle.Bold;
1359 case RTF.Minor.Italic: {
1360 FlushText(rtf, false);
1361 if (rtf.Param == RTF.RTF.NoParam) {
1362 rtf_rtfstyle |= FontStyle.Italic;
1364 rtf_rtfstyle &= ~FontStyle.Italic;
1369 case RTF.Minor.StrikeThru: {
1370 FlushText(rtf, false);
1371 if (rtf.Param == RTF.RTF.NoParam) {
1372 rtf_rtfstyle |= FontStyle.Strikeout;
1374 rtf_rtfstyle &= ~FontStyle.Strikeout;
1379 case RTF.Minor.Underline: {
1380 FlushText(rtf, false);
1381 if (rtf.Param == RTF.RTF.NoParam) {
1382 rtf_rtfstyle |= FontStyle.Underline;
1384 rtf_rtfstyle = rtf_rtfstyle & ~FontStyle.Underline;
1389 case RTF.Minor.NoUnderline: {
1390 FlushText(rtf, false);
1391 rtf_rtfstyle &= ~FontStyle.Underline;
1398 case RTF.Major.ParAttr: {
1399 switch (rtf.Minor) {
1400 case RTF.Minor.LeftIndent:
1401 rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
1407 case RTF.Major.SpecialChar: {
1408 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1415 private void SpecialChar(RTF.RTF rtf) {
1417 case RTF.Minor.Page:
1418 case RTF.Minor.Sect:
1420 case RTF.Minor.Line:
1421 case RTF.Minor.Par: {
1422 FlushText(rtf, true);
1426 case RTF.Minor.Cell: {
1431 case RTF.Minor.NoBrkSpace: {
1436 case RTF.Minor.Tab: {
1437 rtf_line.Append ("\t");
1438 // FlushText (rtf, false);
1442 case RTF.Minor.NoReqHyphen:
1443 case RTF.Minor.NoBrkHyphen: {
1444 rtf_line.Append ("-");
1445 // FlushText (rtf, false);
1449 case RTF.Minor.Bullet: {
1450 Console.WriteLine("*");
1454 case RTF.Minor.WidowCtrl:
1457 case RTF.Minor.EmDash: {
1458 rtf_line.Append ("\u2014");
1462 case RTF.Minor.EnDash: {
1463 rtf_line.Append ("\u2013");
1467 case RTF.Minor.LQuote: {
1468 Console.Write("\u2018");
1472 case RTF.Minor.RQuote: {
1473 Console.Write("\u2019");
1477 case RTF.Minor.LDblQuote: {
1478 Console.Write("\u201C");
1482 case RTF.Minor.RDblQuote: {
1483 Console.Write("\u201D");
1488 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1495 private void HandleText(RTF.RTF rtf) {
1496 if (rtf_skip_count > 0) {
1502 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1503 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1505 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1506 rtf_line.Append((char)rtf.Major);
1508 //rtf_line.Append((char)rtf.Major);
1509 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1513 rtf_line.Append (rtf.EncodedText);
1516 private void FlushText(RTF.RTF rtf, bool newline) {
1520 length = rtf_line.Length;
1521 if (!newline && (length == 0)) {
1525 if (rtf_rtffont == null) {
1526 // First font in table is default
1527 rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont(rtf, 0);
1530 font = new Font(rtf_rtffont.Name, rtf_rtffont_size, rtf_rtfstyle);
1532 if (rtf_color == null) {
1533 System.Windows.Forms.RTF.Color color;
1535 // First color in table is default
1536 color = System.Windows.Forms.RTF.Color.GetColor(rtf, 0);
1538 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1539 rtf_color = new SolidBrush(ForeColor);
1541 rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1546 rtf_chars += rtf_line.Length;
1548 if (rtf_cursor_x == 0) {
1549 document.Add(rtf_cursor_y, rtf_line.ToString(), rtf_rtfalign, font, rtf_color, LineEnding.Wrap);
1550 if (rtf_par_line_left_indent != 0) {
1551 Line line = document.GetLine (rtf_cursor_y);
1552 line.indent = rtf_par_line_left_indent;
1557 line = document.GetLine(rtf_cursor_y);
1558 line.indent = rtf_par_line_left_indent;
1559 if (rtf_line.Length > 0) {
1560 document.InsertString(line, rtf_cursor_x, rtf_line.ToString());
1561 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
1564 document.Split(line, rtf_cursor_x + length);
1572 rtf_cursor_x += length;
1574 rtf_line.Length = 0; // Empty line
1577 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1582 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1585 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1588 rtf = new RTF.RTF(data);
1591 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1592 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1596 rtf_line = new StringBuilder();
1598 rtf_rtffont_size = (int)this.Font.Size;
1599 rtf_rtfalign = HorizontalAlignment.Left;
1600 rtf_rtfstyle = FontStyle.Regular;
1602 rtf_cursor_x = cursor_x;
1603 rtf_cursor_y = cursor_y;
1605 rtf.DefaultFont(this.Font.Name);
1607 rtf_text_map = new RTF.TextMap();
1608 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1610 document.SuspendRecalc ();
1613 rtf.Read(); // That's it
1614 FlushText(rtf, false);
1619 catch (RTF.RTFException e) {
1623 // Seems to be plain text or broken RTF
1624 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1627 to_x = rtf_cursor_x;
1628 to_y = rtf_cursor_y;
1631 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1632 document.ResumeRecalc (true);
1634 document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1637 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1641 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1645 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1650 if (p.X >= document.ViewPortWidth) {
1651 p.X = document.ViewPortWidth - 1;
1652 } else if (p.X < 0) {
1656 if (p.Y >= document.ViewPortHeight) {
1657 p.Y = document.ViewPortHeight - 1;
1658 } else if (p.Y < 0) {
1662 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1665 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1666 if (prev_index != font_index) {
1667 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1670 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1671 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1674 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1678 if (prev_font != null) {
1684 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1688 if (prev_font != null) {
1694 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1695 if (font.Strikeout) {
1696 rtf.Append("\\strike");
1698 if (prev_font != null) {
1699 rtf.Append("\\strike0");
1704 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1705 if (font.Underline) {
1708 if (prev_font != null) {
1709 rtf.Append("\\ul0");
1715 [MonoTODO("Emit unicode and other special characters properly")]
1716 private void EmitRTFText(StringBuilder rtf, string text) {
1720 // start_pos and end_pos are 0-based
1721 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1735 sb = new StringBuilder();
1736 fonts = new ArrayList(10);
1737 colors = new ArrayList(10);
1739 // Two runs, first we parse to determine tables;
1740 // and unlike most of our processing here we work on tags
1743 line_no = start_line.line_no;
1746 // Add default font and color; to optimize document content we don't
1747 // use this.Font and this.ForeColor but the font/color from the first tag
1748 tag = LineTag.FindTag(start_line, pos);
1750 color = ((SolidBrush)tag.color).Color;
1751 fonts.Add(font.Name);
1754 while (line_no <= end_line.line_no) {
1755 line = document.GetLine(line_no);
1756 tag = LineTag.FindTag(line, pos);
1758 if (line_no != end_line.line_no) {
1759 line_len = line.text.Length;
1764 while (pos < line_len) {
1765 if (tag.font.Name != font.Name) {
1767 if (!fonts.Contains(font.Name)) {
1768 fonts.Add(font.Name);
1772 if (((SolidBrush)tag.color).Color != color) {
1773 color = ((SolidBrush)tag.color).Color;
1774 if (!colors.Contains(color)) {
1779 pos = tag.start + tag.length - 1;
1786 // We have the tables, emit the header
1787 sb.Append("{\\rtf1\\ansi");
1788 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
1791 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1794 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
1796 // Emit the font table
1797 sb.Append("{\\fonttbl");
1798 for (i = 0; i < fonts.Count; i++) {
1799 sb.Append(String.Format("{{\\f{0}", i)); // {Font
1800 sb.Append("\\fnil"); // Family
1801 sb.Append("\\fcharset0 "); // Charset ANSI<space>
1802 sb.Append((string)fonts[i]); // Font name
1803 sb.Append(";}"); // }
1807 // Emit the color table (if needed)
1808 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))) {
1809 sb.Append("{\\colortbl "); // Header and NO! default color
1810 for (i = 0; i < colors.Count; i++) {
1811 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
1812 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
1813 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
1819 sb.Append("{\\*\\generator Mono RichTextBox;}");
1820 // Emit initial paragraph settings
1821 tag = LineTag.FindTag(start_line, start_pos);
1822 sb.Append("\\pard"); // Reset to default paragraph properties
1823 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.font.Name), null, tag.font); // Font properties
1824 sb.Append(" "); // Space separator
1827 color = (Color)colors[0];
1829 line_no = start_line.line_no;
1832 while (line_no <= end_line.line_no) {
1833 line = document.GetLine(line_no);
1834 tag = LineTag.FindTag(line, pos);
1836 if (line_no != end_line.line_no) {
1837 line_len = line.text.Length;
1842 while (pos < line_len) {
1845 if (tag.font != font) {
1846 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.font.Name), font, tag.font);
1850 if (((SolidBrush)tag.color).Color != color) {
1851 color = ((SolidBrush)tag.color).Color;
1852 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
1854 if (length != sb.Length) {
1855 sb.Append(" "); // Emit space to separate keywords from text
1858 // Emit the string itself
1859 if (line_no != end_line.line_no) {
1860 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1862 if (end_pos < (tag.start + tag.length - 1)) {
1863 // Emit partial tag only, end_pos is inside this tag
1864 EmitRTFText(sb, tag.line.text.ToString(pos, end_pos - pos));
1866 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1870 pos = tag.start + tag.length - 1;
1873 if (pos >= line.text.Length) {
1874 if (line.ending != LineEnding.Wrap) {
1875 sb.Append("\\par\n");
1886 #endregion // Private Methods