* RichTextBox.cs: Handle some special chars better, and don't
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / RichTextBox.cs
blob22e9d7da1085cb7e427980866e4521115019bec4
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 // NOT COMPLETE
29 using System;
30 using System.Collections;
31 using System.ComponentModel;
32 using System.Drawing;
33 using System.IO;
34 using System.Text;
35 using RTF=System.Windows.Forms.RTF;
37 namespace System.Windows.Forms {
38 public class RichTextBox : TextBoxBase {
39 #region Local Variables
40 internal bool auto_word_select;
41 internal int bullet_indent;
42 internal bool can_redo;
43 internal bool detect_urls;
44 internal string redo_action_name;
45 internal int margin_right;
46 internal string undo_action_name;
47 internal float zoom;
49 private RTF.TextMap rtf_text_map;
50 private int rtf_skip_width;
51 private int rtf_skip_count;
52 private StringBuilder rtf_line;
53 private SolidBrush rtf_color;
54 private RTF.Font rtf_rtffont;
55 private int rtf_rtffont_size;
56 private FontStyle rtf_rtfstyle;
57 private HorizontalAlignment rtf_rtfalign;
58 private int rtf_cursor_x;
59 private int rtf_cursor_y;
60 private int rtf_chars;
61 #endregion // Local Variables
63 #region Public Constructors
64 public RichTextBox() {
65 accepts_return = true;
66 auto_word_select = false;
67 bullet_indent = 0;
68 can_redo = false;
69 detect_urls = true;
70 max_length = Int32.MaxValue;
71 redo_action_name = string.Empty;
72 margin_right = 0;
73 undo_action_name = string.Empty;
74 zoom = 1;
75 base.Multiline = true;
76 document.CRLFSize = 1;
78 scrollbars = RichTextBoxScrollBars.Both;
79 alignment = HorizontalAlignment.Left;
80 LostFocus += new EventHandler(RichTextBox_LostFocus);
81 GotFocus += new EventHandler(RichTextBox_GotFocus);
82 BackColor = ThemeEngine.Current.ColorWindow;
83 ForeColor = ThemeEngine.Current.ColorWindowText;
84 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
85 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
87 #endregion // Public Constructors
89 #region Private & Internal Methods
90 private void RichTextBox_LostFocus(object sender, EventArgs e) {
91 has_focus = false;
92 Invalidate();
95 private void RichTextBox_GotFocus(object sender, EventArgs e) {
96 has_focus = true;
97 Invalidate();
99 #endregion // Private & Internal Methods
101 #region Public Instance Properties
102 public override bool AllowDrop {
103 get {
104 return base.AllowDrop;
107 set {
108 base.AllowDrop = value;
112 [DefaultValue(false)]
113 [Localizable(true)]
114 public override bool AutoSize {
115 get {
116 return auto_size;
119 set {
120 base.AutoSize = value;
124 [DefaultValue(false)]
125 public bool AutoWordSelection {
126 get {
127 return auto_word_select;
130 set {
131 auto_word_select = true;
135 [Browsable(false)]
136 [EditorBrowsable(EditorBrowsableState.Never)]
137 public override System.Drawing.Image BackgroundImage {
138 get {
139 return background_image;
142 set {
143 base.BackgroundImage = value;
147 [DefaultValue(0)]
148 [Localizable(true)]
149 public int BulletIndent {
150 get {
151 return bullet_indent;
154 set {
155 bullet_indent = value;
159 [Browsable(false)]
160 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
161 public bool CanRedo {
162 get {
163 return can_redo;
167 [DefaultValue(true)]
168 public bool DetectUrls {
169 get {
170 return detect_urls;
173 set {
174 detect_urls = true;
178 public override Font Font {
179 get {
180 return base.Font;
183 set {
184 if (font != value) {
185 Line start;
186 Line end;
188 if (auto_size) {
189 if (PreferredHeight != Height) {
190 Height = PreferredHeight;
194 base.Font = value;
196 // Font changes always set the whole doc to that font
197 start = document.GetLine(1);
198 end = document.GetLine(document.Lines);
199 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, new SolidBrush(this.ForeColor));
204 public override Color ForeColor {
205 get {
206 return base.ForeColor;
209 set {
210 base.ForeColor = value;
214 [DefaultValue(Int32.MaxValue)]
215 public override int MaxLength {
216 get {
217 return base.max_length;
220 set {
221 base.max_length = value;
225 [DefaultValue(true)]
226 public override bool Multiline {
227 get {
228 return multiline;
231 set {
232 base.Multiline = value;
236 [Browsable(false)]
237 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
238 [MonoTODO]
239 public string RedoActionName {
240 get {
241 return redo_action_name;
245 [DefaultValue(0)]
246 [Localizable(true)]
247 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
248 public int RightMargin {
249 get {
250 return margin_right;
253 set {
254 margin_right = value;
258 [Browsable(false)]
259 [DefaultValue("")]
260 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
261 public string Rtf {
262 get {
263 Line start_line;
264 Line end_line;
266 start_line = document.GetLine(1);
267 end_line = document.GetLine(document.Lines);
268 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
271 set {
272 MemoryStream data;
274 document.Empty();
275 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
277 InsertRTFFromStream(data, 0, 1);
279 data.Close();
281 Invalidate();
285 [DefaultValue(RichTextBoxScrollBars.Both)]
286 [Localizable(true)]
287 public RichTextBoxScrollBars ScrollBars {
288 get {
289 return scrollbars;
292 set {
293 scrollbars = value;
297 [Browsable(false)]
298 [DefaultValue("")]
299 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
300 public string SelectedRtf {
301 get {
302 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
305 set {
306 MemoryStream data;
307 int x;
308 int y;
309 int sel_start;
310 int chars;
311 Line line;
312 LineTag tag;
314 if (document.selection_visible) {
315 document.ReplaceSelection("");
318 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
320 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
321 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
322 data.Close();
324 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
325 document.SetSelection(line, sel_start);
326 document.PositionCaret(line, sel_start);
327 document.DisplayCaret();
328 ScrollToCaret();
329 OnTextChanged(EventArgs.Empty);
333 [Browsable(false)]
334 [DefaultValue("")]
335 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
336 public override string SelectedText {
337 get {
338 return base.SelectedText;
341 set {
342 base.SelectedText = value;
346 [Browsable(false)]
347 [DefaultValue(HorizontalAlignment.Left)]
348 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
349 public HorizontalAlignment SelectionAlignment {
350 get {
351 HorizontalAlignment align;
352 Line start;
353 Line end;
354 Line line;
356 start = document.ParagraphStart(document.selection_start.line);
357 align = start.alignment;
359 end = document.ParagraphEnd(document.selection_end.line);
361 line = start;
363 while (true) {
364 if (line.alignment != align) {
365 return HorizontalAlignment.Left;
368 if (line == end) {
369 break;
371 line = document.GetLine(line.line_no + 1);
374 return align;
377 set {
378 Line start;
379 Line end;
380 Line line;
382 start = document.ParagraphStart(document.selection_start.line);
384 end = document.ParagraphEnd(document.selection_end.line);
386 line = start;
388 while (true) {
389 line.alignment = value;
391 if (line == end) {
392 break;
394 line = document.GetLine(line.line_no + 1);
396 this.CalculateDocument();
400 [Browsable(false)]
401 [DefaultValue(false)]
402 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
403 [MonoTODO]
404 public bool SelectionBullet {
405 get {
406 return false;
409 set {
413 [Browsable(false)]
414 [DefaultValue(0)]
415 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
416 [MonoTODO]
417 public int SelectionCharOffset {
418 get {
419 return 0;
422 set {
426 [Browsable(false)]
427 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
428 public Color SelectionColor {
429 get {
430 Color color;
431 LineTag start;
432 LineTag end;
433 LineTag tag;
435 start = document.selection_start.tag;
436 end = document.selection_end.tag;
437 color = ((SolidBrush)document.selection_start.tag.color).Color;
439 tag = start;
440 while (true) {
441 if (!color.Equals(((SolidBrush)tag.color).Color)) {
442 return Color.Empty;
445 if (tag == end) {
446 break;
449 tag = document.NextTag(tag);
451 if (tag == null) {
452 break;
456 return color;
459 set {
460 FontDefinition attributes;
461 int sel_start;
462 int sel_end;
464 attributes = new FontDefinition();
465 attributes.color = value;
467 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
468 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
470 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
472 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
473 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
475 document.UpdateView(document.selection_start.line, 0);
476 document.AlignCaret();
480 [Browsable(false)]
481 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
482 public Font SelectionFont {
483 get {
484 Font font;
485 LineTag start;
486 LineTag end;
487 LineTag tag;
489 start = document.selection_start.tag;
490 end = document.selection_end.tag;
491 font = document.selection_start.tag.font;
493 tag = start;
494 while (true) {
495 if (!font.Equals(tag.font)) {
496 return null;
499 if (tag == end) {
500 break;
503 tag = document.NextTag(tag);
505 if (tag == null) {
506 break;
510 return font;
513 set {
514 FontDefinition attributes;
515 int sel_start;
516 int sel_end;
518 attributes = new FontDefinition();
519 attributes.font_obj = value;
521 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
522 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
524 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
526 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
527 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
529 document.UpdateView(document.selection_start.line, 0);
530 document.AlignCaret();
535 [Browsable(false)]
536 [DefaultValue(0)]
537 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
538 [MonoTODO]
539 public int SelectionHangingIndent {
540 get {
541 return 0;
544 set {
548 [Browsable(false)]
549 [DefaultValue(0)]
550 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
551 [MonoTODO]
552 public int SelectionIndent {
553 get {
554 return 0;
557 set {
561 [Browsable(false)]
562 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
563 public override int SelectionLength {
564 get {
565 return base.SelectionLength;
568 set {
569 base.SelectionLength = value;
573 [Browsable(false)]
574 [DefaultValue(false)]
575 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
576 [MonoTODO]
577 public bool SelectionProtected {
578 get {
579 return false;
582 set {
586 [Browsable(false)]
587 [DefaultValue(0)]
588 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
589 [MonoTODO]
590 public int SelectionRightIndent {
591 get {
592 return 0;
595 set {
599 [Browsable(false)]
600 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
601 [MonoTODO]
602 public int[] SelectionTabs {
603 get {
604 return new int[0];
607 set {
611 [Browsable(false)]
612 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
613 public RichTextBoxSelectionTypes SelectionType {
614 get {
615 if (document.selection_start == document.selection_end) {
616 return RichTextBoxSelectionTypes.Empty;
619 // Lazy, but works
620 if (SelectedText.Length > 1) {
621 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
624 return RichTextBoxSelectionTypes.Text;
628 [DefaultValue(false)]
629 [MonoTODO]
630 public bool ShowSelectionMargin {
631 get {
632 return false;
635 set {
639 [Localizable(true)]
640 public override string Text {
641 get {
642 return base.Text;
645 set {
646 base.Text = value;
650 [Browsable(false)]
651 public override int TextLength {
652 get {
653 return base.TextLength;
657 [Browsable(false)]
658 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
659 public string UndoActionName {
660 get {
661 return document.undo.UndoName;
665 [Localizable(true)]
666 [DefaultValue(1)]
667 public float ZoomFactor {
668 get {
669 return zoom;
672 set {
673 zoom = value;
676 #endregion // Public Instance Properties
678 #region Protected Instance Properties
679 protected override CreateParams CreateParams {
680 get {
681 return base.CreateParams;
685 protected override Size DefaultSize {
686 get {
687 return new Size(100, 96);
690 #endregion // Protected Instance Properties
692 #region Public Instance Methods
693 public bool CanPaste(DataFormats.Format clipFormat) {
694 if ((clipFormat.Name == DataFormats.Rtf) ||
695 (clipFormat.Name == DataFormats.Text) ||
696 (clipFormat.Name == DataFormats.UnicodeText)) {
697 return true;
699 return false;
702 public int Find(char[] characterSet) {
703 return Find(characterSet, -1, -1);
706 public int Find(char[] characterSet, int start) {
707 return Find(characterSet, start, -1);
710 public int Find(char[] characterSet, int start, int end) {
711 Document.Marker start_mark;
712 Document.Marker end_mark;
713 Document.Marker result;
715 if (start == -1) {
716 document.GetMarker(out start_mark, true);
717 } else {
718 Line line;
719 LineTag tag;
720 int pos;
722 start_mark = new Document.Marker();
724 document.CharIndexToLineTag(start, out line, out tag, out pos);
725 start_mark.line = line;
726 start_mark.tag = tag;
727 start_mark.pos = pos;
730 if (end == -1) {
731 document.GetMarker(out end_mark, false);
732 } else {
733 Line line;
734 LineTag tag;
735 int pos;
737 end_mark = new Document.Marker();
739 document.CharIndexToLineTag(end, out line, out tag, out pos);
740 end_mark.line = line;
741 end_mark.tag = tag;
742 end_mark.pos = pos;
745 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
746 return document.LineTagToCharIndex(result.line, result.pos);
749 return -1;
752 public int Find(string str) {
753 return Find(str, -1, -1, RichTextBoxFinds.None);
756 public int Find(string str, int start, int end, RichTextBoxFinds options) {
757 Document.Marker start_mark;
758 Document.Marker end_mark;
759 Document.Marker result;
761 if (start == -1) {
762 document.GetMarker(out start_mark, true);
763 } else {
764 Line line;
765 LineTag tag;
766 int pos;
768 start_mark = new Document.Marker();
770 document.CharIndexToLineTag(start, out line, out tag, out pos);
772 start_mark.line = line;
773 start_mark.tag = tag;
774 start_mark.pos = pos;
777 if (end == -1) {
778 document.GetMarker(out end_mark, false);
779 } else {
780 Line line;
781 LineTag tag;
782 int pos;
784 end_mark = new Document.Marker();
786 document.CharIndexToLineTag(end, out line, out tag, out pos);
788 end_mark.line = line;
789 end_mark.tag = tag;
790 end_mark.pos = pos;
793 if (document.Find(str, start_mark, end_mark, out result, options)) {
794 return document.LineTagToCharIndex(result.line, result.pos);
797 return -1;
800 public int Find(string str, int start, RichTextBoxFinds options) {
801 return Find(str, start, -1, options);
804 public int Find(string str, RichTextBoxFinds options) {
805 return Find(str, -1, -1, options);
808 public char GetCharFromPosition(Point pt) {
809 LineTag tag;
810 int pos;
812 PointToTagPos(pt, out tag, out pos);
814 if (pos >= tag.line.text.Length) {
815 return '\n';
818 return tag.line.text[pos];
822 public int GetCharIndexFromPosition(Point pt) {
823 LineTag tag;
824 int pos;
826 PointToTagPos(pt, out tag, out pos);
828 return document.LineTagToCharIndex(tag.line, pos);
831 public int GetLineFromCharIndex(int index) {
832 Line line;
833 LineTag tag;
834 int pos;
836 document.CharIndexToLineTag(index, out line, out tag, out pos);
838 return line.LineNo - 1;
841 public Point GetPositionFromCharIndex(int index) {
842 Line line;
843 LineTag tag;
844 int pos;
846 document.CharIndexToLineTag(index, out line, out tag, out pos);
848 return new Point((int)line.widths[pos] + 1, line.Y + 1);
851 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
852 document.Empty();
854 // FIXME - ignoring unicode
855 if (fileType == RichTextBoxStreamType.PlainText) {
856 StringBuilder sb;
857 int count;
858 byte[] buffer;
860 try {
861 sb = new StringBuilder((int)data.Length);
862 buffer = new byte[1024];
865 catch {
866 throw new IOException("Not enough memory to load document");
869 count = 0;
870 while (count < data.Length) {
871 count += data.Read(buffer, count, 1024);
872 sb.Append(buffer);
874 base.Text = sb.ToString();
875 return;
878 InsertRTFFromStream(data, 0, 1);
881 [MonoTODO("Make smarter RTF detection?")]
882 public void LoadFile(string path) {
883 if (path.EndsWith(".rtf")) {
884 LoadFile(path, RichTextBoxStreamType.RichText);
885 } else {
886 LoadFile(path, RichTextBoxStreamType.PlainText);
890 public void LoadFile(string path, RichTextBoxStreamType fileType) {
891 FileStream data;
893 data = null;
895 try {
896 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
897 LoadFile(data, fileType);
900 catch {
901 throw new IOException("Could not open file " + path);
904 finally {
905 if (data != null) {
906 data.Close();
911 public void Paste(DataFormats.Format clipFormat) {
912 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
915 [MonoTODO()]
916 public void Redo() {
919 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
920 Encoding encoding;
921 int i;
922 Byte[] bytes;
925 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
926 encoding = Encoding.Unicode;
927 } else {
928 encoding = Encoding.ASCII;
931 switch(fileType) {
932 case RichTextBoxStreamType.PlainText:
933 case RichTextBoxStreamType.TextTextOleObjs:
934 case RichTextBoxStreamType.UnicodePlainText: {
935 if (!multiline) {
936 bytes = encoding.GetBytes(document.Root.text.ToString());
937 data.Write(bytes, 0, bytes.Length);
938 return;
941 for (i = 1; i < document.Lines; i++) {
942 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
943 data.Write(bytes, 0, bytes.Length);
945 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
946 data.Write(bytes, 0, bytes.Length);
947 return;
951 // If we're here we're saving RTF
952 Line start_line;
953 Line end_line;
954 StringBuilder rtf;
955 int current;
956 int total;
958 start_line = document.GetLine(1);
959 end_line = document.GetLine(document.Lines);
960 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
961 total = rtf.Length;
962 bytes = new Byte[4096];
964 // Let's chunk it so we don't use up all memory...
965 for (i = 0; i < total; i += 1024) {
966 if ((i + 1024) < total) {
967 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
968 } else {
969 current = total - i;
970 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
972 data.Write(bytes, 0, current);
976 public void SaveFile(string path) {
977 if (path.EndsWith(".rtf")) {
978 SaveFile(path, RichTextBoxStreamType.RichText);
979 } else {
980 SaveFile(path, RichTextBoxStreamType.PlainText);
984 public void SaveFile(string path, RichTextBoxStreamType fileType) {
985 FileStream data;
987 data = null;
989 // try {
990 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
991 SaveFile(data, fileType);
992 // }
994 // catch {
995 // throw new IOException("Could not write document to file " + path);
996 // }
998 // finally {
999 if (data != null) {
1000 data.Close();
1002 // }
1005 #endregion // Public Instance Methods
1007 #region Protected Instance Methods
1008 protected virtual object CreateRichEditOleCallback() {
1009 throw new NotImplementedException();
1012 protected override void OnBackColorChanged(EventArgs e) {
1013 base.OnBackColorChanged (e);
1016 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1017 if (ContentsResized != null) {
1018 ContentsResized(this, e);
1022 protected override void OnContextMenuChanged(EventArgs e) {
1023 base.OnContextMenuChanged (e);
1026 protected override void OnHandleCreated(EventArgs e) {
1027 base.OnHandleCreated (e);
1030 protected override void OnHandleDestroyed(EventArgs e) {
1031 base.OnHandleDestroyed (e);
1034 protected virtual void OnHScroll(EventArgs e) {
1035 if (HScroll != null) {
1036 HScroll(this, e);
1040 [MonoTODO("Determine when to call this")]
1041 protected virtual void OnImeChange(EventArgs e) {
1042 if (ImeChange != null) {
1043 ImeChange(this, e);
1047 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1048 if (LinkClicked != null) {
1049 LinkClicked(this, e);
1053 protected virtual void OnProtected(EventArgs e) {
1054 if (Protected != null) {
1055 Protected(this, e);
1059 protected override void OnRightToLeftChanged(EventArgs e) {
1060 base.OnRightToLeftChanged (e);
1063 protected virtual void OnSelectionChanged(EventArgs e) {
1064 if (SelectionChanged != null) {
1065 SelectionChanged(this, e);
1069 protected override void OnSystemColorsChanged(EventArgs e) {
1070 base.OnSystemColorsChanged (e);
1073 protected override void OnTextChanged(EventArgs e) {
1074 base.OnTextChanged (e);
1077 protected virtual void OnVScroll(EventArgs e) {
1078 if (VScroll != null) {
1079 VScroll(this, e);
1083 protected override void WndProc(ref Message m) {
1084 base.WndProc (ref m);
1086 #endregion // Protected Instance Methods
1088 #region Events
1089 [Browsable(false)]
1090 [EditorBrowsable(EditorBrowsableState.Never)]
1091 public event EventHandler BackgroundImageChanged;
1093 public event ContentsResizedEventHandler ContentsResized;
1095 [Browsable(false)]
1096 [EditorBrowsable(EditorBrowsableState.Never)]
1097 public event EventHandler DoubleClick;
1099 [Browsable(false)]
1100 [EditorBrowsable(EditorBrowsableState.Never)]
1101 public event DragEventHandler DragDrop {
1102 add {
1103 base.DragDrop += value;
1106 remove {
1107 base.DragDrop -= value;
1111 [Browsable(false)]
1112 [EditorBrowsable(EditorBrowsableState.Never)]
1113 public event DragEventHandler DragEnter {
1114 add {
1115 base.DragEnter += value;
1118 remove {
1119 base.DragEnter -= value;
1123 [Browsable(false)]
1124 [EditorBrowsable(EditorBrowsableState.Never)]
1125 public event EventHandler DragLeave {
1126 add {
1127 base.DragLeave += value;
1130 remove {
1131 base.DragLeave -= value;
1136 [Browsable(false)]
1137 [EditorBrowsable(EditorBrowsableState.Never)]
1138 public event DragEventHandler DragOver {
1139 add {
1140 base.DragOver += value;
1143 remove {
1144 base.DragOver -= value;
1149 [Browsable(false)]
1150 [EditorBrowsable(EditorBrowsableState.Never)]
1151 public event GiveFeedbackEventHandler GiveFeedback;
1153 public event EventHandler HScroll;
1154 public event EventHandler ImeChange;
1155 public event LinkClickedEventHandler LinkClicked;
1156 public event EventHandler Protected;
1158 [Browsable(false)]
1159 [EditorBrowsable(EditorBrowsableState.Never)]
1160 public event QueryContinueDragEventHandler QueryContinueDrag;
1161 public event EventHandler SelectionChanged;
1162 public event EventHandler VScroll;
1163 #endregion // Events
1165 #region Private Methods
1166 private void HandleControl(RTF.RTF rtf) {
1167 // Console.WriteLine ("HANDLING MAJOR: {0} MINOR: {1}", rtf.Major, rtf.Minor);
1168 switch(rtf.Major) {
1169 case RTF.Major.Unicode: {
1170 switch(rtf.Minor) {
1171 case Minor.UnicodeCharBytes: {
1172 rtf_skip_width = rtf.Param;
1173 break;
1176 case Minor.UnicodeChar: {
1177 rtf_skip_count += rtf_skip_width;
1178 rtf_line.Append((char)rtf.Param);
1179 break;
1182 break;
1185 case RTF.Major.Destination: {
1186 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1187 rtf.SkipGroup();
1188 break;
1191 case RTF.Major.CharAttr: {
1192 switch(rtf.Minor) {
1193 case Minor.ForeColor: {
1194 System.Windows.Forms.RTF.Color color;
1196 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1197 if (color != null) {
1198 FlushText(rtf, false);
1199 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1200 this.rtf_color = new SolidBrush(ForeColor);
1201 } else {
1202 this.rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1205 break;
1208 case Minor.FontSize: {
1209 FlushText(rtf, false);
1210 this.rtf_rtffont_size = rtf.Param / 2;
1211 break;
1214 case Minor.FontNum: {
1215 System.Windows.Forms.RTF.Font font;
1217 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1218 if (font != null) {
1219 FlushText(rtf, false);
1220 this.rtf_rtffont = font;
1222 break;
1225 case Minor.Plain: {
1226 FlushText(rtf, false);
1227 rtf_rtfstyle = FontStyle.Regular;
1228 break;
1231 case Minor.Bold: {
1232 FlushText(rtf, false);
1233 if (rtf.Param == RTF.RTF.NoParam) {
1234 rtf_rtfstyle |= FontStyle.Bold;
1235 } else {
1236 rtf_rtfstyle &= ~FontStyle.Bold;
1238 break;
1241 case Minor.Italic: {
1242 FlushText(rtf, false);
1243 if (rtf.Param == RTF.RTF.NoParam) {
1244 rtf_rtfstyle |= FontStyle.Italic;
1245 } else {
1246 rtf_rtfstyle &= ~FontStyle.Italic;
1248 break;
1251 case Minor.StrikeThru: {
1252 FlushText(rtf, false);
1253 if (rtf.Param == RTF.RTF.NoParam) {
1254 rtf_rtfstyle |= FontStyle.Strikeout;
1255 } else {
1256 rtf_rtfstyle &= ~FontStyle.Strikeout;
1258 break;
1261 case Minor.Underline: {
1262 FlushText(rtf, false);
1263 if (rtf.Param == RTF.RTF.NoParam) {
1264 rtf_rtfstyle |= FontStyle.Underline;
1265 } else {
1266 rtf_rtfstyle = rtf_rtfstyle & ~FontStyle.Underline;
1268 break;
1271 case Minor.NoUnderline: {
1272 FlushText(rtf, false);
1273 rtf_rtfstyle &= ~FontStyle.Underline;
1274 break;
1277 break;
1280 case RTF.Major.SpecialChar: {
1281 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1282 SpecialChar(rtf);
1283 break;
1288 private void SpecialChar(RTF.RTF rtf) {
1289 switch(rtf.Minor) {
1290 case Minor.Page:
1291 case Minor.Sect:
1292 case Minor.Row:
1293 case Minor.Line:
1294 case Minor.Par: {
1295 FlushText(rtf, true);
1296 break;
1299 case Minor.Cell: {
1300 Console.Write(" ");
1301 break;
1304 case Minor.NoBrkSpace: {
1305 Console.Write(" ");
1306 break;
1309 case Minor.Tab: {
1310 rtf_line.Append ("\t");
1311 // FlushText (rtf, false);
1312 break;
1315 case Minor.NoReqHyphen:
1316 case Minor.NoBrkHyphen: {
1317 rtf_line.Append ("-");
1318 // FlushText (rtf, false);
1319 break;
1322 case Minor.Bullet: {
1323 Console.WriteLine("*");
1324 break;
1327 case Minor.WidowCtrl:
1328 break;
1330 case Minor.EmDash: {
1331 rtf_line.Append ("\u2014");
1332 break;
1335 case Minor.EnDash: {
1336 rtf_line.Append ("\u2013");
1337 break;
1340 case Minor.LQuote: {
1341 Console.Write("\u2018");
1342 break;
1345 case Minor.RQuote: {
1346 Console.Write("\u2019");
1347 break;
1350 case Minor.LDblQuote: {
1351 Console.Write("\u201C");
1352 break;
1355 case Minor.RDblQuote: {
1356 Console.Write("\u201D");
1357 break;
1360 default: {
1361 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1362 // rtf.SkipGroup();
1363 break;
1368 private void HandleText(RTF.RTF rtf) {
1369 if (rtf_skip_count > 0) {
1370 rtf_skip_count--;
1371 return;
1374 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1375 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1376 } else {
1377 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1378 rtf_line.Append((char)rtf.Major);
1379 } else {
1380 //rtf_line.Append((char)rtf.Major);
1381 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1386 private void FlushText(RTF.RTF rtf, bool newline) {
1387 int length;
1388 Font font;
1390 length = rtf_line.Length;
1391 if (!newline && (length == 0)) {
1392 return;
1395 if (rtf_rtffont == null) {
1396 // First font in table is default
1397 rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont(rtf, 0);
1400 font = new Font(rtf_rtffont.Name, rtf_rtffont_size, rtf_rtfstyle);
1402 if (rtf_color == null) {
1403 System.Windows.Forms.RTF.Color color;
1405 // First color in table is default
1406 color = System.Windows.Forms.RTF.Color.GetColor(rtf, 0);
1408 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1409 rtf_color = new SolidBrush(ForeColor);
1410 } else {
1411 rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1415 rtf_chars += rtf_line.Length;
1417 if (rtf_cursor_x == 0) {
1418 document.Add(rtf_cursor_y, rtf_line.ToString(), rtf_rtfalign, font, rtf_color);
1419 } else {
1420 Line line;
1422 line = document.GetLine(rtf_cursor_y);
1423 if (rtf_line.Length > 0) {
1424 document.InsertString(line, rtf_cursor_x, rtf_line.ToString());
1425 document.FormatText(line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length, font, rtf_color); // FormatText is 1-based
1427 if (newline) {
1428 document.Split(line, rtf_cursor_x + length);
1432 if (newline) {
1433 rtf_cursor_x = 0;
1434 rtf_cursor_y++;
1435 } else {
1436 rtf_cursor_x += length;
1438 rtf_line.Length = 0; // Empty line
1441 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1442 int x;
1443 int y;
1444 int chars;
1446 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1449 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1450 RTF.RTF rtf;
1452 rtf = new RTF.RTF(data);
1454 // Prepare
1455 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1456 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1458 rtf_skip_width = 0;
1459 rtf_skip_count = 0;
1460 rtf_line = new StringBuilder();
1461 rtf_color = null;
1462 rtf_rtffont_size = (int)this.Font.Size;
1463 rtf_rtfalign = HorizontalAlignment.Left;
1464 rtf_rtfstyle = FontStyle.Regular;
1465 rtf_rtffont = null;
1466 rtf_cursor_x = cursor_x;
1467 rtf_cursor_y = cursor_y;
1468 rtf_chars = 0;
1469 rtf.DefaultFont(this.Font.Name);
1471 rtf_text_map = new RTF.TextMap();
1472 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1474 document.NoRecalc = true;
1476 try {
1477 rtf.Read(); // That's it
1478 FlushText(rtf, false);
1481 catch (RTF.RTFException e) {
1482 // Seems to be plain text or broken RTF
1483 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1486 to_x = rtf_cursor_x;
1487 to_y = rtf_cursor_y;
1488 chars = rtf_chars;
1490 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1491 document.NoRecalc = false;
1493 document.Invalidate(document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1496 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1497 OnHScroll(e);
1500 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1501 OnVScroll(e);
1504 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1505 Point p;
1507 p = pt;
1509 if (p.X >= document.ViewPortWidth) {
1510 p.X = document.ViewPortWidth - 1;
1511 } else if (p.X < 0) {
1512 p.X = 0;
1515 if (p.Y >= document.ViewPortHeight) {
1516 p.Y = document.ViewPortHeight - 1;
1517 } else if (p.Y < 0) {
1518 p.Y = 0;
1521 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1524 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1525 if (prev_index != font_index) {
1526 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1529 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1530 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1533 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1534 if (font.Bold) {
1535 rtf.Append("\\b");
1536 } else {
1537 if (prev_font != null) {
1538 rtf.Append("\\b0");
1543 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1544 if (font.Italic) {
1545 rtf.Append("\\i");
1546 } else {
1547 if (prev_font != null) {
1548 rtf.Append("\\i0");
1553 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1554 if (font.Strikeout) {
1555 rtf.Append("\\strike");
1556 } else {
1557 if (prev_font != null) {
1558 rtf.Append("\\strike0");
1563 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1564 if (font.Underline) {
1565 rtf.Append("\\ul");
1566 } else {
1567 if (prev_font != null) {
1568 rtf.Append("\\ul0");
1574 [MonoTODO("Emit unicode and other special characters properly")]
1575 private void EmitRTFText(StringBuilder rtf, string text) {
1576 rtf.Append(text);
1579 // start_pos and end_pos are 0-based
1580 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1581 StringBuilder sb;
1582 ArrayList fonts;
1583 ArrayList colors;
1584 Color color;
1585 Font font;
1586 Line line;
1587 LineTag tag;
1588 int pos;
1589 int line_no;
1590 int line_len;
1591 int i;
1592 int length;
1594 sb = new StringBuilder();
1595 fonts = new ArrayList(10);
1596 colors = new ArrayList(10);
1598 // Two runs, first we parse to determine tables;
1599 // and unlike most of our processing here we work on tags
1601 line = start_line;
1602 line_no = start_line.line_no;
1603 pos = start_pos;
1605 // Add default font and color; to optimize document content we don't
1606 // use this.Font and this.ForeColor but the font/color from the first tag
1607 tag = LineTag.FindTag(start_line, pos);
1608 font = tag.font;
1609 color = ((SolidBrush)tag.color).Color;
1610 fonts.Add(font.Name);
1611 colors.Add(color);
1613 while (line_no <= end_line.line_no) {
1614 line = document.GetLine(line_no);
1615 tag = LineTag.FindTag(line, pos);
1617 if (line_no != end_line.line_no) {
1618 line_len = line.text.Length;
1619 } else {
1620 line_len = end_pos;
1623 while (pos < line_len) {
1624 if (tag.font.Name != font.Name) {
1625 font = tag.font;
1626 if (!fonts.Contains(font.Name)) {
1627 fonts.Add(font.Name);
1631 if (((SolidBrush)tag.color).Color != color) {
1632 color = ((SolidBrush)tag.color).Color;
1633 if (!colors.Contains(color)) {
1634 colors.Add(color);
1638 pos = tag.start + tag.length - 1;
1639 tag = tag.next;
1641 pos = 0;
1642 line_no++;
1645 // We have the tables, emit the header
1646 sb.Append("{\\rtf1\\ansi");
1647 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
1649 // Default Font
1650 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1652 // Default Language
1653 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
1655 // Emit the font table
1656 sb.Append("{\\fonttbl");
1657 for (i = 0; i < fonts.Count; i++) {
1658 sb.Append(String.Format("{{\\f{0}", i)); // {Font
1659 sb.Append("\\fnil"); // Family
1660 sb.Append("\\fcharset0 "); // Charset ANSI<space>
1661 sb.Append((string)fonts[i]); // Font name
1662 sb.Append(";}"); // }
1664 sb.Append("}\n");
1666 // Emit the color table (if needed)
1667 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))) {
1668 sb.Append("{\\colortbl "); // Header and NO! default color
1669 for (i = 0; i < colors.Count; i++) {
1670 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
1671 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
1672 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
1673 sb.Append(";");
1675 sb.Append("}\n");
1678 sb.Append("{\\*\\generator Mono RichTextBox;}");
1679 // Emit initial paragraph settings
1680 tag = LineTag.FindTag(start_line, start_pos);
1681 sb.Append("\\pard"); // Reset to default paragraph properties
1682 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.font.Name), null, tag.font); // Font properties
1683 sb.Append(" "); // Space separator
1685 font = tag.font;
1686 color = (Color)colors[0];
1687 line = start_line;
1688 line_no = start_line.line_no;
1689 pos = start_pos;
1691 while (line_no <= end_line.line_no) {
1692 line = document.GetLine(line_no);
1693 tag = LineTag.FindTag(line, pos);
1695 if (line_no != end_line.line_no) {
1696 line_len = line.text.Length;
1697 } else {
1698 line_len = end_pos;
1701 while (pos < line_len) {
1702 length = sb.Length;
1704 if (tag.font != font) {
1705 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.font.Name), font, tag.font);
1706 font = tag.font;
1709 if (((SolidBrush)tag.color).Color != color) {
1710 color = ((SolidBrush)tag.color).Color;
1711 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
1713 if (length != sb.Length) {
1714 sb.Append(" "); // Emit space to separate keywords from text
1717 // Emit the string itself
1718 if (line_no != end_line.line_no) {
1719 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1720 } else {
1721 if (end_pos < (tag.start + tag.length - 1)) {
1722 // Emit partial tag only, end_pos is inside this tag
1723 EmitRTFText(sb, tag.line.text.ToString(pos, end_pos - pos));
1724 } else {
1725 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1729 pos = tag.start + tag.length - 1;
1730 tag = tag.next;
1732 if (pos >= line.text.Length) {
1733 if (!line.soft_break) {
1734 sb.Append("\\par\n");
1737 pos = 0;
1738 line_no++;
1741 sb.Append("}\n");
1743 return sb;
1745 #endregion // Private Methods