In System.Windows.Forms:
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / RichTextBox.cs
blobf8327a0ed136a4512d6e282b5e920b6ae4d9d363
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:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
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)
22 // Authors:
23 // Peter Bartok <pbartok@novell.com>
27 // #define DEBUG
29 using System;
30 using System.Collections;
31 using System.ComponentModel;
32 using System.Drawing;
33 using System.Drawing.Imaging;
34 using System.IO;
35 using System.Text;
36 using System.Runtime.InteropServices;
37 using RTF=System.Windows.Forms.RTF;
39 namespace System.Windows.Forms {
40 #if NET_2_0
41 [ClassInterface (ClassInterfaceType.AutoDispatch)]
42 [Docking (DockingBehavior.Ask)]
43 [ComVisible (true)]
44 [Designer ("System.Windows.Forms.Design.RichTextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
45 #endif
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;
52 internal float zoom;
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_count;
61 private int rtf_cursor_x;
62 private int rtf_cursor_y;
63 private int rtf_chars;
65 #if NET_2_0
66 private bool enable_auto_drag_drop;
67 private RichTextBoxLanguageOptions language_option;
68 private bool rich_text_shortcuts_enabled;
69 private Color selection_back_color;
70 #endif
71 #endregion // Local Variables
73 #region Public Constructors
74 public RichTextBox() {
75 accepts_return = true;
76 auto_size = false;
77 auto_word_select = false;
78 bullet_indent = 0;
79 base.MaxLength = Int32.MaxValue;
80 margin_right = 0;
81 zoom = 1;
82 base.Multiline = true;
83 document.CRLFSize = 1;
84 shortcuts_enabled = true;
85 base.EnableLinks = true;
86 richtext = 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;
96 #if NET_2_0
97 backcolor_set = false;
98 language_option = RichTextBoxLanguageOptions.AutoFontSizeAdjust;
99 rich_text_shortcuts_enabled = true;
100 selection_back_color = DefaultBackColor;
101 #endif
102 ForeColor = ThemeEngine.Current.ColorWindowText;
104 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
105 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
107 #if NET_2_0
108 SetStyle (ControlStyles.StandardDoubleClick, false);
109 #endif
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) {
123 #if NET_2_0
124 backcolor_set = false;
125 if (!ReadOnly) {
126 backColor = SystemColors.Window;
128 #else
129 backColor = SystemColors.Window;
130 #endif
132 return backColor;
135 internal override void RaiseSelectionChanged()
137 OnSelectionChanged (EventArgs.Empty);
140 private void RichTextBox_LostFocus(object sender, EventArgs e) {
141 Invalidate();
144 private void RichTextBox_GotFocus(object sender, EventArgs e) {
145 Invalidate();
147 #endregion // Private & Internal Methods
149 #region Public Instance Properties
150 #if NET_2_0
151 [Browsable (false)]
152 #endif
153 public override bool AllowDrop {
154 get {
155 return base.AllowDrop;
158 set {
159 base.AllowDrop = value;
163 [DefaultValue(false)]
164 #if NET_2_0
165 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
166 [RefreshProperties (RefreshProperties.Repaint)]
167 [EditorBrowsable (EditorBrowsableState.Never)]
168 [Browsable (false)]
169 #else
170 [Localizable(true)]
171 #endif
172 public override bool AutoSize {
173 get {
174 return auto_size;
177 set {
178 base.AutoSize = value;
182 [MonoTODO ("Value not respected, always true")]
183 [DefaultValue(false)]
184 public bool AutoWordSelection {
185 get { return auto_word_select; }
186 set { auto_word_select = value; }
189 [Browsable(false)]
190 [EditorBrowsable(EditorBrowsableState.Never)]
191 public override System.Drawing.Image BackgroundImage {
192 get { return base.BackgroundImage; }
193 set { base.BackgroundImage = value; }
196 #if NET_2_0
197 [Browsable (false)]
198 [EditorBrowsable (EditorBrowsableState.Never)]
199 public override ImageLayout BackgroundImageLayout {
200 get { return base.BackgroundImageLayout; }
201 set { base.BackgroundImageLayout = value; }
203 #endif
205 [DefaultValue(0)]
206 [Localizable(true)]
207 public int BulletIndent {
208 get {
209 return bullet_indent;
212 set {
213 bullet_indent = value;
217 [Browsable(false)]
218 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
219 public bool CanRedo {
220 get {
221 return document.undo.CanRedo;
225 [DefaultValue(true)]
226 public bool DetectUrls {
227 get { return base.EnableLinks; }
228 set { base.EnableLinks = value; }
231 #if NET_2_0
232 [MonoTODO ("Stub")]
233 [DefaultValue (false)]
234 public bool EnableAutoDragDrop {
235 get { return enable_auto_drag_drop; }
236 set { enable_auto_drag_drop = value; }
238 #endif
240 public override Font Font {
241 get {
242 return base.Font;
245 set {
246 if (font != value) {
247 Line start;
248 Line end;
250 if (auto_size) {
251 if (PreferredHeight != Height) {
252 Height = PreferredHeight;
256 base.Font = value;
258 // Font changes always set the whole doc to that font
259 start = document.GetLine(1);
260 end = document.GetLine(document.Lines);
261 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, Color.Empty, Color.Empty, FormatSpecified.Font);
266 public override Color ForeColor {
267 get {
268 return base.ForeColor;
271 set {
272 base.ForeColor = value;
276 #if NET_2_0
277 [MonoTODO ("Stub")]
278 [Browsable (false)]
279 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
280 public RichTextBoxLanguageOptions LanguageOption {
281 get { return language_option; }
282 set { language_option = value; }
284 #endif
286 [DefaultValue(Int32.MaxValue)]
287 public override int MaxLength {
288 get { return base.MaxLength; }
289 set { base.MaxLength = value; }
292 [DefaultValue(true)]
293 public override bool Multiline {
294 get {
295 return base.Multiline;
298 set {
299 base.Multiline = value;
303 [Browsable(false)]
304 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
305 [MonoTODO]
306 public string RedoActionName {
307 get {
308 return document.undo.RedoActionName;
312 #if NET_2_0
313 [MonoTODO ("Stub")]
314 [Browsable (false)]
315 [DefaultValue (true)]
316 [EditorBrowsable (EditorBrowsableState.Never)]
317 public bool RichTextShortcutsEnabled {
318 get { return rich_text_shortcuts_enabled; }
319 set { rich_text_shortcuts_enabled = value; }
321 #endif
323 [DefaultValue(0)]
324 [Localizable(true)]
325 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
326 public int RightMargin {
327 get {
328 return margin_right;
331 set {
332 margin_right = value;
336 [Browsable(false)]
337 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
338 #if NET_2_0
339 [RefreshProperties (RefreshProperties.All)]
340 #else
341 [DefaultValue("")]
342 #endif
343 public string Rtf {
344 get {
345 Line start_line;
346 Line end_line;
348 start_line = document.GetLine(1);
349 end_line = document.GetLine(document.Lines);
350 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
353 set {
354 MemoryStream data;
356 document.Empty();
357 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
359 InsertRTFFromStream(data, 0, 1);
361 data.Close();
363 Invalidate();
367 [DefaultValue(RichTextBoxScrollBars.Both)]
368 [Localizable(true)]
369 public RichTextBoxScrollBars ScrollBars {
370 get {
371 return scrollbars;
374 set {
375 if (!Enum.IsDefined (typeof (RichTextBoxScrollBars), value))
376 throw new InvalidEnumArgumentException ("value", (int) value,
377 typeof (RichTextBoxScrollBars));
379 if (value != scrollbars) {
380 scrollbars = value;
381 CalculateDocument ();
386 [Browsable(false)]
387 [DefaultValue("")]
388 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
389 public string SelectedRtf {
390 get {
391 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
394 set {
395 MemoryStream data;
396 int x;
397 int y;
398 int sel_start;
399 int chars;
400 Line line;
401 LineTag tag;
403 if (document.selection_visible) {
404 document.ReplaceSelection("", false);
407 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
409 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
410 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
411 data.Close();
413 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
414 document.SetSelection(line, sel_start);
415 document.PositionCaret(line, sel_start);
416 document.DisplayCaret();
417 ScrollToCaret();
418 OnTextChanged(EventArgs.Empty);
422 [Browsable(false)]
423 [DefaultValue("")]
424 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
425 public override string SelectedText {
426 get {
427 return base.SelectedText;
430 set {
431 base.SelectedText = value;
435 [Browsable(false)]
436 [DefaultValue(HorizontalAlignment.Left)]
437 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
438 public HorizontalAlignment SelectionAlignment {
439 get {
440 HorizontalAlignment align;
441 Line start;
442 Line end;
443 Line line;
445 start = document.ParagraphStart(document.selection_start.line);
446 align = start.alignment;
448 end = document.ParagraphEnd(document.selection_end.line);
450 line = start;
452 while (true) {
453 if (line.alignment != align) {
454 return HorizontalAlignment.Left;
457 if (line == end) {
458 break;
460 line = document.GetLine(line.line_no + 1);
463 return align;
466 set {
467 Line start;
468 Line end;
469 Line line;
471 start = document.ParagraphStart(document.selection_start.line);
473 end = document.ParagraphEnd(document.selection_end.line);
475 line = start;
477 while (true) {
478 line.alignment = value;
480 if (line == end) {
481 break;
483 line = document.GetLine(line.line_no + 1);
485 this.CalculateDocument();
489 #if NET_2_0
490 [MonoTODO ("Stub")]
491 [Browsable (false)]
492 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
493 public Color SelectionBackColor {
494 get { return selection_back_color; }
495 set { selection_back_color = value; }
497 #endif
499 [Browsable(false)]
500 [DefaultValue(false)]
501 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
502 [MonoTODO]
503 public bool SelectionBullet {
504 get {
505 return false;
508 set {
512 [Browsable(false)]
513 [DefaultValue(0)]
514 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
515 [MonoTODO]
516 public int SelectionCharOffset {
517 get {
518 return 0;
521 set {
525 [Browsable(false)]
526 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
527 public Color SelectionColor {
528 get {
529 Color color;
530 LineTag start;
531 LineTag end;
532 LineTag tag;
534 if (selection_length > 0) {
535 start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
536 end = document.selection_start.line.FindTag (document.selection_end.pos);
537 } else {
538 start = document.selection_start.line.FindTag (document.selection_start.pos);
539 end = start;
542 color = start.Color;
544 tag = start;
545 while (tag != null) {
547 if (!color.Equals (tag.Color))
548 return Color.Empty;
550 if (tag == end)
551 break;
553 tag = document.NextTag (tag);
556 return color;
559 set {
560 if (value == Color.Empty)
561 value = DefaultForeColor;
563 int sel_start;
564 int sel_end;
566 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
567 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
569 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
570 document.selection_end.line, document.selection_end.pos + 1, null,
571 value, Color.Empty, FormatSpecified.Color);
573 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
574 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
576 document.UpdateView(document.selection_start.line, 0);
578 //Re-Align the caret in case its changed size or position
579 //probably not necessary here
580 document.AlignCaret(false);
584 [Browsable(false)]
585 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
586 public Font SelectionFont {
587 get {
588 Font font;
589 LineTag start;
590 LineTag end;
591 LineTag tag;
593 if (selection_length > 0) {
594 start = document.selection_start.line.FindTag (document.selection_start.pos + 1);
595 end = document.selection_start.line.FindTag (document.selection_end.pos);
596 } else {
597 start = document.selection_start.line.FindTag (document.selection_start.pos);
598 end = start;
601 font = start.Font;
603 if (selection_length > 1) {
604 tag = start;
605 while (tag != null) {
607 if (!font.Equals(tag.Font))
608 return null;
610 if (tag == end)
611 break;
613 tag = document.NextTag (tag);
617 return font;
620 set {
621 int sel_start;
622 int sel_end;
624 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
625 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
627 document.FormatText (document.selection_start.line, document.selection_start.pos + 1,
628 document.selection_end.line, document.selection_end.pos + 1, value,
629 Color.Empty, Color.Empty, FormatSpecified.Font);
631 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
632 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
634 document.UpdateView(document.selection_start.line, 0);
635 //Re-Align the caret in case its changed size or position
636 Document.AlignCaret (false);
641 [Browsable(false)]
642 [DefaultValue(0)]
643 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
644 [MonoTODO]
645 public int SelectionHangingIndent {
646 get {
647 return 0;
650 set {
654 [Browsable(false)]
655 [DefaultValue(0)]
656 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
657 [MonoTODO]
658 public int SelectionIndent {
659 get {
660 return 0;
663 set {
667 [Browsable(false)]
668 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
669 public override int SelectionLength {
670 get {
671 return base.SelectionLength;
674 set {
675 base.SelectionLength = value;
679 [Browsable(false)]
680 [DefaultValue(false)]
681 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
682 [MonoTODO]
683 public bool SelectionProtected {
684 get {
685 return false;
688 set {
692 [Browsable(false)]
693 [DefaultValue(0)]
694 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
695 [MonoTODO]
696 public int SelectionRightIndent {
697 get {
698 return 0;
701 set {
705 [Browsable(false)]
706 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
707 [MonoTODO]
708 public int[] SelectionTabs {
709 get {
710 return new int[0];
713 set {
717 [Browsable(false)]
718 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
719 public RichTextBoxSelectionTypes SelectionType {
720 get {
721 if (document.selection_start == document.selection_end) {
722 return RichTextBoxSelectionTypes.Empty;
725 // Lazy, but works
726 if (SelectedText.Length > 1) {
727 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
730 return RichTextBoxSelectionTypes.Text;
734 [DefaultValue(false)]
735 [MonoTODO]
736 public bool ShowSelectionMargin {
737 get {
738 return false;
741 set {
745 [Localizable(true)]
746 #if NET_2_0
747 [RefreshProperties (RefreshProperties.All)]
748 #endif
749 public override string Text {
750 get {
751 return base.Text;
754 set {
755 base.Text = value;
759 [Browsable(false)]
760 public override int TextLength {
761 get {
762 return base.TextLength;
766 [Browsable(false)]
767 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
768 public string UndoActionName {
769 get {
770 return document.undo.UndoActionName;
774 [Localizable(true)]
775 [DefaultValue(1)]
776 public float ZoomFactor {
777 get {
778 return zoom;
781 set {
782 zoom = value;
785 #endregion // Public Instance Properties
787 #region Protected Instance Properties
788 protected override CreateParams CreateParams {
789 get {
790 return base.CreateParams;
794 protected override Size DefaultSize {
795 get {
796 return new Size(100, 96);
799 #endregion // Protected Instance Properties
801 #region Public Instance Methods
802 public bool CanPaste(DataFormats.Format clipFormat) {
803 if ((clipFormat.Name == DataFormats.Rtf) ||
804 (clipFormat.Name == DataFormats.Text) ||
805 (clipFormat.Name == DataFormats.UnicodeText)) {
806 return true;
808 return false;
811 public int Find(char[] characterSet) {
812 return Find(characterSet, -1, -1);
815 public int Find(char[] characterSet, int start) {
816 return Find(characterSet, start, -1);
819 public int Find(char[] characterSet, int start, int end) {
820 Document.Marker start_mark;
821 Document.Marker end_mark;
822 Document.Marker result;
824 if (start == -1) {
825 document.GetMarker(out start_mark, true);
826 } else {
827 Line line;
828 LineTag tag;
829 int pos;
831 start_mark = new Document.Marker();
833 document.CharIndexToLineTag(start, out line, out tag, out pos);
834 start_mark.line = line;
835 start_mark.tag = tag;
836 start_mark.pos = pos;
839 if (end == -1) {
840 document.GetMarker(out end_mark, false);
841 } else {
842 Line line;
843 LineTag tag;
844 int pos;
846 end_mark = new Document.Marker();
848 document.CharIndexToLineTag(end, out line, out tag, out pos);
849 end_mark.line = line;
850 end_mark.tag = tag;
851 end_mark.pos = pos;
854 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
855 return document.LineTagToCharIndex(result.line, result.pos);
858 return -1;
861 public int Find(string str) {
862 return Find(str, -1, -1, RichTextBoxFinds.None);
865 public int Find(string str, int start, int end, RichTextBoxFinds options) {
866 Document.Marker start_mark;
867 Document.Marker end_mark;
868 Document.Marker result;
870 if (start == -1) {
871 document.GetMarker(out start_mark, true);
872 } else {
873 Line line;
874 LineTag tag;
875 int pos;
877 start_mark = new Document.Marker();
879 document.CharIndexToLineTag(start, out line, out tag, out pos);
881 start_mark.line = line;
882 start_mark.tag = tag;
883 start_mark.pos = pos;
886 if (end == -1) {
887 document.GetMarker(out end_mark, false);
888 } else {
889 Line line;
890 LineTag tag;
891 int pos;
893 end_mark = new Document.Marker();
895 document.CharIndexToLineTag(end, out line, out tag, out pos);
897 end_mark.line = line;
898 end_mark.tag = tag;
899 end_mark.pos = pos;
902 if (document.Find(str, start_mark, end_mark, out result, options)) {
903 return document.LineTagToCharIndex(result.line, result.pos);
906 return -1;
909 public int Find(string str, int start, RichTextBoxFinds options) {
910 return Find(str, start, -1, options);
913 public int Find(string str, RichTextBoxFinds options) {
914 return Find(str, -1, -1, options);
918 #if !NET_2_0
919 public char GetCharFromPosition(Point pt) {
920 LineTag tag;
921 int pos;
923 PointToTagPos(pt, out tag, out pos);
925 if (pos >= tag.Line.text.Length) {
926 return '\n';
929 return tag.Line.text[pos];
931 #else
932 internal override char GetCharFromPositionInternal (Point p)
934 LineTag tag;
935 int pos;
937 PointToTagPos (p, out tag, out pos);
939 if (pos >= tag.Line.text.Length)
940 return '\n';
942 return tag.Line.text[pos];
944 #endif
946 public
947 #if NET_2_0
948 override
949 #endif
950 int GetCharIndexFromPosition(Point pt) {
951 LineTag tag;
952 int pos;
954 PointToTagPos(pt, out tag, out pos);
956 return document.LineTagToCharIndex(tag.Line, pos);
959 public
960 #if NET_2_0
961 override
962 #endif
963 int GetLineFromCharIndex(int index) {
964 Line line;
965 LineTag tag;
966 int pos;
968 document.CharIndexToLineTag(index, out line, out tag, out pos);
970 return line.LineNo - 1;
973 public
974 #if NET_2_0
975 override
976 #endif
977 Point GetPositionFromCharIndex(int index) {
978 Line line;
979 LineTag tag;
980 int pos;
982 document.CharIndexToLineTag(index, out line, out tag, out pos);
983 return new Point(line.X + (int)line.widths[pos] + document.OffsetX - document.ViewPortX,
984 line.Y + document.OffsetY - document.ViewPortY);
987 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
988 document.Empty();
991 // FIXME - ignoring unicode
992 if (fileType == RichTextBoxStreamType.PlainText) {
993 StringBuilder sb;
994 char[] buffer;
996 try {
997 sb = new StringBuilder ((int) data.Length);
998 buffer = new char [1024];
999 } catch {
1000 throw new IOException("Not enough memory to load document");
1003 StreamReader sr = new StreamReader (data, Encoding.Default, true);
1004 int charsRead = sr.Read (buffer, 0, buffer.Length);
1005 while (charsRead > 0) {
1006 sb.Append (buffer, 0, charsRead);
1007 charsRead = sr.Read (buffer, 0, buffer.Length);
1009 base.Text = sb.ToString();
1010 return;
1013 InsertRTFFromStream(data, 0, 1);
1015 document.PositionCaret (document.GetLine (1), 0);
1016 document.SetSelectionToCaret (true);
1017 ScrollToCaret ();
1020 [MonoTODO("Make smarter RTF detection?")]
1021 public void LoadFile(string path) {
1022 if (path.EndsWith(".rtf")) {
1023 LoadFile(path, RichTextBoxStreamType.RichText);
1024 } else {
1025 LoadFile(path, RichTextBoxStreamType.PlainText);
1029 public void LoadFile(string path, RichTextBoxStreamType fileType) {
1030 FileStream data;
1032 data = null;
1035 try {
1036 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
1038 LoadFile(data, fileType);
1040 #if !DEBUG
1041 catch (Exception ex) {
1042 throw new IOException("Could not open file " + path, ex);
1044 #endif
1045 finally {
1046 if (data != null) {
1047 data.Close();
1052 public void Paste(DataFormats.Format clipFormat) {
1053 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
1056 public void Redo()
1058 if (document.undo.Redo ())
1059 OnTextChanged (EventArgs.Empty);
1062 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
1063 Encoding encoding;
1064 int i;
1065 Byte[] bytes;
1068 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
1069 encoding = Encoding.Unicode;
1070 } else {
1071 encoding = Encoding.ASCII;
1074 switch(fileType) {
1075 case RichTextBoxStreamType.PlainText:
1076 case RichTextBoxStreamType.TextTextOleObjs:
1077 case RichTextBoxStreamType.UnicodePlainText: {
1078 if (!Multiline) {
1079 bytes = encoding.GetBytes(document.Root.text.ToString());
1080 data.Write(bytes, 0, bytes.Length);
1081 return;
1084 for (i = 1; i < document.Lines; i++) {
1085 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
1086 data.Write(bytes, 0, bytes.Length);
1088 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
1089 data.Write(bytes, 0, bytes.Length);
1090 return;
1094 // If we're here we're saving RTF
1095 Line start_line;
1096 Line end_line;
1097 StringBuilder rtf;
1098 int current;
1099 int total;
1101 start_line = document.GetLine(1);
1102 end_line = document.GetLine(document.Lines);
1103 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
1104 total = rtf.Length;
1105 bytes = new Byte[4096];
1107 // Let's chunk it so we don't use up all memory...
1108 for (i = 0; i < total; i += 1024) {
1109 if ((i + 1024) < total) {
1110 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
1111 } else {
1112 current = total - i;
1113 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
1115 data.Write(bytes, 0, current);
1119 public void SaveFile(string path) {
1120 if (path.EndsWith(".rtf")) {
1121 SaveFile(path, RichTextBoxStreamType.RichText);
1122 } else {
1123 SaveFile(path, RichTextBoxStreamType.PlainText);
1127 public void SaveFile(string path, RichTextBoxStreamType fileType) {
1128 FileStream data;
1130 data = null;
1132 // try {
1133 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
1134 SaveFile(data, fileType);
1135 // }
1137 // catch {
1138 // throw new IOException("Could not write document to file " + path);
1139 // }
1141 // finally {
1142 if (data != null) {
1143 data.Close();
1145 // }
1148 #if NET_2_0
1149 [EditorBrowsable (EditorBrowsableState.Never)]
1150 public new void DrawToBitmap (Bitmap bitmap, Rectangle targetBounds)
1152 Graphics dc = Graphics.FromImage (bitmap);
1154 Draw (dc, targetBounds);
1156 #endif
1158 #endregion // Public Instance Methods
1160 #region Protected Instance Methods
1161 protected virtual object CreateRichEditOleCallback() {
1162 throw new NotImplementedException();
1165 protected override void OnBackColorChanged(EventArgs e) {
1166 base.OnBackColorChanged (e);
1169 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1170 ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
1171 if (eh != null)
1172 eh (this, e);
1175 protected override void OnContextMenuChanged(EventArgs e) {
1176 base.OnContextMenuChanged (e);
1179 protected override void OnHandleCreated(EventArgs e) {
1180 base.OnHandleCreated (e);
1183 protected override void OnHandleDestroyed(EventArgs e) {
1184 base.OnHandleDestroyed (e);
1187 protected virtual void OnHScroll(EventArgs e) {
1188 EventHandler eh = (EventHandler)(Events [HScrollEvent]);
1189 if (eh != null)
1190 eh (this, e);
1193 [MonoTODO("Determine when to call this")]
1194 protected virtual void OnImeChange(EventArgs e) {
1195 EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
1196 if (eh != null)
1197 eh (this, e);
1200 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1201 LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
1202 if (eh != null)
1203 eh (this, e);
1206 protected virtual void OnProtected(EventArgs e) {
1207 EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
1208 if (eh != null)
1209 eh (this, e);
1212 protected override void OnRightToLeftChanged(EventArgs e) {
1213 base.OnRightToLeftChanged (e);
1216 protected virtual void OnSelectionChanged(EventArgs e) {
1217 EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
1218 if (eh != null)
1219 eh (this, e);
1222 #if !NET_2_0
1223 protected override void OnSystemColorsChanged(EventArgs e) {
1224 base.OnSystemColorsChanged (e);
1227 protected override void OnTextChanged(EventArgs e) {
1228 base.OnTextChanged (e);
1230 #endif
1232 protected virtual void OnVScroll(EventArgs e) {
1233 EventHandler eh = (EventHandler)(Events [VScrollEvent]);
1234 if (eh != null)
1235 eh (this, e);
1238 protected override void WndProc(ref Message m) {
1239 base.WndProc (ref m);
1242 #if NET_2_0
1243 protected override bool ProcessCmdKey (ref Message m, Keys keyData)
1245 return base.ProcessCmdKey (ref m, keyData);
1247 #endif
1248 #endregion // Protected Instance Methods
1250 #region Events
1251 static object ContentsResizedEvent = new object ();
1252 static object HScrollEvent = new object ();
1253 static object ImeChangeEvent = new object ();
1254 static object LinkClickedEvent = new object ();
1255 static object ProtectedEvent = new object ();
1256 static object SelectionChangedEvent = new object ();
1257 static object VScrollEvent = new object ();
1259 [Browsable(false)]
1260 [EditorBrowsable(EditorBrowsableState.Never)]
1261 public new event EventHandler BackgroundImageChanged {
1262 add { base.BackgroundImageChanged += value; }
1263 remove { base.BackgroundImageChanged -= value; }
1266 #if NET_2_0
1267 [Browsable (false)]
1268 [EditorBrowsable (EditorBrowsableState.Never)]
1269 public new event EventHandler BackgroundImageLayoutChanged {
1270 add { base.BackgroundImageLayoutChanged += value; }
1271 remove { base.BackgroundImageLayoutChanged -= value; }
1273 #endif
1275 public event ContentsResizedEventHandler ContentsResized {
1276 add { Events.AddHandler (ContentsResizedEvent, value); }
1277 remove { Events.RemoveHandler (ContentsResizedEvent, value); }
1280 #if !NET_2_0
1281 [Browsable(false)]
1282 [EditorBrowsable(EditorBrowsableState.Never)]
1283 public new event EventHandler DoubleClick {
1284 add { base.DoubleClick += value; }
1285 remove { base.DoubleClick -= value; }
1287 #endif
1289 [Browsable(false)]
1290 #if !NET_2_0
1291 [EditorBrowsable(EditorBrowsableState.Never)]
1292 #endif
1293 public new event DragEventHandler DragDrop {
1294 add { base.DragDrop += value; }
1295 remove { base.DragDrop -= value; }
1298 [Browsable(false)]
1299 #if !NET_2_0
1300 [EditorBrowsable(EditorBrowsableState.Never)]
1301 #endif
1302 public new event DragEventHandler DragEnter {
1303 add { base.DragEnter += value; }
1304 remove { base.DragEnter -= value; }
1307 [Browsable(false)]
1308 [EditorBrowsable(EditorBrowsableState.Never)]
1309 public new event EventHandler DragLeave {
1310 add { base.DragLeave += value; }
1311 remove { base.DragLeave -= value; }
1315 [Browsable(false)]
1316 [EditorBrowsable(EditorBrowsableState.Never)]
1317 public new event DragEventHandler DragOver {
1318 add { base.DragOver += value; }
1319 remove { base.DragOver -= value; }
1323 [Browsable(false)]
1324 [EditorBrowsable(EditorBrowsableState.Never)]
1325 public new event GiveFeedbackEventHandler GiveFeedback {
1326 add { base.GiveFeedback += value; }
1327 remove { base.GiveFeedback -= value; }
1330 public event EventHandler HScroll {
1331 add { Events.AddHandler (HScrollEvent, value); }
1332 remove { Events.RemoveHandler (HScrollEvent, value); }
1335 public event EventHandler ImeChange {
1336 add { Events.AddHandler (ImeChangeEvent, value); }
1337 remove { Events.RemoveHandler (ImeChangeEvent, value); }
1340 public event LinkClickedEventHandler LinkClicked {
1341 add { Events.AddHandler (LinkClickedEvent, value); }
1342 remove { Events.RemoveHandler (LinkClickedEvent, value); }
1345 public event EventHandler Protected {
1346 add { Events.AddHandler (ProtectedEvent, value); }
1347 remove { Events.RemoveHandler (ProtectedEvent, value); }
1350 [Browsable(false)]
1351 [EditorBrowsable(EditorBrowsableState.Never)]
1352 public new event QueryContinueDragEventHandler QueryContinueDrag {
1353 add { base.QueryContinueDrag += value; }
1354 remove { base.QueryContinueDrag -= value; }
1357 [MonoTODO("Currently does not ever fire")]
1358 public event EventHandler SelectionChanged {
1359 add { Events.AddHandler (SelectionChangedEvent, value); }
1360 remove { Events.RemoveHandler (SelectionChangedEvent, value); }
1363 public event EventHandler VScroll {
1364 add { Events.AddHandler (VScrollEvent, value); }
1365 remove { Events.RemoveHandler (VScrollEvent, value); }
1367 #endregion // Events
1369 #region Private Methods
1371 internal override void SelectWord ()
1373 document.ExpandSelection(CaretSelection.Word, false);
1376 private class RtfSectionStyle : ICloneable {
1377 internal Color rtf_color;
1378 internal RTF.Font rtf_rtffont;
1379 internal int rtf_rtffont_size;
1380 internal FontStyle rtf_rtfstyle;
1381 internal HorizontalAlignment rtf_rtfalign;
1382 internal int rtf_par_line_left_indent;
1383 internal bool rtf_visible;
1384 internal int rtf_skip_width;
1386 public object Clone ()
1388 RtfSectionStyle new_style = new RtfSectionStyle ();
1390 new_style.rtf_color = rtf_color;
1391 new_style.rtf_par_line_left_indent = rtf_par_line_left_indent;
1392 new_style.rtf_rtfalign = rtf_rtfalign;
1393 new_style.rtf_rtffont = rtf_rtffont;
1394 new_style.rtf_rtffont_size = rtf_rtffont_size;
1395 new_style.rtf_rtfstyle = rtf_rtfstyle;
1396 new_style.rtf_visible = rtf_visible;
1397 new_style.rtf_skip_width = rtf_skip_width;
1399 return new_style;
1403 // To allow us to keep track of the sections and revert formatting
1404 // as we go in and out of sections of the document.
1405 private void HandleGroup (RTF.RTF rtf)
1407 //start group - save the current formatting on to a stack
1408 //end group - go back to the formatting at the current group
1409 if (rtf_section_stack == null) {
1410 rtf_section_stack = new Stack ();
1413 if (rtf.Major == RTF.Major.BeginGroup) {
1414 rtf_section_stack.Push (rtf_style.Clone ());
1415 //spec specifies resetting unicode ignore at begin group as an attempt at error
1416 //recovery.
1417 rtf_skip_count = 0;
1418 } else if (rtf.Major == RTF.Major.EndGroup) {
1419 if (rtf_section_stack.Count > 0) {
1420 FlushText (rtf, false);
1422 rtf_style = (RtfSectionStyle) rtf_section_stack.Pop ();
1427 [MonoTODO("Add QuadJust support for justified alignment")]
1428 private void HandleControl(RTF.RTF rtf) {
1429 switch(rtf.Major) {
1430 case RTF.Major.Unicode: {
1431 switch(rtf.Minor) {
1432 case RTF.Minor.UnicodeCharBytes: {
1433 rtf_style.rtf_skip_width = rtf.Param;
1434 break;
1437 case RTF.Minor.UnicodeChar: {
1438 FlushText (rtf, false);
1439 rtf_skip_count += rtf_style.rtf_skip_width;
1440 rtf_line.Append((char)rtf.Param);
1441 break;
1444 break;
1447 case RTF.Major.Destination: {
1448 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1449 rtf.SkipGroup();
1450 break;
1453 case RTF.Major.PictAttr:
1454 if (rtf.Picture != null && rtf.Picture.IsValid ()) {
1455 Line line = document.GetLine (rtf_cursor_y);
1456 document.InsertPicture (line, 0, rtf.Picture);
1457 rtf_cursor_x++;
1459 FlushText (rtf, true);
1460 rtf.Picture = null;
1462 break;
1464 case RTF.Major.CharAttr: {
1465 switch(rtf.Minor) {
1466 case RTF.Minor.ForeColor: {
1467 System.Windows.Forms.RTF.Color color;
1469 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1471 if (color != null) {
1472 FlushText(rtf, false);
1473 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1474 this.rtf_style.rtf_color = ForeColor;
1475 } else {
1476 this.rtf_style.rtf_color = Color.FromArgb(color.Red, color.Green, color.Blue);
1478 FlushText (rtf, false);
1480 break;
1483 case RTF.Minor.FontSize: {
1484 FlushText(rtf, false);
1485 this.rtf_style.rtf_rtffont_size = rtf.Param / 2;
1486 break;
1489 case RTF.Minor.FontNum: {
1490 System.Windows.Forms.RTF.Font font;
1492 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1493 if (font != null) {
1494 FlushText(rtf, false);
1495 this.rtf_style.rtf_rtffont = font;
1497 break;
1500 case RTF.Minor.Plain: {
1501 FlushText(rtf, false);
1502 rtf_style.rtf_rtfstyle = FontStyle.Regular;
1503 break;
1506 case RTF.Minor.Bold: {
1507 FlushText(rtf, false);
1508 if (rtf.Param == RTF.RTF.NoParam) {
1509 rtf_style.rtf_rtfstyle |= FontStyle.Bold;
1510 } else {
1511 rtf_style.rtf_rtfstyle &= ~FontStyle.Bold;
1513 break;
1516 case RTF.Minor.Italic: {
1517 FlushText(rtf, false);
1518 if (rtf.Param == RTF.RTF.NoParam) {
1519 rtf_style.rtf_rtfstyle |= FontStyle.Italic;
1520 } else {
1521 rtf_style.rtf_rtfstyle &= ~FontStyle.Italic;
1523 break;
1526 case RTF.Minor.StrikeThru: {
1527 FlushText(rtf, false);
1528 if (rtf.Param == RTF.RTF.NoParam) {
1529 rtf_style.rtf_rtfstyle |= FontStyle.Strikeout;
1530 } else {
1531 rtf_style.rtf_rtfstyle &= ~FontStyle.Strikeout;
1533 break;
1536 case RTF.Minor.Underline: {
1537 FlushText(rtf, false);
1538 if (rtf.Param == RTF.RTF.NoParam) {
1539 rtf_style.rtf_rtfstyle |= FontStyle.Underline;
1540 } else {
1541 rtf_style.rtf_rtfstyle = rtf_style.rtf_rtfstyle & ~FontStyle.Underline;
1543 break;
1546 case RTF.Minor.Invisible: {
1547 FlushText (rtf, false);
1548 rtf_style.rtf_visible = false;
1549 break;
1552 case RTF.Minor.NoUnderline: {
1553 FlushText(rtf, false);
1554 rtf_style.rtf_rtfstyle &= ~FontStyle.Underline;
1555 break;
1558 break;
1561 case RTF.Major.ParAttr: {
1562 switch (rtf.Minor) {
1564 case RTF.Minor.ParDef:
1565 FlushText (rtf, false);
1566 rtf_style.rtf_par_line_left_indent = 0;
1567 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1568 break;
1570 case RTF.Minor.LeftIndent:
1571 rtf_style.rtf_par_line_left_indent = (int) (((float) rtf.Param / 1440.0F) * CreateGraphics ().DpiX + 0.5F);
1572 break;
1574 case RTF.Minor.QuadCenter:
1575 FlushText (rtf, false);
1576 rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
1577 break;
1579 case RTF.Minor.QuadJust:
1580 FlushText (rtf, false);
1581 rtf_style.rtf_rtfalign = HorizontalAlignment.Center;
1582 break;
1584 case RTF.Minor.QuadLeft:
1585 FlushText (rtf, false);
1586 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1587 break;
1589 case RTF.Minor.QuadRight:
1590 FlushText (rtf, false);
1591 rtf_style.rtf_rtfalign = HorizontalAlignment.Right;
1592 break;
1594 break;
1597 case RTF.Major.SpecialChar: {
1598 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1599 SpecialChar (rtf);
1600 break;
1605 private void SpecialChar(RTF.RTF rtf) {
1606 switch(rtf.Minor) {
1607 case RTF.Minor.Page:
1608 case RTF.Minor.Sect:
1609 case RTF.Minor.Row:
1610 case RTF.Minor.Line:
1611 case RTF.Minor.Par: {
1612 FlushText(rtf, true);
1613 break;
1616 case RTF.Minor.Cell: {
1617 Console.Write(" ");
1618 break;
1621 case RTF.Minor.NoBrkSpace: {
1622 Console.Write(" ");
1623 break;
1626 case RTF.Minor.Tab: {
1627 rtf_line.Append ("\t");
1628 // FlushText (rtf, false);
1629 break;
1632 case RTF.Minor.NoReqHyphen:
1633 case RTF.Minor.NoBrkHyphen: {
1634 rtf_line.Append ("-");
1635 // FlushText (rtf, false);
1636 break;
1639 case RTF.Minor.Bullet: {
1640 Console.WriteLine("*");
1641 break;
1644 case RTF.Minor.WidowCtrl:
1645 break;
1647 case RTF.Minor.EmDash: {
1648 rtf_line.Append ("\u2014");
1649 break;
1652 case RTF.Minor.EnDash: {
1653 rtf_line.Append ("\u2013");
1654 break;
1657 case RTF.Minor.LQuote: {
1658 Console.Write("\u2018");
1659 break;
1662 case RTF.Minor.RQuote: {
1663 Console.Write("\u2019");
1664 break;
1667 case RTF.Minor.LDblQuote: {
1668 Console.Write("\u201C");
1669 break;
1672 case RTF.Minor.RDblQuote: {
1673 Console.Write("\u201D");
1674 break;
1677 default: {
1678 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1679 // rtf.SkipGroup();
1680 break;
1685 private void HandleText(RTF.RTF rtf) {
1686 string str = rtf.EncodedText;
1688 //todo - simplistically skips characters, should skip bytes?
1689 if (rtf_skip_count > 0 && str.Length > 0) {
1690 int iToRemove = Math.Min (rtf_skip_count, str.Length);
1692 str = str.Substring (iToRemove);
1693 rtf_skip_count-=iToRemove;
1697 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1698 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1699 } else {
1700 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1701 rtf_line.Append((char)rtf.Major);
1702 } else {
1703 //rtf_line.Append((char)rtf.Major);
1704 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1709 if (rtf_style.rtf_visible)
1710 rtf_line.Append (str);
1713 private void FlushText(RTF.RTF rtf, bool newline) {
1714 int length;
1715 Font font;
1717 length = rtf_line.Length;
1718 if (!newline && (length == 0)) {
1719 return;
1722 if (rtf_style.rtf_rtffont == null) {
1723 // First font in table is default
1724 rtf_style.rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont (rtf, 0);
1727 font = new Font (rtf_style.rtf_rtffont.Name, rtf_style.rtf_rtffont_size, rtf_style.rtf_rtfstyle);
1729 if (rtf_style.rtf_color == Color.Empty) {
1730 System.Windows.Forms.RTF.Color color;
1732 // First color in table is default
1733 color = System.Windows.Forms.RTF.Color.GetColor (rtf, 0);
1735 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1736 rtf_style.rtf_color = ForeColor;
1737 } else {
1738 rtf_style.rtf_color = Color.FromArgb (color.Red, color.Green, color.Blue);
1743 rtf_chars += rtf_line.Length;
1747 if (rtf_cursor_x == 0) {
1748 if (newline && rtf_line.ToString ().EndsWith ("\n") == false)
1749 rtf_line.Append ("\n");
1751 document.Add (rtf_cursor_y, rtf_line.ToString (), rtf_style.rtf_rtfalign, font, rtf_style.rtf_color,
1752 newline ? LineEnding.Rich : LineEnding.Wrap);
1753 if (rtf_style.rtf_par_line_left_indent != 0) {
1754 Line line = document.GetLine (rtf_cursor_y);
1755 line.indent = rtf_style.rtf_par_line_left_indent;
1757 } else {
1758 Line line;
1760 line = document.GetLine (rtf_cursor_y);
1761 line.indent = rtf_style.rtf_par_line_left_indent;
1762 if (rtf_line.Length > 0) {
1763 document.InsertString (line, rtf_cursor_x, rtf_line.ToString ());
1764 document.FormatText (line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length,
1765 font, rtf_style.rtf_color, Color.Empty,
1766 FormatSpecified.Font | FormatSpecified.Color);
1768 if (newline) {
1769 document.Split (line, rtf_cursor_x + length);
1770 line = document.GetLine (rtf_cursor_y);
1771 line.ending = LineEnding.Rich;
1773 if (line.Text.EndsWith ("\n") == false)
1774 line.Text += "\n";
1778 if (newline) {
1779 rtf_cursor_x = 0;
1780 rtf_cursor_y++;
1781 } else {
1782 rtf_cursor_x += length;
1784 rtf_line.Length = 0; // Empty line
1787 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1788 int x;
1789 int y;
1790 int chars;
1792 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1795 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1796 RTF.RTF rtf;
1798 rtf = new RTF.RTF(data);
1800 // Prepare
1801 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1802 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1803 rtf.ClassCallback[RTF.TokenClass.Group] = new RTF.ClassDelegate(HandleGroup);
1805 rtf_skip_count = 0;
1806 rtf_line = new StringBuilder();
1807 rtf_style.rtf_color = Color.Empty;
1808 rtf_style.rtf_rtffont_size = (int)this.Font.Size;
1809 rtf_style.rtf_rtfalign = HorizontalAlignment.Left;
1810 rtf_style.rtf_rtfstyle = FontStyle.Regular;
1811 rtf_style.rtf_rtffont = null;
1812 rtf_style.rtf_visible = true;
1813 rtf_style.rtf_skip_width = 1;
1814 rtf_cursor_x = cursor_x;
1815 rtf_cursor_y = cursor_y;
1816 rtf_chars = 0;
1817 rtf.DefaultFont(this.Font.Name);
1819 rtf_text_map = new RTF.TextMap();
1820 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1822 document.SuspendRecalc ();
1824 try {
1825 rtf.Read(); // That's it
1826 FlushText(rtf, false);
1831 catch (RTF.RTFException e) {
1832 #if DEBUG
1833 throw e;
1834 #endif
1835 // Seems to be plain text or broken RTF
1836 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1839 to_x = rtf_cursor_x;
1840 to_y = rtf_cursor_y;
1841 chars = rtf_chars;
1843 // clear the section stack if it was used
1844 if (rtf_section_stack != null)
1845 rtf_section_stack.Clear();
1847 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1848 document.ResumeRecalc (true);
1850 document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1853 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1854 OnHScroll(e);
1857 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1858 OnVScroll(e);
1861 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1862 Point p;
1864 p = pt;
1866 if (p.X >= document.ViewPortWidth) {
1867 p.X = document.ViewPortWidth - 1;
1868 } else if (p.X < 0) {
1869 p.X = 0;
1872 if (p.Y >= document.ViewPortHeight) {
1873 p.Y = document.ViewPortHeight - 1;
1874 } else if (p.Y < 0) {
1875 p.Y = 0;
1878 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1881 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1882 if (prev_index != font_index) {
1883 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1886 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1887 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1890 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1891 if (font.Bold) {
1892 rtf.Append("\\b");
1893 } else {
1894 if (prev_font != null) {
1895 rtf.Append("\\b0");
1900 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1901 if (font.Italic) {
1902 rtf.Append("\\i");
1903 } else {
1904 if (prev_font != null) {
1905 rtf.Append("\\i0");
1910 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1911 if (font.Strikeout) {
1912 rtf.Append("\\strike");
1913 } else {
1914 if (prev_font != null) {
1915 rtf.Append("\\strike0");
1920 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1921 if (font.Underline) {
1922 rtf.Append("\\ul");
1923 } else {
1924 if (prev_font != null) {
1925 rtf.Append("\\ul0");
1931 [MonoTODO("Emit unicode and other special characters properly")]
1932 private void EmitRTFText(StringBuilder rtf, string text) {
1933 rtf.Append(text);
1936 // start_pos and end_pos are 0-based
1937 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1938 StringBuilder sb;
1939 ArrayList fonts;
1940 ArrayList colors;
1941 Color color;
1942 Font font;
1943 Line line;
1944 LineTag tag;
1945 int pos;
1946 int line_no;
1947 int line_len;
1948 int i;
1949 int length;
1951 sb = new StringBuilder();
1952 fonts = new ArrayList(10);
1953 colors = new ArrayList(10);
1955 // Two runs, first we parse to determine tables;
1956 // and unlike most of our processing here we work on tags
1958 line = start_line;
1959 line_no = start_line.line_no;
1960 pos = start_pos;
1962 // Add default font and color; to optimize document content we don't
1963 // use this.Font and this.ForeColor but the font/color from the first tag
1964 tag = LineTag.FindTag(start_line, pos);
1965 font = tag.Font;
1966 color = tag.Color;
1967 fonts.Add(font.Name);
1968 colors.Add(color);
1970 while (line_no <= end_line.line_no) {
1971 line = document.GetLine(line_no);
1972 tag = LineTag.FindTag(line, pos);
1974 if (line_no != end_line.line_no) {
1975 line_len = line.text.Length;
1976 } else {
1977 line_len = end_pos;
1980 while (pos < line_len) {
1981 if (tag.Font.Name != font.Name) {
1982 font = tag.Font;
1983 if (!fonts.Contains(font.Name)) {
1984 fonts.Add(font.Name);
1988 if (tag.Color != color) {
1989 color = tag.Color;
1990 if (!colors.Contains(color)) {
1991 colors.Add(color);
1995 pos = tag.Start + tag.Length - 1;
1996 tag = tag.Next;
1998 pos = 0;
1999 line_no++;
2002 // We have the tables, emit the header
2003 sb.Append("{\\rtf1\\ansi");
2004 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
2006 // Default Font
2007 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
2009 // Default Language
2010 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
2012 // Emit the font table
2013 sb.Append("{\\fonttbl");
2014 for (i = 0; i < fonts.Count; i++) {
2015 sb.Append(String.Format("{{\\f{0}", i)); // {Font
2016 sb.Append("\\fnil"); // Family
2017 sb.Append("\\fcharset0 "); // Charset ANSI<space>
2018 sb.Append((string)fonts[i]); // Font name
2019 sb.Append(";}"); // }
2021 sb.Append("}\n");
2023 // Emit the color table (if needed)
2024 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))) {
2025 sb.Append("{\\colortbl "); // Header and NO! default color
2026 for (i = 0; i < colors.Count; i++) {
2027 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
2028 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
2029 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
2030 sb.Append(";");
2032 sb.Append("}\n");
2035 sb.Append("{\\*\\generator Mono RichTextBox;}");
2036 // Emit initial paragraph settings
2037 tag = LineTag.FindTag(start_line, start_pos);
2038 sb.Append("\\pard"); // Reset to default paragraph properties
2039 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.Font.Name), null, tag.Font); // Font properties
2040 sb.Append(" "); // Space separator
2042 font = tag.Font;
2043 color = (Color)colors[0];
2044 line = start_line;
2045 line_no = start_line.line_no;
2046 pos = start_pos;
2048 while (line_no <= end_line.line_no) {
2049 line = document.GetLine(line_no);
2050 tag = LineTag.FindTag(line, pos);
2052 if (line_no != end_line.line_no) {
2053 line_len = line.text.Length;
2054 } else {
2055 line_len = end_pos;
2058 while (pos < line_len) {
2059 length = sb.Length;
2061 if (tag.Font != font) {
2062 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.Font.Name), font, tag.Font);
2063 font = tag.Font;
2066 if (tag.Color != color) {
2067 color = tag.Color;
2068 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
2070 if (length != sb.Length) {
2071 sb.Append(" "); // Emit space to separate keywords from text
2074 // Emit the string itself
2075 if (line_no != end_line.line_no) {
2076 EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
2077 } else {
2078 if (end_pos < (tag.Start + tag.Length - 1)) {
2079 // Emit partial tag only, end_pos is inside this tag
2080 EmitRTFText(sb, tag.Line.text.ToString(pos, end_pos - pos));
2081 } else {
2082 EmitRTFText(sb, tag.Line.text.ToString(pos, tag.Start + tag.Length - pos - 1));
2086 pos = tag.Start + tag.Length - 1;
2087 tag = tag.Next;
2089 if (pos >= line.text.Length) {
2090 if (line.ending != LineEnding.Wrap) {
2091 sb.Append("\\par\n");
2094 pos = 0;
2095 line_no++;
2098 sb.Append("}\n");
2100 return sb;
2102 #endregion // Private Methods