* TextBoxBase.cs: Use the new SuspendRecalc/ResumeRecalc methods
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / RichTextBox.cs
blob7394386de5480a06496f4677c76b8e3b3de81f7b
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 // #define DEBUG
31 using System;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Drawing;
35 using System.IO;
36 using System.Text;
37 using RTF=System.Windows.Forms.RTF;
39 namespace System.Windows.Forms {
40 public class RichTextBox : TextBoxBase {
41 #region Local Variables
42 internal bool auto_word_select;
43 internal int bullet_indent;
44 internal bool can_redo;
45 internal bool detect_urls;
46 internal string redo_action_name;
47 internal int margin_right;
48 internal string undo_action_name;
49 internal float zoom;
51 private RTF.TextMap rtf_text_map;
52 private int rtf_skip_width;
53 private int rtf_skip_count;
54 private StringBuilder rtf_line;
55 private SolidBrush rtf_color;
56 private RTF.Font rtf_rtffont;
57 private int rtf_rtffont_size;
58 private FontStyle rtf_rtfstyle;
59 private HorizontalAlignment rtf_rtfalign;
60 private int rtf_cursor_x;
61 private int rtf_cursor_y;
62 private int rtf_chars;
63 #endregion // Local Variables
65 #region Public Constructors
66 public RichTextBox() {
67 accepts_return = true;
68 auto_word_select = false;
69 bullet_indent = 0;
70 can_redo = false;
71 detect_urls = true;
72 max_length = Int32.MaxValue;
73 redo_action_name = string.Empty;
74 margin_right = 0;
75 undo_action_name = string.Empty;
76 zoom = 1;
77 base.Multiline = true;
78 document.CRLFSize = 1;
80 scrollbars = RichTextBoxScrollBars.Both;
81 alignment = HorizontalAlignment.Left;
82 LostFocus += new EventHandler(RichTextBox_LostFocus);
83 GotFocus += new EventHandler(RichTextBox_GotFocus);
84 BackColor = ThemeEngine.Current.ColorWindow;
85 ForeColor = ThemeEngine.Current.ColorWindowText;
86 base.HScrolled += new EventHandler(RichTextBox_HScrolled);
87 base.VScrolled += new EventHandler(RichTextBox_VScrolled);
89 #if NET_2_0
90 SetStyle (ControlStyles.StandardDoubleClick, false);
91 #endif
93 #endregion // Public Constructors
95 #region Private & Internal Methods
96 private void RichTextBox_LostFocus(object sender, EventArgs e) {
97 Invalidate();
100 private void RichTextBox_GotFocus(object sender, EventArgs e) {
101 Invalidate();
103 #endregion // Private & Internal Methods
105 #region Public Instance Properties
106 public override bool AllowDrop {
107 get {
108 return base.AllowDrop;
111 set {
112 base.AllowDrop = value;
116 [DefaultValue(false)]
117 [Localizable(true)]
118 public override bool AutoSize {
119 get {
120 return auto_size;
123 set {
124 base.AutoSize = value;
128 [DefaultValue(false)]
129 public bool AutoWordSelection {
130 get {
131 return auto_word_select;
134 set {
135 auto_word_select = true;
139 [Browsable(false)]
140 [EditorBrowsable(EditorBrowsableState.Never)]
141 public override System.Drawing.Image BackgroundImage {
142 get { return base.BackgroundImage; }
143 set { base.BackgroundImage = value; }
146 [DefaultValue(0)]
147 [Localizable(true)]
148 public int BulletIndent {
149 get {
150 return bullet_indent;
153 set {
154 bullet_indent = value;
158 [Browsable(false)]
159 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
160 public bool CanRedo {
161 get {
162 return can_redo;
166 [DefaultValue(true)]
167 public bool DetectUrls {
168 get {
169 return detect_urls;
172 set {
173 detect_urls = true;
177 public override Font Font {
178 get {
179 return base.Font;
182 set {
183 if (font != value) {
184 Line start;
185 Line end;
187 if (auto_size) {
188 if (PreferredHeight != Height) {
189 Height = PreferredHeight;
193 base.Font = value;
195 // Font changes always set the whole doc to that font
196 start = document.GetLine(1);
197 end = document.GetLine(document.Lines);
198 document.FormatText(start, 1, end, end.text.Length + 1, base.Font, null, null, FormatSpecified.Font);
203 public override Color ForeColor {
204 get {
205 return base.ForeColor;
208 set {
209 base.ForeColor = value;
213 [DefaultValue(Int32.MaxValue)]
214 public override int MaxLength {
215 get {
216 return base.max_length;
219 set {
220 base.max_length = value;
224 [DefaultValue(true)]
225 public override bool Multiline {
226 get {
227 return multiline;
230 set {
231 base.Multiline = value;
235 [Browsable(false)]
236 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
237 [MonoTODO]
238 public string RedoActionName {
239 get {
240 return redo_action_name;
244 [DefaultValue(0)]
245 [Localizable(true)]
246 [MonoTODO("Teach TextControl.RecalculateLine to consider the right margin as well")]
247 public int RightMargin {
248 get {
249 return margin_right;
252 set {
253 margin_right = value;
257 [Browsable(false)]
258 [DefaultValue("")]
259 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
260 public string Rtf {
261 get {
262 Line start_line;
263 Line end_line;
265 start_line = document.GetLine(1);
266 end_line = document.GetLine(document.Lines);
267 return GenerateRTF(start_line, 0, end_line, end_line.text.Length).ToString();
270 set {
271 MemoryStream data;
273 document.Empty();
274 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
276 InsertRTFFromStream(data, 0, 1);
278 data.Close();
280 Invalidate();
284 [DefaultValue(RichTextBoxScrollBars.Both)]
285 [Localizable(true)]
286 public RichTextBoxScrollBars ScrollBars {
287 get {
288 return scrollbars;
291 set {
292 scrollbars = value;
296 [Browsable(false)]
297 [DefaultValue("")]
298 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
299 public string SelectedRtf {
300 get {
301 return GenerateRTF(document.selection_start.line, document.selection_start.pos, document.selection_end.line, document.selection_end.pos).ToString();
304 set {
305 MemoryStream data;
306 int x;
307 int y;
308 int sel_start;
309 int chars;
310 Line line;
311 LineTag tag;
313 if (document.selection_visible) {
314 document.ReplaceSelection("", false);
317 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
319 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
320 InsertRTFFromStream(data, document.selection_start.pos, document.selection_start.line.line_no, out x, out y, out chars);
321 data.Close();
323 document.CharIndexToLineTag(sel_start + chars + (y - document.selection_start.line.line_no) * 2, out line, out tag, out sel_start);
324 document.SetSelection(line, sel_start);
325 document.PositionCaret(line, sel_start);
326 document.DisplayCaret();
327 ScrollToCaret();
328 OnTextChanged(EventArgs.Empty);
332 [Browsable(false)]
333 [DefaultValue("")]
334 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
335 public override string SelectedText {
336 get {
337 return base.SelectedText;
340 set {
341 base.SelectedText = value;
345 [Browsable(false)]
346 [DefaultValue(HorizontalAlignment.Left)]
347 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
348 public HorizontalAlignment SelectionAlignment {
349 get {
350 HorizontalAlignment align;
351 Line start;
352 Line end;
353 Line line;
355 start = document.ParagraphStart(document.selection_start.line);
356 align = start.alignment;
358 end = document.ParagraphEnd(document.selection_end.line);
360 line = start;
362 while (true) {
363 if (line.alignment != align) {
364 return HorizontalAlignment.Left;
367 if (line == end) {
368 break;
370 line = document.GetLine(line.line_no + 1);
373 return align;
376 set {
377 Line start;
378 Line end;
379 Line line;
381 start = document.ParagraphStart(document.selection_start.line);
383 end = document.ParagraphEnd(document.selection_end.line);
385 line = start;
387 while (true) {
388 line.alignment = value;
390 if (line == end) {
391 break;
393 line = document.GetLine(line.line_no + 1);
395 this.CalculateDocument();
399 [Browsable(false)]
400 [DefaultValue(false)]
401 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
402 [MonoTODO]
403 public bool SelectionBullet {
404 get {
405 return false;
408 set {
412 [Browsable(false)]
413 [DefaultValue(0)]
414 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
415 [MonoTODO]
416 public int SelectionCharOffset {
417 get {
418 return 0;
421 set {
425 [Browsable(false)]
426 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
427 public Color SelectionColor {
428 get {
429 Color color;
430 LineTag start;
431 LineTag end;
432 LineTag tag;
434 start = document.selection_start.tag;
435 end = document.selection_end.tag;
436 color = ((SolidBrush)document.selection_start.tag.color).Color;
438 tag = start;
439 while (true) {
440 if (!color.Equals(((SolidBrush)tag.color).Color)) {
441 return Color.Empty;
444 if (tag == end) {
445 break;
448 tag = document.NextTag(tag);
450 if (tag == null) {
451 break;
455 return color;
458 set {
459 FontDefinition attributes;
460 int sel_start;
461 int sel_end;
463 attributes = new FontDefinition();
464 attributes.color = value;
466 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
467 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
469 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
471 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
472 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
474 document.UpdateView(document.selection_start.line, 0);
475 document.AlignCaret();
479 [Browsable(false)]
480 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
481 public Font SelectionFont {
482 get {
483 Font font;
484 LineTag start;
485 LineTag end;
486 LineTag tag;
488 start = document.selection_start.tag;
489 end = document.selection_end.tag;
490 font = document.selection_start.tag.font;
492 tag = start;
493 while (true) {
494 if (!font.Equals(tag.font)) {
495 return null;
498 if (tag == end) {
499 break;
502 tag = document.NextTag(tag);
504 if (tag == null) {
505 break;
509 return font;
512 set {
513 FontDefinition attributes;
514 int sel_start;
515 int sel_end;
517 attributes = new FontDefinition();
518 attributes.font_obj = value;
520 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
521 sel_end = document.LineTagToCharIndex(document.selection_end.line, document.selection_end.pos);
523 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
525 document.CharIndexToLineTag(sel_start, out document.selection_start.line, out document.selection_start.tag, out document.selection_start.pos);
526 document.CharIndexToLineTag(sel_end, out document.selection_end.line, out document.selection_end.tag, out document.selection_end.pos);
528 document.UpdateView(document.selection_start.line, 0);
529 document.AlignCaret();
534 [Browsable(false)]
535 [DefaultValue(0)]
536 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
537 [MonoTODO]
538 public int SelectionHangingIndent {
539 get {
540 return 0;
543 set {
547 [Browsable(false)]
548 [DefaultValue(0)]
549 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
550 [MonoTODO]
551 public int SelectionIndent {
552 get {
553 return 0;
556 set {
560 [Browsable(false)]
561 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
562 public override int SelectionLength {
563 get {
564 return base.SelectionLength;
567 set {
568 base.SelectionLength = value;
572 [Browsable(false)]
573 [DefaultValue(false)]
574 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
575 [MonoTODO]
576 public bool SelectionProtected {
577 get {
578 return false;
581 set {
585 [Browsable(false)]
586 [DefaultValue(0)]
587 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
588 [MonoTODO]
589 public int SelectionRightIndent {
590 get {
591 return 0;
594 set {
598 [Browsable(false)]
599 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
600 [MonoTODO]
601 public int[] SelectionTabs {
602 get {
603 return new int[0];
606 set {
610 [Browsable(false)]
611 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
612 public RichTextBoxSelectionTypes SelectionType {
613 get {
614 if (document.selection_start == document.selection_end) {
615 return RichTextBoxSelectionTypes.Empty;
618 // Lazy, but works
619 if (SelectedText.Length > 1) {
620 return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
623 return RichTextBoxSelectionTypes.Text;
627 [DefaultValue(false)]
628 [MonoTODO]
629 public bool ShowSelectionMargin {
630 get {
631 return false;
634 set {
638 [Localizable(true)]
639 public override string Text {
640 get {
641 return base.Text;
644 set {
645 base.Text = value;
649 [Browsable(false)]
650 public override int TextLength {
651 get {
652 return base.TextLength;
656 [Browsable(false)]
657 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
658 public string UndoActionName {
659 get {
660 return document.undo.UndoName;
664 [Localizable(true)]
665 [DefaultValue(1)]
666 public float ZoomFactor {
667 get {
668 return zoom;
671 set {
672 zoom = value;
675 #endregion // Public Instance Properties
677 #region Protected Instance Properties
678 protected override CreateParams CreateParams {
679 get {
680 return base.CreateParams;
684 protected override Size DefaultSize {
685 get {
686 return new Size(100, 96);
689 #endregion // Protected Instance Properties
691 #region Public Instance Methods
692 public bool CanPaste(DataFormats.Format clipFormat) {
693 if ((clipFormat.Name == DataFormats.Rtf) ||
694 (clipFormat.Name == DataFormats.Text) ||
695 (clipFormat.Name == DataFormats.UnicodeText)) {
696 return true;
698 return false;
701 public int Find(char[] characterSet) {
702 return Find(characterSet, -1, -1);
705 public int Find(char[] characterSet, int start) {
706 return Find(characterSet, start, -1);
709 public int Find(char[] characterSet, int start, int end) {
710 Document.Marker start_mark;
711 Document.Marker end_mark;
712 Document.Marker result;
714 if (start == -1) {
715 document.GetMarker(out start_mark, true);
716 } else {
717 Line line;
718 LineTag tag;
719 int pos;
721 start_mark = new Document.Marker();
723 document.CharIndexToLineTag(start, out line, out tag, out pos);
724 start_mark.line = line;
725 start_mark.tag = tag;
726 start_mark.pos = pos;
729 if (end == -1) {
730 document.GetMarker(out end_mark, false);
731 } else {
732 Line line;
733 LineTag tag;
734 int pos;
736 end_mark = new Document.Marker();
738 document.CharIndexToLineTag(end, out line, out tag, out pos);
739 end_mark.line = line;
740 end_mark.tag = tag;
741 end_mark.pos = pos;
744 if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
745 return document.LineTagToCharIndex(result.line, result.pos);
748 return -1;
751 public int Find(string str) {
752 return Find(str, -1, -1, RichTextBoxFinds.None);
755 public int Find(string str, int start, int end, RichTextBoxFinds options) {
756 Document.Marker start_mark;
757 Document.Marker end_mark;
758 Document.Marker result;
760 if (start == -1) {
761 document.GetMarker(out start_mark, true);
762 } else {
763 Line line;
764 LineTag tag;
765 int pos;
767 start_mark = new Document.Marker();
769 document.CharIndexToLineTag(start, out line, out tag, out pos);
771 start_mark.line = line;
772 start_mark.tag = tag;
773 start_mark.pos = pos;
776 if (end == -1) {
777 document.GetMarker(out end_mark, false);
778 } else {
779 Line line;
780 LineTag tag;
781 int pos;
783 end_mark = new Document.Marker();
785 document.CharIndexToLineTag(end, out line, out tag, out pos);
787 end_mark.line = line;
788 end_mark.tag = tag;
789 end_mark.pos = pos;
792 if (document.Find(str, start_mark, end_mark, out result, options)) {
793 return document.LineTagToCharIndex(result.line, result.pos);
796 return -1;
799 public int Find(string str, int start, RichTextBoxFinds options) {
800 return Find(str, start, -1, options);
803 public int Find(string str, RichTextBoxFinds options) {
804 return Find(str, -1, -1, options);
807 public char GetCharFromPosition(Point pt) {
808 LineTag tag;
809 int pos;
811 PointToTagPos(pt, out tag, out pos);
813 if (pos >= tag.line.text.Length) {
814 return '\n';
817 return tag.line.text[pos];
821 public int GetCharIndexFromPosition(Point pt) {
822 LineTag tag;
823 int pos;
825 PointToTagPos(pt, out tag, out pos);
827 return document.LineTagToCharIndex(tag.line, pos);
830 public int GetLineFromCharIndex(int index) {
831 Line line;
832 LineTag tag;
833 int pos;
835 document.CharIndexToLineTag(index, out line, out tag, out pos);
837 return line.LineNo - 1;
840 public Point GetPositionFromCharIndex(int index) {
841 Line line;
842 LineTag tag;
843 int pos;
845 document.CharIndexToLineTag(index, out line, out tag, out pos);
847 return new Point((int)line.widths[pos] + 1, line.Y + 1);
850 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
851 document.Empty();
853 // FIXME - ignoring unicode
854 if (fileType == RichTextBoxStreamType.PlainText) {
855 StringBuilder sb;
856 int count;
857 byte[] buffer;
859 try {
860 sb = new StringBuilder((int)data.Length);
861 buffer = new byte[1024];
862 } catch {
863 throw new IOException("Not enough memory to load document");
866 count = 0;
867 while (count < data.Length) {
868 count += data.Read(buffer, count, 1024);
869 sb.Append(buffer);
871 base.Text = sb.ToString();
872 return;
875 InsertRTFFromStream(data, 0, 1);
877 document.PositionCaret (document.GetLine (1), 0);
878 document.SetSelectionToCaret (true);
879 ScrollToCaret ();
882 [MonoTODO("Make smarter RTF detection?")]
883 public void LoadFile(string path) {
884 if (path.EndsWith(".rtf")) {
885 LoadFile(path, RichTextBoxStreamType.RichText);
886 } else {
887 LoadFile(path, RichTextBoxStreamType.PlainText);
891 public void LoadFile(string path, RichTextBoxStreamType fileType) {
892 FileStream data;
894 data = null;
897 try {
898 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
900 LoadFile(data, fileType);
902 #if !DEBUG
903 catch {
904 throw new IOException("Could not open file " + path);
906 #endif
907 finally {
908 if (data != null) {
909 data.Close();
914 public void Paste(DataFormats.Format clipFormat) {
915 base.Paste(Clipboard.GetDataObject(), clipFormat, false);
918 [MonoTODO()]
919 public void Redo() {
922 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
923 Encoding encoding;
924 int i;
925 Byte[] bytes;
928 if (fileType == RichTextBoxStreamType.UnicodePlainText) {
929 encoding = Encoding.Unicode;
930 } else {
931 encoding = Encoding.ASCII;
934 switch(fileType) {
935 case RichTextBoxStreamType.PlainText:
936 case RichTextBoxStreamType.TextTextOleObjs:
937 case RichTextBoxStreamType.UnicodePlainText: {
938 if (!multiline) {
939 bytes = encoding.GetBytes(document.Root.text.ToString());
940 data.Write(bytes, 0, bytes.Length);
941 return;
944 for (i = 1; i < document.Lines; i++) {
945 bytes = encoding.GetBytes(document.GetLine(i).text.ToString() + Environment.NewLine);
946 data.Write(bytes, 0, bytes.Length);
948 bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
949 data.Write(bytes, 0, bytes.Length);
950 return;
954 // If we're here we're saving RTF
955 Line start_line;
956 Line end_line;
957 StringBuilder rtf;
958 int current;
959 int total;
961 start_line = document.GetLine(1);
962 end_line = document.GetLine(document.Lines);
963 rtf = GenerateRTF(start_line, 0, end_line, end_line.text.Length);
964 total = rtf.Length;
965 bytes = new Byte[4096];
967 // Let's chunk it so we don't use up all memory...
968 for (i = 0; i < total; i += 1024) {
969 if ((i + 1024) < total) {
970 current = encoding.GetBytes(rtf.ToString(i, 1024), 0, 1024, bytes, 0);
971 } else {
972 current = total - i;
973 current = encoding.GetBytes(rtf.ToString(i, current), 0, current, bytes, 0);
975 data.Write(bytes, 0, current);
979 public void SaveFile(string path) {
980 if (path.EndsWith(".rtf")) {
981 SaveFile(path, RichTextBoxStreamType.RichText);
982 } else {
983 SaveFile(path, RichTextBoxStreamType.PlainText);
987 public void SaveFile(string path, RichTextBoxStreamType fileType) {
988 FileStream data;
990 data = null;
992 // try {
993 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
994 SaveFile(data, fileType);
995 // }
997 // catch {
998 // throw new IOException("Could not write document to file " + path);
999 // }
1001 // finally {
1002 if (data != null) {
1003 data.Close();
1005 // }
1008 #endregion // Public Instance Methods
1010 #region Protected Instance Methods
1011 protected virtual object CreateRichEditOleCallback() {
1012 throw new NotImplementedException();
1015 protected override void OnBackColorChanged(EventArgs e) {
1016 base.OnBackColorChanged (e);
1019 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1020 ContentsResizedEventHandler eh = (ContentsResizedEventHandler)(Events [ContentsResizedEvent]);
1021 if (eh != null)
1022 eh (this, e);
1025 protected override void OnContextMenuChanged(EventArgs e) {
1026 base.OnContextMenuChanged (e);
1029 protected override void OnHandleCreated(EventArgs e) {
1030 base.OnHandleCreated (e);
1033 protected override void OnHandleDestroyed(EventArgs e) {
1034 base.OnHandleDestroyed (e);
1037 protected virtual void OnHScroll(EventArgs e) {
1038 EventHandler eh = (EventHandler)(Events [HScrollEvent]);
1039 if (eh != null)
1040 eh (this, e);
1043 [MonoTODO("Determine when to call this")]
1044 protected virtual void OnImeChange(EventArgs e) {
1045 EventHandler eh = (EventHandler)(Events [ImeChangeEvent]);
1046 if (eh != null)
1047 eh (this, e);
1050 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1051 LinkClickedEventHandler eh = (LinkClickedEventHandler)(Events [LinkClickedEvent]);
1052 if (eh != null)
1053 eh (this, e);
1056 protected virtual void OnProtected(EventArgs e) {
1057 EventHandler eh = (EventHandler)(Events [ProtectedEvent]);
1058 if (eh != null)
1059 eh (this, e);
1062 protected override void OnRightToLeftChanged(EventArgs e) {
1063 base.OnRightToLeftChanged (e);
1066 protected virtual void OnSelectionChanged(EventArgs e) {
1067 EventHandler eh = (EventHandler)(Events [SelectionChangedEvent]);
1068 if (eh != null)
1069 eh (this, e);
1072 protected override void OnSystemColorsChanged(EventArgs e) {
1073 base.OnSystemColorsChanged (e);
1076 protected override void OnTextChanged(EventArgs e) {
1077 base.OnTextChanged (e);
1080 protected virtual void OnVScroll(EventArgs e) {
1081 EventHandler eh = (EventHandler)(Events [VScrollEvent]);
1082 if (eh != null)
1083 eh (this, e);
1086 protected override void WndProc(ref Message m) {
1087 base.WndProc (ref m);
1089 #endregion // Protected Instance Methods
1091 #region Events
1092 static object ContentsResizedEvent = new object ();
1093 static object HScrollEvent = new object ();
1094 static object ImeChangeEvent = new object ();
1095 static object LinkClickedEvent = new object ();
1096 static object ProtectedEvent = new object ();
1097 static object SelectionChangedEvent = new object ();
1098 static object VScrollEvent = new object ();
1100 [Browsable(false)]
1101 [EditorBrowsable(EditorBrowsableState.Never)]
1102 public new event EventHandler BackgroundImageChanged {
1103 add { base.BackgroundImageChanged += value; }
1104 remove { base.BackgroundImageChanged -= value; }
1107 public event ContentsResizedEventHandler ContentsResized {
1108 add { Events.AddHandler (ContentsResizedEvent, value); }
1109 remove { Events.RemoveHandler (ContentsResizedEvent, value); }
1112 [Browsable(false)]
1113 [EditorBrowsable(EditorBrowsableState.Never)]
1114 public new event EventHandler DoubleClick {
1115 add { base.DoubleClick += value; }
1116 remove { base.DoubleClick -= value; }
1119 [Browsable(false)]
1120 [EditorBrowsable(EditorBrowsableState.Never)]
1121 public new event DragEventHandler DragDrop {
1122 add { base.DragDrop += value; }
1123 remove { base.DragDrop -= value; }
1126 [Browsable(false)]
1127 [EditorBrowsable(EditorBrowsableState.Never)]
1128 public new event DragEventHandler DragEnter {
1129 add { base.DragEnter += value; }
1130 remove { base.DragEnter -= value; }
1133 [Browsable(false)]
1134 [EditorBrowsable(EditorBrowsableState.Never)]
1135 public new event EventHandler DragLeave {
1136 add { base.DragLeave += value; }
1137 remove { base.DragLeave -= value; }
1141 [Browsable(false)]
1142 [EditorBrowsable(EditorBrowsableState.Never)]
1143 public new event DragEventHandler DragOver {
1144 add { base.DragOver += value; }
1145 remove { base.DragOver -= value; }
1149 [Browsable(false)]
1150 [EditorBrowsable(EditorBrowsableState.Never)]
1151 public new event GiveFeedbackEventHandler GiveFeedback {
1152 add { base.GiveFeedback += value; }
1153 remove { base.GiveFeedback -= value; }
1156 public event EventHandler HScroll {
1157 add { Events.AddHandler (HScrollEvent, value); }
1158 remove { Events.RemoveHandler (HScrollEvent, value); }
1161 public event EventHandler ImeChange {
1162 add { Events.AddHandler (ImeChangeEvent, value); }
1163 remove { Events.RemoveHandler (ImeChangeEvent, value); }
1166 public event LinkClickedEventHandler LinkClicked {
1167 add { Events.AddHandler (LinkClickedEvent, value); }
1168 remove { Events.RemoveHandler (LinkClickedEvent, value); }
1171 public event EventHandler Protected {
1172 add { Events.AddHandler (ProtectedEvent, value); }
1173 remove { Events.RemoveHandler (ProtectedEvent, value); }
1176 [Browsable(false)]
1177 [EditorBrowsable(EditorBrowsableState.Never)]
1178 public new event QueryContinueDragEventHandler QueryContinueDrag {
1179 add { base.QueryContinueDrag += value; }
1180 remove { base.QueryContinueDrag -= value; }
1183 public event EventHandler SelectionChanged {
1184 add { Events.AddHandler (SelectionChangedEvent, value); }
1185 remove { Events.RemoveHandler (SelectionChangedEvent, value); }
1188 public event EventHandler VScroll {
1189 add { Events.AddHandler (VScrollEvent, value); }
1190 remove { Events.RemoveHandler (VScrollEvent, value); }
1192 #endregion // Events
1194 #region Private Methods
1196 internal override void SelectWord ()
1198 document.ExpandSelection(CaretSelection.Word, false);
1201 private void HandleControl(RTF.RTF rtf) {
1202 // Console.WriteLine ("HANDLING MAJOR: {0} MINOR: {1}", rtf.Major, rtf.Minor);
1203 switch(rtf.Major) {
1204 case RTF.Major.Unicode: {
1205 switch(rtf.Minor) {
1206 case Minor.UnicodeCharBytes: {
1207 rtf_skip_width = rtf.Param;
1208 break;
1211 case Minor.UnicodeChar: {
1212 rtf_skip_count += rtf_skip_width;
1213 rtf_line.Append((char)rtf.Param);
1214 break;
1217 break;
1220 case RTF.Major.Destination: {
1221 // Console.Write("[Got Destination control {0}]", rtf.Minor);
1222 rtf.SkipGroup();
1223 break;
1226 case RTF.Major.CharAttr: {
1227 switch(rtf.Minor) {
1228 case Minor.ForeColor: {
1229 System.Windows.Forms.RTF.Color color;
1231 color = System.Windows.Forms.RTF.Color.GetColor(rtf, rtf.Param);
1233 if (color != null) {
1234 FlushText(rtf, false);
1235 if (color.Red == -1 && color.Green == -1 && color.Blue == -1) {
1236 this.rtf_color = new SolidBrush(ForeColor);
1237 } else {
1238 this.rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1240 FlushText (rtf, false);
1242 break;
1245 case Minor.FontSize: {
1246 FlushText(rtf, false);
1247 this.rtf_rtffont_size = rtf.Param / 2;
1248 break;
1251 case Minor.FontNum: {
1252 System.Windows.Forms.RTF.Font font;
1254 font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1255 if (font != null) {
1256 FlushText(rtf, false);
1257 this.rtf_rtffont = font;
1259 break;
1262 case Minor.Plain: {
1263 FlushText(rtf, false);
1264 rtf_rtfstyle = FontStyle.Regular;
1265 break;
1268 case Minor.Bold: {
1269 FlushText(rtf, false);
1270 if (rtf.Param == RTF.RTF.NoParam) {
1271 rtf_rtfstyle |= FontStyle.Bold;
1272 } else {
1273 rtf_rtfstyle &= ~FontStyle.Bold;
1275 break;
1278 case Minor.Italic: {
1279 FlushText(rtf, false);
1280 if (rtf.Param == RTF.RTF.NoParam) {
1281 rtf_rtfstyle |= FontStyle.Italic;
1282 } else {
1283 rtf_rtfstyle &= ~FontStyle.Italic;
1285 break;
1288 case Minor.StrikeThru: {
1289 FlushText(rtf, false);
1290 if (rtf.Param == RTF.RTF.NoParam) {
1291 rtf_rtfstyle |= FontStyle.Strikeout;
1292 } else {
1293 rtf_rtfstyle &= ~FontStyle.Strikeout;
1295 break;
1298 case Minor.Underline: {
1299 FlushText(rtf, false);
1300 if (rtf.Param == RTF.RTF.NoParam) {
1301 rtf_rtfstyle |= FontStyle.Underline;
1302 } else {
1303 rtf_rtfstyle = rtf_rtfstyle & ~FontStyle.Underline;
1305 break;
1308 case Minor.NoUnderline: {
1309 FlushText(rtf, false);
1310 rtf_rtfstyle &= ~FontStyle.Underline;
1311 break;
1314 break;
1317 case RTF.Major.SpecialChar: {
1318 //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1319 SpecialChar(rtf);
1320 break;
1325 private void SpecialChar(RTF.RTF rtf) {
1326 switch(rtf.Minor) {
1327 case Minor.Page:
1328 case Minor.Sect:
1329 case Minor.Row:
1330 case Minor.Line:
1331 case Minor.Par: {
1332 FlushText(rtf, true);
1333 break;
1336 case Minor.Cell: {
1337 Console.Write(" ");
1338 break;
1341 case Minor.NoBrkSpace: {
1342 Console.Write(" ");
1343 break;
1346 case Minor.Tab: {
1347 rtf_line.Append ("\t");
1348 // FlushText (rtf, false);
1349 break;
1352 case Minor.NoReqHyphen:
1353 case Minor.NoBrkHyphen: {
1354 rtf_line.Append ("-");
1355 // FlushText (rtf, false);
1356 break;
1359 case Minor.Bullet: {
1360 Console.WriteLine("*");
1361 break;
1364 case Minor.WidowCtrl:
1365 break;
1367 case Minor.EmDash: {
1368 rtf_line.Append ("\u2014");
1369 break;
1372 case Minor.EnDash: {
1373 rtf_line.Append ("\u2013");
1374 break;
1377 case Minor.LQuote: {
1378 Console.Write("\u2018");
1379 break;
1382 case Minor.RQuote: {
1383 Console.Write("\u2019");
1384 break;
1387 case Minor.LDblQuote: {
1388 Console.Write("\u201C");
1389 break;
1392 case Minor.RDblQuote: {
1393 Console.Write("\u201D");
1394 break;
1397 default: {
1398 // Console.WriteLine ("skipped special char: {0}", rtf.Minor);
1399 // rtf.SkipGroup();
1400 break;
1405 private void HandleText(RTF.RTF rtf) {
1406 if (rtf_skip_count > 0) {
1407 rtf_skip_count--;
1408 return;
1411 if ((RTF.StandardCharCode)rtf.Minor != RTF.StandardCharCode.nothing) {
1412 rtf_line.Append(rtf_text_map[(RTF.StandardCharCode)rtf.Minor]);
1413 } else {
1414 if ((int)rtf.Major > 31 && (int)rtf.Major < 128) {
1415 rtf_line.Append((char)rtf.Major);
1416 } else {
1417 //rtf_line.Append((char)rtf.Major);
1418 Console.Write("[Literal:0x{0:X2}]", (int)rtf.Major);
1423 private void FlushText(RTF.RTF rtf, bool newline) {
1424 int length;
1425 Font font;
1427 length = rtf_line.Length;
1428 if (!newline && (length == 0)) {
1429 return;
1432 if (rtf_rtffont == null) {
1433 // First font in table is default
1434 rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont(rtf, 0);
1437 font = new Font(rtf_rtffont.Name, rtf_rtffont_size, rtf_rtfstyle);
1439 if (rtf_color == null) {
1440 System.Windows.Forms.RTF.Color color;
1442 // First color in table is default
1443 color = System.Windows.Forms.RTF.Color.GetColor(rtf, 0);
1445 if ((color == null) || (color.Red == -1 && color.Green == -1 && color.Blue == -1)) {
1446 rtf_color = new SolidBrush(ForeColor);
1447 } else {
1448 rtf_color = new SolidBrush(Color.FromArgb(color.Red, color.Green, color.Blue));
1453 rtf_chars += rtf_line.Length;
1455 if (rtf_cursor_x == 0) {
1456 document.Add(rtf_cursor_y, rtf_line.ToString(), rtf_rtfalign, font, rtf_color);
1457 } else {
1458 Line line;
1460 line = document.GetLine(rtf_cursor_y);
1461 if (rtf_line.Length > 0) {
1462 document.InsertString(line, rtf_cursor_x, rtf_line.ToString());
1463 document.FormatText(line, rtf_cursor_x + 1, line, rtf_cursor_x + 1 + length, font, rtf_color, null, FormatSpecified.Font | FormatSpecified.Color); // FormatText is 1-based
1465 if (newline) {
1466 document.Split(line, rtf_cursor_x + length);
1470 if (newline) {
1471 rtf_cursor_x = 0;
1472 rtf_cursor_y++;
1473 } else {
1474 rtf_cursor_x += length;
1476 rtf_line.Length = 0; // Empty line
1479 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1480 int x;
1481 int y;
1482 int chars;
1484 InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1487 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y, out int to_x, out int to_y, out int chars) {
1488 RTF.RTF rtf;
1490 rtf = new RTF.RTF(data);
1492 // Prepare
1493 rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1494 rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1496 rtf_skip_width = 0;
1497 rtf_skip_count = 0;
1498 rtf_line = new StringBuilder();
1499 rtf_color = null;
1500 rtf_rtffont_size = (int)this.Font.Size;
1501 rtf_rtfalign = HorizontalAlignment.Left;
1502 rtf_rtfstyle = FontStyle.Regular;
1503 rtf_rtffont = null;
1504 rtf_cursor_x = cursor_x;
1505 rtf_cursor_y = cursor_y;
1506 rtf_chars = 0;
1507 rtf.DefaultFont(this.Font.Name);
1509 rtf_text_map = new RTF.TextMap();
1510 RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1512 document.SuspendRecalc ();
1514 try {
1515 rtf.Read(); // That's it
1516 FlushText(rtf, false);
1521 catch (RTF.RTFException e) {
1522 #if DEBUG
1523 throw e;
1524 #endif
1525 // Seems to be plain text or broken RTF
1526 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1529 to_x = rtf_cursor_x;
1530 to_y = rtf_cursor_y;
1531 chars = rtf_chars;
1533 document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1534 document.ResumeRecalc (true);
1536 document.Invalidate (document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1539 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1540 OnHScroll(e);
1543 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1544 OnVScroll(e);
1547 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1548 Point p;
1550 p = pt;
1552 if (p.X >= document.ViewPortWidth) {
1553 p.X = document.ViewPortWidth - 1;
1554 } else if (p.X < 0) {
1555 p.X = 0;
1558 if (p.Y >= document.ViewPortHeight) {
1559 p.Y = document.ViewPortHeight - 1;
1560 } else if (p.Y < 0) {
1561 p.Y = 0;
1564 tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1567 private void EmitRTFFontProperties(StringBuilder rtf, int prev_index, int font_index, Font prev_font, Font font) {
1568 if (prev_index != font_index) {
1569 rtf.Append(String.Format("\\f{0}", font_index)); // Font table entry
1572 if ((prev_font == null) || (prev_font.Size != font.Size)) {
1573 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2))); // Font size
1576 if ((prev_font == null) || (font.Bold != prev_font.Bold)) {
1577 if (font.Bold) {
1578 rtf.Append("\\b");
1579 } else {
1580 if (prev_font != null) {
1581 rtf.Append("\\b0");
1586 if ((prev_font == null) || (font.Italic != prev_font.Italic)) {
1587 if (font.Italic) {
1588 rtf.Append("\\i");
1589 } else {
1590 if (prev_font != null) {
1591 rtf.Append("\\i0");
1596 if ((prev_font == null) || (font.Strikeout != prev_font.Strikeout)) {
1597 if (font.Strikeout) {
1598 rtf.Append("\\strike");
1599 } else {
1600 if (prev_font != null) {
1601 rtf.Append("\\strike0");
1606 if ((prev_font == null) || (font.Underline != prev_font.Underline)) {
1607 if (font.Underline) {
1608 rtf.Append("\\ul");
1609 } else {
1610 if (prev_font != null) {
1611 rtf.Append("\\ul0");
1617 [MonoTODO("Emit unicode and other special characters properly")]
1618 private void EmitRTFText(StringBuilder rtf, string text) {
1619 rtf.Append(text);
1622 // start_pos and end_pos are 0-based
1623 private StringBuilder GenerateRTF(Line start_line, int start_pos, Line end_line, int end_pos) {
1624 StringBuilder sb;
1625 ArrayList fonts;
1626 ArrayList colors;
1627 Color color;
1628 Font font;
1629 Line line;
1630 LineTag tag;
1631 int pos;
1632 int line_no;
1633 int line_len;
1634 int i;
1635 int length;
1637 sb = new StringBuilder();
1638 fonts = new ArrayList(10);
1639 colors = new ArrayList(10);
1641 // Two runs, first we parse to determine tables;
1642 // and unlike most of our processing here we work on tags
1644 line = start_line;
1645 line_no = start_line.line_no;
1646 pos = start_pos;
1648 // Add default font and color; to optimize document content we don't
1649 // use this.Font and this.ForeColor but the font/color from the first tag
1650 tag = LineTag.FindTag(start_line, pos);
1651 font = tag.font;
1652 color = ((SolidBrush)tag.color).Color;
1653 fonts.Add(font.Name);
1654 colors.Add(color);
1656 while (line_no <= end_line.line_no) {
1657 line = document.GetLine(line_no);
1658 tag = LineTag.FindTag(line, pos);
1660 if (line_no != end_line.line_no) {
1661 line_len = line.text.Length;
1662 } else {
1663 line_len = end_pos;
1666 while (pos < line_len) {
1667 if (tag.font.Name != font.Name) {
1668 font = tag.font;
1669 if (!fonts.Contains(font.Name)) {
1670 fonts.Add(font.Name);
1674 if (((SolidBrush)tag.color).Color != color) {
1675 color = ((SolidBrush)tag.color).Color;
1676 if (!colors.Contains(color)) {
1677 colors.Add(color);
1681 pos = tag.start + tag.length - 1;
1682 tag = tag.next;
1684 pos = 0;
1685 line_no++;
1688 // We have the tables, emit the header
1689 sb.Append("{\\rtf1\\ansi");
1690 sb.Append("\\ansicpg1252"); // FIXME - is this correct?
1692 // Default Font
1693 sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1695 // Default Language
1696 sb.Append("\\deflang1033\n"); // FIXME - always 1033?
1698 // Emit the font table
1699 sb.Append("{\\fonttbl");
1700 for (i = 0; i < fonts.Count; i++) {
1701 sb.Append(String.Format("{{\\f{0}", i)); // {Font
1702 sb.Append("\\fnil"); // Family
1703 sb.Append("\\fcharset0 "); // Charset ANSI<space>
1704 sb.Append((string)fonts[i]); // Font name
1705 sb.Append(";}"); // }
1707 sb.Append("}\n");
1709 // Emit the color table (if needed)
1710 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))) {
1711 sb.Append("{\\colortbl "); // Header and NO! default color
1712 for (i = 0; i < colors.Count; i++) {
1713 sb.Append(String.Format("\\red{0}", ((Color)colors[i]).R));
1714 sb.Append(String.Format("\\green{0}", ((Color)colors[i]).G));
1715 sb.Append(String.Format("\\blue{0}", ((Color)colors[i]).B));
1716 sb.Append(";");
1718 sb.Append("}\n");
1721 sb.Append("{\\*\\generator Mono RichTextBox;}");
1722 // Emit initial paragraph settings
1723 tag = LineTag.FindTag(start_line, start_pos);
1724 sb.Append("\\pard"); // Reset to default paragraph properties
1725 EmitRTFFontProperties(sb, -1, fonts.IndexOf(tag.font.Name), null, tag.font); // Font properties
1726 sb.Append(" "); // Space separator
1728 font = tag.font;
1729 color = (Color)colors[0];
1730 line = start_line;
1731 line_no = start_line.line_no;
1732 pos = start_pos;
1734 while (line_no <= end_line.line_no) {
1735 line = document.GetLine(line_no);
1736 tag = LineTag.FindTag(line, pos);
1738 if (line_no != end_line.line_no) {
1739 line_len = line.text.Length;
1740 } else {
1741 line_len = end_pos;
1744 while (pos < line_len) {
1745 length = sb.Length;
1747 if (tag.font != font) {
1748 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.font.Name), font, tag.font);
1749 font = tag.font;
1752 if (((SolidBrush)tag.color).Color != color) {
1753 color = ((SolidBrush)tag.color).Color;
1754 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
1756 if (length != sb.Length) {
1757 sb.Append(" "); // Emit space to separate keywords from text
1760 // Emit the string itself
1761 if (line_no != end_line.line_no) {
1762 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1763 } else {
1764 if (end_pos < (tag.start + tag.length - 1)) {
1765 // Emit partial tag only, end_pos is inside this tag
1766 EmitRTFText(sb, tag.line.text.ToString(pos, end_pos - pos));
1767 } else {
1768 EmitRTFText(sb, tag.line.text.ToString(pos, tag.start + tag.length - pos - 1));
1772 pos = tag.start + tag.length - 1;
1773 tag = tag.next;
1775 if (pos >= line.text.Length) {
1776 if (!line.soft_break) {
1777 sb.Append("\\par\n");
1780 pos = 0;
1781 line_no++;
1784 sb.Append("}\n");
1786 return sb;
1788 #endregion // Private Methods