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 base.EnableLinks = true;
87 rtf_style = new RtfSectionStyle ();
88 rtf_section_stack = null;
90 scrollbars = RichTextBoxScrollBars.Both;
91 alignment = HorizontalAlignment.Left;
92 LostFocus += new EventHandler(RichTextBox_LostFocus);
93 GotFocus += new EventHandler(RichTextBox_GotFocus);
94 BackColor = ThemeEngine.Current.ColorWindow;
96 backcolor_set = false;
97 language_option = RichTextBoxLanguageOptions.AutoFontSizeAdjust;
98 rich_text_shortcuts_enabled = true;
99 selection_back_color = DefaultBackColor;
101 ForeColor = ThemeEngine.Current.ColorWindowText;
103 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
104 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
107 SetStyle (ControlStyles.StandardDoubleClick, false);
110 #endregion // Public Constructors
112 #region Private & Internal Methods
114 internal override void HandleLinkClicked (LinkRectangle link)
116 OnLinkClicked (new LinkClickedEventArgs (link.LinkTag.LinkText));
119 internal override Color ChangeBackColor (Color backColor)
121 if (backColor == Color.Empty) {
123 backcolor_set = false;
125 backColor = SystemColors.Window;
128 backColor = SystemColors.Window;
134 private void RichTextBox_LostFocus(object sender, EventArgs e) {
138 private void RichTextBox_GotFocus(object sender, EventArgs e) {
141 #endregion // Private & Internal Methods
143 #region Public Instance Properties
147 public override bool AllowDrop {
149 return base.AllowDrop;
153 base.AllowDrop = value;
157 [DefaultValue(false)]
159 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
160 [RefreshProperties (RefreshProperties.Repaint)]
161 [EditorBrowsable (EditorBrowsableState.Never)]
166 public override bool AutoSize {
172 base.AutoSize = value;
176 [DefaultValue(false)]
177 public bool AutoWordSelection {
179 return auto_word_select;
183 auto_word_select = true;
188 [EditorBrowsable(EditorBrowsableState.Never)]
189 public override System.Drawing.Image BackgroundImage {
190 get { return base.BackgroundImage; }
191 set { base.BackgroundImage = value; }
196 [EditorBrowsable (EditorBrowsableState.Never)]
197 public override ImageLayout BackgroundImageLayout {
198 get { return base.BackgroundImageLayout; }
199 set { base.BackgroundImageLayout = value; }
205 public int BulletIndent {
207 return bullet_indent;
211 bullet_indent = value;
216 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
217 public bool CanRedo {
219 return document.undo.CanRedo;
224 public bool DetectUrls {
225 get { return base.EnableLinks; }
226 set { base.EnableLinks = value; }
231 [DefaultValue (false)]
232 public bool EnableAutoDragDrop {
233 get { return enable_auto_drag_drop; }
234 set { enable_auto_drag_drop = value; }
238 public override Font Font {
249 if (PreferredHeight != Height) {
250 Height = PreferredHeight;
256 // Font changes always set the whole doc to that font
257 start = document.GetLine(1);
258 end = document.GetLine(document.Lines);
259 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, Color.Empty, Color.Empty, FormatSpecified.Font);
264 public override Color ForeColor {
266 return base.ForeColor;
270 base.ForeColor = value;
277 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
278 public RichTextBoxLanguageOptions LanguageOption {
279 get { return language_option; }
280 set { language_option = value; }
284 [DefaultValue(Int32.MaxValue)]
285 public override int MaxLength {
287 return base.max_length;
291 base.max_length = value;
296 public override bool Multiline {
298 return base.Multiline;
302 base.Multiline = value;
307 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
309 public string RedoActionName {
311 return document.undo.RedoActionName;
318 [DefaultValue (true)]
319 [EditorBrowsable (EditorBrowsableState.Never)]
320 public bool RichTextShortcutsEnabled {
321 get { return rich_text_shortcuts_enabled; }
322 set { rich_text_shortcuts_enabled = value; }
328 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
329 public int RightMargin {
335 margin_right = value;
340 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
342 [RefreshProperties (RefreshProperties.All)]
351 start_line = document.GetLine(1);
352 end_line = document.GetLine(document.Lines);
353 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
360 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
362 InsertRTFFromStream(data, 0, 1);
370 [DefaultValue(RichTextBoxScrollBars.Both)]
372 public RichTextBoxScrollBars ScrollBars {
378 if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
379 throw new InvalidEnumArgumentException ("value", (int) value,
380 typeof (RichTextBoxScrollBars));
382 if (value != scrollbars) {
384 CalculateDocument ();
391 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
392 public string SelectedRtf {
394 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
406 if (document.selection_visible) {
407 document.ReplaceSelection("", false);
410 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
412 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
413 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
416 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
417 document.SetSelection(line, sel_start);
418 document.PositionCaret(line, sel_start);
419 document.DisplayCaret();
421 OnTextChanged(EventArgs.Empty);
427 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
428 public override string SelectedText {
430 return base.SelectedText;
434 base.SelectedText = value;
439 [DefaultValue(HorizontalAlignment.Left)]
440 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
441 public HorizontalAlignment SelectionAlignment {
443 HorizontalAlignment align;
448 start = document.ParagraphStart(document.selection_start.line);
449 align = start.alignment;
451 end = document.ParagraphEnd(document.selection_end.line);
456 if (line.alignment != align) {
457 return HorizontalAlignment.Left;
463 line = document.GetLine(line.line_no + 1);
474 start = document.ParagraphStart(document.selection_start.line);
476 end = document.ParagraphEnd(document.selection_end.line);
481 line.alignment = value;
486 line = document.GetLine(line.line_no + 1);
488 this.CalculateDocument();
495 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
496 public Color SelectionBackColor {
497 get { return selection_back_color; }
498 set { selection_back_color = value; }
503 [DefaultValue(false)]
504 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
506 public bool SelectionBullet {
517 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
519 public int SelectionCharOffset {
529 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
530 public Color SelectionColor {
537 start = document.selection_start.line.FindTag (document.selection_start.pos);
538 end = document.selection_start.line.FindTag (document.selection_end.pos);
542 while (tag != null && tag != end) {
544 if (!color.Equals (tag.Color))
547 tag = document.NextTag (tag);
554 if (value == Color.Empty)
555 value = DefaultForeColor;
560 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
561 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
563 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
564 document.selection_end.line, document.selection_end.pos + 1, null,
565 value, Color.Empty, FormatSpecified.Color);
567 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
568 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
570 document.UpdateView(document.selection_start.line, 0);
571 document.AlignCaret();
576 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
577 public Font SelectionFont {
584 start = document.selection_start.line.FindTag (document.selection_start.pos);
585 end = document.selection_start.line.FindTag (document.selection_end.pos);
588 if (selection_length > 1) {
590 while (tag != null && tag != end) {
592 if (!font.Equals(tag.Font))
595 tag = document.NextTag (tag);
606 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
607 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
609 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
610 document.selection_end.line, document.selection_end.pos + 1, value,
611 Color.Empty, Color.Empty, FormatSpecified.Font);
613 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
614 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
616 document.UpdateView(document.selection_start.line, 0);
617 document.AlignCaret();
624 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
626 public int SelectionHangingIndent {
637 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
639 public int SelectionIndent {
649 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
650 public override int SelectionLength {
652 return base.SelectionLength;
656 base.SelectionLength = value;
661 [DefaultValue(false)]
662 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
664 public bool SelectionProtected {
675 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
677 public int SelectionRightIndent {
687 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
689 public int[] SelectionTabs {
699 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
700 public RichTextBoxSelectionTypes SelectionType {
702 if (document.selection_start == document.selection_end) {
703 return RichTextBoxSelectionTypes.Empty;
707 if (SelectedText.Length > 1) {
708 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
711 return RichTextBoxSelectionTypes.Text;
715 [DefaultValue(false)]
717 public bool ShowSelectionMargin {
728 [RefreshProperties (RefreshProperties.All)]
730 public override string Text {
741 public override int TextLength {
743 return base.TextLength;
748 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
749 public string UndoActionName {
751 return document.undo.UndoActionName;
757 public float ZoomFactor {
766 #endregion // Public Instance Properties
768 #region Protected Instance Properties
769 protected override CreateParams CreateParams {
771 return base.CreateParams;
775 protected override Size DefaultSize {
777 return new Size(100, 96);
780 #endregion // Protected Instance Properties
782 #region Public Instance Methods
783 public bool CanPaste(DataFormats.Format clipFormat) {
784 if ((clipFormat.Name == DataFormats.Rtf) ||
785 (clipFormat.Name == DataFormats.Text) ||
786 (clipFormat.Name == DataFormats.UnicodeText)) {
792 public int Find(char[] characterSet) {
793 return Find(characterSet, -1, -1);
796 public int Find(char[] characterSet, int start) {
797 return Find(characterSet, start, -1);
800 public int Find(char[] characterSet, int start, int end) {
801 Document.Marker start_mark;
802 Document.Marker end_mark;
803 Document.Marker result;
806 document.GetMarker(out start_mark, true);
812 start_mark = new Document.Marker();
814 document.CharIndexToLineTag(start, out line, out tag, out pos);
815 start_mark.line = line;
816 start_mark.tag = tag;
817 start_mark.pos = pos;
821 document.GetMarker(out end_mark, false);
827 end_mark = new Document.Marker();
829 document.CharIndexToLineTag(end, out line, out tag, out pos);
830 end_mark.line = line;
835 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
836 return document.LineTagToCharIndex(result.line, result.pos);
842 public int Find(string str) {
843 return Find(str, -1, -1, RichTextBoxFinds.None);
846 public int Find(string str, int start, int end, RichTextBoxFinds options) {
847 Document.Marker start_mark;
848 Document.Marker end_mark;
849 Document.Marker result;
852 document.GetMarker(out start_mark, true);
858 start_mark = new Document.Marker();
860 document.CharIndexToLineTag(start, out line, out tag, out pos);
862 start_mark.line = line;
863 start_mark.tag = tag;
864 start_mark.pos = pos;
868 document.GetMarker(out end_mark, false);
874 end_mark = new Document.Marker();
876 document.CharIndexToLineTag(end, out line, out tag, out pos);
878 end_mark.line = line;
883 if (document.Find(str, start_mark, end_mark, out result, options)) {
884 return document.LineTagToCharIndex(result.line, result.pos);
890 public int Find(string str, int start, RichTextBoxFinds options) {
891 return Find(str, start, -1, options);
894 public int Find(string str, RichTextBoxFinds options) {
895 return Find(str, -1, -1, options);
900 public char GetCharFromPosition(Point pt) {
904 PointToTagPos(pt, out tag, out pos);
906 if (pos >= tag.Line.text.Length) {
910 return tag.Line.text[pos];
913 internal override char GetCharFromPositionInternal (Point p)
918 PointToTagPos (p, out tag, out pos);
920 if (pos >= tag.Line.text.Length)
923 return tag.Line.text[pos];
931 int GetCharIndexFromPosition(Point pt) {
935 PointToTagPos(pt, out tag, out pos);
937 return document.LineTagToCharIndex(tag.Line, pos);
944 int GetLineFromCharIndex(int index) {
949 document.CharIndexToLineTag(index, out line, out tag, out pos);
951 return line.LineNo - 1;
958 Point GetPositionFromCharIndex(int index) {
963 document.CharIndexToLineTag(index, out line, out tag, out pos);
965 return new Point((int)line.widths[pos] + 1, line.Y + 1);
968 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
972 // FIXME - ignoring unicode
973 if (fileType == RichTextBoxStreamType.PlainText) {
978 sb = new StringBuilder ((int) data.Length);
979 buffer = new char [1024];
981 throw new IOException("Not enough memory to load document");
984 StreamReader sr = new StreamReader (data, Encoding.Default, true);
985 int charsRead = sr.Read (buffer, 0, buffer.Length);
986 while (charsRead > 0) {
987 sb.Append (buffer, 0, charsRead);
988 charsRead = sr.Read (buffer, 0, buffer.Length);
990 base.Text = sb.ToString();
994 InsertRTFFromStream(data, 0, 1);
996 document.PositionCaret (document.GetLine (1), 0);
997 document.SetSelectionToCaret (true);
1001 [MonoTODO("Make smarter RTF detection?")]
1002 public void LoadFile(string path) {
1003 if (path.EndsWith(".rtf")) {
1004 LoadFile(path, RichTextBoxStreamType.RichText);
1006 LoadFile(path, RichTextBoxStreamType.PlainText);
1010 public void LoadFile(string path, RichTextBoxStreamType fileType) {
1017 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
1019 LoadFile(data, fileType);
1022 catch (Exception ex) {
1023 throw new IOException("Could not open file " + path, ex);
1033 public void Paste(DataFormats.Format clipFormat) {
1034 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
1039 document.undo.Redo ();
1042 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
1048 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
1049 encoding = Encoding.Unicode;
1051 encoding = Encoding.ASCII;
1055 case RichTextBoxStreamType.PlainText:
1056 case RichTextBoxStreamType.TextTextOleObjs:
1057 case RichTextBoxStreamType.UnicodePlainText: {
1059 bytes = encoding.GetBytes(document.Root.text.ToString());
1060 data.Write(bytes, 0, bytes.Length);
1064 for (i = 1; i < document.Lines; i++) {
1065 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
1066 data.Write(bytes, 0, bytes.Length);
1068 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
1069 data.Write(bytes, 0, bytes.Length);
1074 // If we're here we're saving RTF
1081 start_line = document.GetLine(1);
1082 end_line = document.GetLine(document.Lines);
1083 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
1085 bytes = new Byte[4096];
1087 // Let's chunk it so we don't use up all memory...
1088 for (i = 0; i < total; i += 1024) {
1089 if ((i + 1024) < total) {
1090 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
1092 current = total - i;
1093 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
1095 data.Write(bytes, 0, current);
1099 public void SaveFile(string path) {
1100 if (path.EndsWith(".rtf")) {
1101 SaveFile(path, RichTextBoxStreamType.RichText);
1103 SaveFile(path, RichTextBoxStreamType.PlainText);
1107 public void SaveFile(string path, RichTextBoxStreamType fileType) {
1113 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
1114 SaveFile(data, fileType);
1118 // throw new IOException("Could not write document to file " + path);
1129 [EditorBrowsable (EditorBrowsableState.Never)]
1130 public new void DrawToBitmap (Bitmap bitmap, Rectangle clip)
1132 Graphics dc = Graphics.FromImage (bitmap);
1134 if (backcolor_set || (Enabled && !read_only)) {
1135 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), clip);
1137 dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControl), clip);
1140 // Draw the viewable document
1141 document.Draw (dc, clip);
1145 #endregion // Public Instance Methods
1147 #region Protected Instance Methods
1148 protected virtual object CreateRichEditOleCallback() {
1149 throw new NotImplementedException();
1152 protected override void OnBackColorChanged(EventArgs e) {
1153 base.OnBackColorChanged (e);
1156 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1157 ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
1162 protected override void OnContextMenuChanged(EventArgs e) {
1163 base.OnContextMenuChanged (e);
1166 protected override void OnHandleCreated(EventArgs e) {
1167 base.OnHandleCreated (e);
1170 protected override void OnHandleDestroyed(EventArgs e) {
1171 base.OnHandleDestroyed (e);
1174 protected virtual void OnHScroll(EventArgs e) {
1175 EventHandler eh = (EventHandler)(Events [HScrollEvent]);
1180 [MonoTODO("Determine when to call this")]
1181 protected virtual void OnImeChange(EventArgs e) {
1182 EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
1187 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1188 LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
1193 protected virtual void OnProtected(EventArgs e) {
1194 EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
1199 protected override void OnRightToLeftChanged(EventArgs e) {
1200 base.OnRightToLeftChanged (e);
1203 protected virtual void OnSelectionChanged(EventArgs e) {
1204 EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
1210 protected override void OnSystemColorsChanged(EventArgs e) {
1211 base.OnSystemColorsChanged (e);
1214 protected override void OnTextChanged(EventArgs e) {
1215 base.OnTextChanged (e);
1219 protected virtual void OnVScroll(EventArgs e) {
1220 EventHandler eh = (EventHandler)(Events [VScrollEvent]);
1225 protected override void WndProc(ref Message m) {
1226 base.WndProc (ref m);
1230 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
1232 return base.ProcessCmdKey (ref msg, keyData);
1235 #endregion // Protected Instance Methods
1238 static object ContentsResizedEvent = new object ();
1239 static object HScrollEvent = new object ();
1240 static object ImeChangeEvent = new object ();
1241 static object LinkClickedEvent = new object ();
1242 static object ProtectedEvent = new object ();
1243 static object SelectionChangedEvent = new object ();
1244 static object VScrollEvent = new object ();
1247 [EditorBrowsable(EditorBrowsableState.Never)]
1248 public new event EventHandler BackgroundImageChanged {
1249 add { base.BackgroundImageChanged += value; }
1250 remove { base.BackgroundImageChanged -= value; }
1255 [EditorBrowsable (EditorBrowsableState.Never)]
1256 public new event EventHandler BackgroundImageLayoutChanged {
1257 add { base.BackgroundImageLayoutChanged += value; }
1258 remove { base.BackgroundImageLayoutChanged -= value; }
1262 public event ContentsResizedEventHandler ContentsResized {
1263 add { Events.AddHandler (ContentsResizedEvent, value); }
1264 remove { Events.RemoveHandler (ContentsResizedEvent, value); }
1269 [EditorBrowsable(EditorBrowsableState.Never)]
1270 public new event EventHandler DoubleClick {
1271 add { base.DoubleClick += value; }
1272 remove { base.DoubleClick -= value; }
1278 [EditorBrowsable(EditorBrowsableState.Never)]
1280 public new event DragEventHandler DragDrop {
1281 add { base.DragDrop += value; }
1282 remove { base.DragDrop -= value; }
1287 [EditorBrowsable(EditorBrowsableState.Never)]
1289 public new event DragEventHandler DragEnter {
1290 add { base.DragEnter += value; }
1291 remove { base.DragEnter -= value; }
1295 [EditorBrowsable(EditorBrowsableState.Never)]
1296 public new event EventHandler DragLeave {
1297 add { base.DragLeave += value; }
1298 remove { base.DragLeave -= value; }
1303 [EditorBrowsable(EditorBrowsableState.Never)]
1304 public new event DragEventHandler DragOver {
1305 add { base.DragOver += value; }
1306 remove { base.DragOver -= value; }
1311 [EditorBrowsable(EditorBrowsableState.Never)]
1312 public new event GiveFeedbackEventHandler GiveFeedback {
1313 add { base.GiveFeedback += value; }
1314 remove { base.GiveFeedback -= value; }
1317 public event EventHandler HScroll {
1318 add { Events.AddHandler (HScrollEvent, value); }
1319 remove { Events.RemoveHandler (HScrollEvent, value); }
1322 public event EventHandler ImeChange {
1323 add { Events.AddHandler (ImeChangeEvent, value); }
1324 remove { Events.RemoveHandler (ImeChangeEvent, value); }
1327 public event LinkClickedEventHandler LinkClicked {
1328 add { Events.AddHandler (LinkClickedEvent, value); }
1329 remove { Events.RemoveHandler (LinkClickedEvent, value); }
1332 public event EventHandler Protected {
1333 add { Events.AddHandler (ProtectedEvent, value); }
1334 remove { Events.RemoveHandler (ProtectedEvent, value); }
1338 [EditorBrowsable(EditorBrowsableState.Never)]
1339 public new event QueryContinueDragEventHandler QueryContinueDrag {
1340 add { base.QueryContinueDrag += value; }
1341 remove { base.QueryContinueDrag -= value; }
1344 public event EventHandler SelectionChanged {
1345 add { Events.AddHandler (SelectionChangedEvent, value); }
1346 remove { Events.RemoveHandler (SelectionChangedEvent, value); }
1349 public event EventHandler VScroll {
1350 add { Events.AddHandler (VScrollEvent, value); }
1351 remove { Events.RemoveHandler (VScrollEvent, value); }
1353 #endregion // Events
1355 #region Private Methods
1357 internal override void SelectWord ()
1359 document.ExpandSelection(CaretSelection.Word, false);
1362 private class RtfSectionStyle : ICloneable {
1363 internal Color rtf_color;
1364 internal RTF.Font rtf_rtffont;
1365 internal int rtf_rtffont_size;
1366 internal FontStyle rtf_rtfstyle;
1367 internal HorizontalAlignment rtf_rtfalign;
1368 internal int rtf_par_line_left_indent;
1370 public object Clone ()
1372 RtfSectionStyle new_style = new RtfSectionStyle ();
1374 new_style.rtf_color = rtf_color;
1375 new_style.rtf_par_line_left_indent = rtf_par_line_left_indent;
1376 new_style.rtf_rtfalign = rtf_rtfalign;
1377 new_style.rtf_rtffont = rtf_rtffont;
1378 new_style.rtf_rtffont_size = rtf_rtffont_size;
1379 new_style.rtf_rtfstyle = rtf_rtfstyle;
1385 // To allow us to keep track of the sections and revert formatting
1386 // as we go in and out of sections of the document.
1387 private void HandleGroup (RTF.RTF rtf)
1389 //start group - save the current formatting on to a stack
1390 //end group - go back to the formatting at the current group
1391 if (rtf_section_stack == null) {
1392 rtf_section_stack = new Stack ();
1395 if (rtf.Major == RTF.Major.BeginGroup) {
1396 rtf_section_stack.Push (rtf_style.Clone ());
1397 } else if (rtf.Major == RTF.Major.EndGroup) {
1398 if (rtf_section_stack.Count > 0) {
1399 FlushText (rtf, false);
1401 rtf_style = (RtfSectionStyle) rtf_section_stack.Pop ();
1406 [MonoTODO("Add QuadJust support for justified alignment")]
1407 private void HandleControl(RTF.RTF rtf) {
1409 case RTF.Major.Unicode: {
1411 case RTF.Minor.UnicodeCharBytes: {
1412 rtf_skip_width = rtf.Param;
1416 case RTF.Minor.UnicodeChar: {
1417 rtf_skip_count += rtf_skip_width;
1418 rtf_line.Append((char)rtf.Param);
1425 case RTF.Major.Destination: {
1426 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1431 case RTF.Major.PictAttr:
1432 if (rtf.Picture != null && rtf.Picture.IsValid ()) {
1433 Line line = document.GetLine (rtf_cursor_y);
1434 document.InsertPicture (line, 0, rtf.Picture);
1437 FlushText (rtf, true);
1442 case RTF.Major.CharAttr: {
1444 case RTF.Minor.ForeColor: {
1445 System.Windows.Forms.RTF.Color color;
1447 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1449 if (color != null) {
1450 FlushText(rtf, false);
1451 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1452 this.rtf_style.rtf_color = ForeColor;
1454 this.rtf_style.rtf_color = Color.FromArgb(color.Red, color.Green, color.Blue);
1456 FlushText (rtf, false);
1461 case RTF.Minor.FontSize: {
1462 FlushText(rtf, false);
1463 this.rtf_style.rtf_rtffont_size = rtf.Param / 2;
1467 case RTF.Minor.FontNum: {
1468 System.Windows.Forms.RTF.Font font;
1470 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1472 FlushText(rtf, false);
1473 this.rtf_style.rtf_rtffont = font;
1478 case RTF.Minor.Plain: {
1479 FlushText(rtf, false);
1480 rtf_style.rtf_rtfstyle = FontStyle.Regular;
1484 case RTF.Minor.Bold: {
1485 FlushText(rtf, false);
1486 if (rtf.Param == RTF.RTF.NoParam) {
1487 rtf_style.rtf_rtfstyle |= FontStyle.Bold;
1489 rtf_style.rtf_rtfstyle &= ~FontStyle.Bold;
1494 case RTF.Minor.Italic: {
1495 FlushText(rtf, false);
1496 if (rtf.Param == RTF.RTF.NoParam) {
1497 rtf_style.rtf_rtfstyle |= FontStyle.Italic;
1499 rtf_style.rtf_rtfstyle &= ~FontStyle.Italic;
1504 case RTF.Minor.StrikeThru: {
1505 FlushText(rtf, false);
1506 if (rtf.Param == RTF.RTF.NoParam) {
1507 rtf_style.rtf_rtfstyle |= FontStyle.Strikeout;
1509 rtf_style.rtf_rtfstyle &= ~FontStyle.Strikeout;
1514 case RTF.Minor.Underline: {
1515 FlushText(rtf, false);
1516 if (rtf.Param == RTF.RTF.NoParam) {
1517 rtf_style.rtf_rtfstyle |= FontStyle.Underline;
1519 rtf_style.rtf_rtfstyle = rtf_style.rtf_rtfstyle & ~FontStyle.Underline;
1524 case RTF.Minor.NoUnderline: {
1525 FlushText(rtf, false);
1526 rtf_style.rtf_rtfstyle &= ~FontStyle.Underline;
1533 case RTF.Major.ParAttr: {
1534 switch (rtf.Minor) {
1536 case RTF.Minor.ParDef:
1537 FlushText (rtf, false);
1538 rtf_style.rtf_par_line_left_indent = 0;
1539 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1542 case RTF.Minor.LeftIndent:
1543 rtf_style.rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
1546 case RTF.Minor.QuadCenter:
1547 FlushText (rtf, false);
1548 rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
1551 case RTF.Minor.QuadJust:
1552 FlushText (rtf, false);
1553 rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
1556 case RTF.Minor.QuadLeft:
1557 FlushText (rtf, false);
1558 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1561 case RTF.Minor.QuadRight:
1562 FlushText (rtf, false);
1563 rtf_style.rtf_rtfalign = HorizontalAlignment.Right;
1569 case RTF.Major.SpecialChar: {
1570 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1577 private void SpecialChar(RTF.RTF rtf) {
1579 case RTF.Minor.Page:
1580 case RTF.Minor.Sect:
1582 case RTF.Minor.Line:
1583 case RTF.Minor.Par: {
1584 FlushText(rtf, true);
1588 case RTF.Minor.Cell: {
1593 case RTF.Minor.NoBrkSpace: {
1598 case RTF.Minor.Tab: {
1599 rtf_line.Append ("\t");
1600 // FlushText (rtf, false);
1604 case RTF.Minor.NoReqHyphen:
1605 case RTF.Minor.NoBrkHyphen: {
1606 rtf_line.Append ("-");
1607 // FlushText (rtf, false);
1611 case RTF.Minor.Bullet: {
1612 Console.WriteLine("*");
1616 case RTF.Minor.WidowCtrl:
1619 case RTF.Minor.EmDash: {
1620 rtf_line.Append ("\u2014");
1624 case RTF.Minor.EnDash: {
1625 rtf_line.Append ("\u2013");
1629 case RTF.Minor.LQuote: {
1630 Console.Write("\u2018");
1634 case RTF.Minor.RQuote: {
1635 Console.Write("\u2019");
1639 case RTF.Minor.LDblQuote: {
1640 Console.Write("\u201C");
1644 case RTF.Minor.RDblQuote: {
1645 Console.Write("\u201D");
1650 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1657 private void HandleText(RTF.RTF rtf) {
1658 if (rtf_skip_count > 0) {
1664 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1665 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1667 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1668 rtf_line.Append((char)rtf.Major);
1670 //rtf_line.Append((char)rtf.Major);
1671 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1675 rtf_line.Append (rtf.EncodedText);
1678 private void FlushText(RTF.RTF rtf, bool newline) {
1682 length = rtf_line.Length;
1683 if (!newline && (length == 0)) {
1687 if (rtf_style.rtf_rtffont == null) {
1688 // First font in table is default
1689 rtf_style.rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont (rtf, 0);
1692 font = new Font (rtf_style.rtf_rtffont.Name, rtf_style.rtf_rtffont_size, rtf_style.rtf_rtfstyle);
1694 if (rtf_style.rtf_color == Color.Empty) {
1695 System.Windows.Forms.RTF.Color color;
1697 // First color in table is default
1698 color = System.Windows.Forms.RTF.Color.GetColor (rtf, 0);
1700 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1701 rtf_style.rtf_color = ForeColor;
1703 rtf_style.rtf_color = Color.FromArgb (color.Red, color.Green, color.Blue);
1708 rtf_chars += rtf_line.Length;
1712 if (rtf_cursor_x == 0) {
1713 document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
1714 newline ? LineEnding.Rich : LineEnding.Wrap);
1715 if (rtf_style.rtf_par_line_left_indent != 0) {
1716 Line line = document.GetLine (rtf_cursor_y);
1717 line.indent = rtf_style.rtf_par_line_left_indent;
1722 line = document.GetLine (rtf_cursor_y);
1723 line.indent = rtf_style.rtf_par_line_left_indent;
1724 if (rtf_line.Length > 0) {
1725 document.InsertString (line, rtf_cursor_x, rtf_line.ToString ());
1726 document.FormatText (line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length,
1727 font, rtf_style.rtf_color, Color.Empty,
1728 FormatSpecified.Font | FormatSpecified.Color);
1731 document.Split (line, rtf_cursor_x + length);
1732 line = document.GetLine (rtf_cursor_y);
1733 line.ending = LineEnding.Rich;
1741 rtf_cursor_x += length;
1743 rtf_line.Length = 0; // Empty line
1746 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1751 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1754 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1757 rtf = new RTF.RTF(data);
1760 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1761 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1762 rtf.ClassCallback[RTF.TokenClass.Group] = new RTF.ClassDelegate(HandleGroup);
1766 rtf_line = new StringBuilder();
1767 rtf_style.rtf_color = Color.Empty;
1768 rtf_style.rtf_rtffont_size = (int)this.Font.Size;
1769 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1770 rtf_style.rtf_rtfstyle = FontStyle.Regular;
1771 rtf_style.rtf_rtffont = null;
1772 rtf_cursor_x = cursor_x;
1773 rtf_cursor_y = cursor_y;
1775 rtf.DefaultFont(this.Font.Name);
1777 rtf_text_map = new RTF.TextMap();
1778 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1780 document.SuspendRecalc ();
1783 rtf.Read(); // That's it
1784 FlushText(rtf, false);
1789 catch (RTF.RTFException e) {
1793 // Seems to be plain text or broken RTF
1794 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1797 to_x = rtf_cursor_x;
1798 to_y = rtf_cursor_y;
1801 // clear the section stack if it was used
1802 if (rtf_section_stack != null)
1803 rtf_section_stack.Clear();
1805 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1806 document.ResumeRecalc (true);
1808 document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1811 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1815 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1819 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1824 if (p.X >= document.ViewPortWidth) {
1825 p.X = document.ViewPortWidth - 1;
1826 } else if (p.X < 0) {
1830 if (p.Y >= document.ViewPortHeight) {
1831 p.Y = document.ViewPortHeight - 1;
1832 } else if (p.Y < 0) {
1836 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1839 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1840 if (prev_index != font_index) {
1841 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1844 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1845 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1848 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1852 if (prev_font != null) {
1858 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1862 if (prev_font != null) {
1868 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1869 if (font.Strikeout) {
1870 rtf.Append("\\strike");
1872 if (prev_font != null) {
1873 rtf.Append("\\strike0");
1878 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1879 if (font.Underline) {
1882 if (prev_font != null) {
1883 rtf.Append("\\ul0");
1889 [MonoTODO("Emit unicode and other special characters properly")]
1890 private void EmitRTFText(StringBuilder rtf, string text) {
1894 // start_pos and end_pos are 0-based
1895 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1909 sb = new StringBuilder();
1910 fonts = new ArrayList(10);
1911 colors = new ArrayList(10);
1913 // Two runs, first we parse to determine tables;
1914 // and unlike most of our processing here we work on tags
1917 line_no = start_line.line_no;
1920 // Add default font and color; to optimize document content we don't
1921 // use this.Font and this.ForeColor but the font/color from the first tag
1922 tag = LineTag.FindTag(start_line, pos);
1925 fonts.Add(font.Name);
1928 while (line_no <= end_line.line_no) {
1929 line = document.GetLine(line_no);
1930 tag = LineTag.FindTag(line, pos);
1932 if (line_no != end_line.line_no) {
1933 line_len = line.text.Length;
1938 while (pos < line_len) {
1939 if (tag.Font.Name != font.Name) {
1941 if (!fonts.Contains(font.Name)) {
1942 fonts.Add(font.Name);
1946 if (tag.Color != color) {
1948 if (!colors.Contains(color)) {
1953 pos = tag.Start + tag.Length - 1;
1960 // We have the tables, emit the header
1961 sb.Append("{\\rtf1\\ansi");
1962 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
1965 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1968 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
1970 // Emit the font table
1971 sb.Append("{\\fonttbl");
1972 for (i = 0; i < fonts.Count; i++) {
1973 sb.Append(String.Format("{{\\f{0}", i)); // {Font
1974 sb.Append("\\fnil"); // Family
1975 sb.Append("\\fcharset0 "); // Charset ANSI<space>
1976 sb.Append((string)fonts[i]); // Font name
1977 sb.Append(";}"); // }
1981 // Emit the color table (if needed)
1982 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))) {
1983 sb.Append("{\\colortbl "); // Header and NO! default color
1984 for (i = 0; i < colors.Count; i++) {
1985 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
1986 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
1987 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
1993 sb.Append("{\\*\\generator Mono RichTextBox;}");
1994 // Emit initial paragraph settings
1995 tag = LineTag.FindTag(start_line, start_pos);
1996 sb.Append("\\pard"); // Reset to default paragraph properties
1997 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.Font.Name), null, tag.Font); // Font properties
1998 sb.Append(" "); // Space separator
2001 color = (Color)colors[0];
2003 line_no = start_line.line_no;
2006 while (line_no <= end_line.line_no) {
2007 line = document.GetLine(line_no);
2008 tag = LineTag.FindTag(line, pos);
2010 if (line_no != end_line.line_no) {
2011 line_len = line.text.Length;
2016 while (pos < line_len) {
2019 if (tag.Font != font) {
2020 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.Font.Name), font, tag.Font);
2024 if (tag.Color != color) {
2026 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
2028 if (length != sb.Length) {
2029 sb.Append(" "); // Emit space to separate keywords from text
2032 // Emit the string itself
2033 if (line_no != end_line.line_no) {
2034 EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
2036 if (end_pos < (tag.Start + tag.Length - 1)) {
2037 // Emit partial tag only, end_pos is inside this tag
2038 EmitRTFText(sb, tag.Line.text.ToString(pos, end_pos - pos));
2040 EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
2044 pos = tag.Start + tag.Length - 1;
2047 if (pos >= line.text.Length) {
2048 if (line.ending != LineEnding.Wrap) {
2049 sb.Append("\\par\n");
2060 #endregion // Private Methods