merge r67228-r67235, r67237, r67251 and r67256-67259 to trunk (they are
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / RichTextBox.cs
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.
19 //
20 // Copyright (c) 2005-2006 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    <pbartok@novell.com>
24 //
25 //
26
27 // NOT COMPLETE
28
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;
36
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;
48
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
62
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;
77
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);
86                 }
87                 #endregion      // Public Constructors
88
89                 #region Private & Internal Methods
90                 private void RichTextBox_LostFocus(object sender, EventArgs e) {
91                         has_focus = false;
92                         Invalidate();
93                 }
94
95                 private void RichTextBox_GotFocus(object sender, EventArgs e) {
96                         has_focus = true;
97                         Invalidate();
98                 }
99                 #endregion      // Private & Internal Methods
100
101                 #region Public Instance Properties
102                 public override bool AllowDrop {
103                         get {
104                                 return base.AllowDrop;
105                         }
106
107                         set {
108                                 base.AllowDrop = value;
109                         }
110                 }
111
112                 [DefaultValue(false)]
113                 [Localizable(true)]
114                 public override bool AutoSize {
115                         get {
116                                 return auto_size;
117                         }
118
119                         set {
120                                 base.AutoSize = value;
121                         }
122                 }
123
124                 [DefaultValue(false)]
125                 public bool AutoWordSelection {
126                         get {
127                                 return auto_word_select;
128                         }
129
130                         set {
131                                 auto_word_select = true;
132                         }
133                 }
134
135                 [Browsable(false)]
136                 [EditorBrowsable(EditorBrowsableState.Never)]
137                 public override System.Drawing.Image BackgroundImage {
138                         get {
139                                 return background_image;
140                         }
141
142                         set {
143                                 base.BackgroundImage = value;
144                         }
145                 }
146
147                 [DefaultValue(0)]
148                 [Localizable(true)]
149                 public int BulletIndent {
150                         get {
151                                 return bullet_indent;
152                         }
153
154                         set {
155                                 bullet_indent = value;
156                         }
157                 }
158
159                 [Browsable(false)]
160                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
161                 public bool CanRedo {
162                         get {
163                                 return can_redo;
164                         }
165                 }
166
167                 [DefaultValue(true)]
168                 public bool DetectUrls {
169                         get {
170                                 return detect_urls;
171                         }
172
173                         set {
174                                 detect_urls = true;
175                         }
176                 }
177
178                 public override Font Font {
179                         get {
180                                 return base.Font;
181                         }
182
183                         set {
184                                 if (font != value) {
185                                         Line    start;
186                                         Line    end;
187
188                                         if (auto_size) {
189                                                 if (PreferredHeight != Height) {
190                                                         Height = PreferredHeight;
191                                                 }
192                                         }
193
194                                         base.Font = value;
195
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));
200                                 }
201                         }
202                 }
203
204                 public override Color ForeColor {
205                         get {
206                                 return base.ForeColor;
207                         }
208
209                         set {
210                                 base.ForeColor = value;
211                         }
212                 }
213
214                 [DefaultValue(Int32.MaxValue)]
215                 public override int MaxLength {
216                         get {
217                                 return base.max_length;
218                         }
219
220                         set {
221                                 base.max_length = value;
222                         }
223                 }
224
225                 [DefaultValue(true)]
226                 public override bool Multiline {
227                         get {
228                                 return multiline;
229                         }
230
231                         set {
232                                 base.Multiline = value;
233                         }
234                 }
235
236                 [Browsable(false)]
237                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
238                 [MonoTODO]
239                 public string RedoActionName {
240                         get {
241                                 return redo_action_name;
242                         }
243                 }
244
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;
251                         }
252
253                         set {
254                                 margin_right = value;
255                         }
256                 }
257
258                 [Browsable(false)]
259                 [DefaultValue("")]
260                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
261                 public string Rtf {
262                         get {
263                                 Line            start_line;
264                                 Line            end_line;
265
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();
269                         }
270
271                         set {
272                                 MemoryStream    data;
273
274                                 document.Empty();
275                                 data = new MemoryStream(Encoding.ASCII.GetBytes(value), false);
276
277                                 InsertRTFFromStream(data, 0, 1);
278
279                                 data.Close();
280
281                                 Invalidate();
282                         }
283                 }
284
285                 [DefaultValue(RichTextBoxScrollBars.Both)]
286                 [Localizable(true)]
287                 public RichTextBoxScrollBars ScrollBars {
288                         get {
289                                 return scrollbars;
290                         }
291
292                         set {
293                                 scrollbars = value;
294                         }
295                 }
296
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();
303                         }
304
305                         set {
306                                 MemoryStream    data;
307                                 int             x;
308                                 int             y;
309                                 int             sel_start;
310                                 int             chars;
311                                 Line            line;
312                                 LineTag         tag;
313
314                                 if (document.selection_visible) {
315                                         document.ReplaceSelection("");
316                                 }
317
318                                 sel_start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
319
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();
323
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);
330                         }
331                 }
332
333                 [Browsable(false)]
334                 [DefaultValue("")]
335                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
336                 public override string SelectedText {
337                         get {
338                                 return base.SelectedText;
339                         }
340
341                         set {
342                                 base.SelectedText = value;
343                         }
344                 }
345
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;
355
356                                 start = document.ParagraphStart(document.selection_start.line);
357                                 align = start.alignment;
358
359                                 end = document.ParagraphEnd(document.selection_end.line);
360
361                                 line = start;
362
363                                 while (true) {
364                                         if (line.alignment != align) {
365                                                 return HorizontalAlignment.Left;
366                                         }
367
368                                         if (line == end) {
369                                                 break;
370                                         }
371                                         line = document.GetLine(line.line_no + 1);
372                                 }
373
374                                 return align;
375                         }
376
377                         set {
378                                 Line                    start;
379                                 Line                    end;
380                                 Line                    line;
381
382                                 start = document.ParagraphStart(document.selection_start.line);
383
384                                 end = document.ParagraphEnd(document.selection_end.line);
385
386                                 line = start;
387
388                                 while (true) {
389                                         line.alignment = value;
390
391                                         if (line == end) {
392                                                 break;
393                                         }
394                                         line = document.GetLine(line.line_no + 1);
395                                 }
396                                 this.CalculateDocument();
397                         }
398                 }
399
400                 [Browsable(false)]
401                 [DefaultValue(false)]
402                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
403                 [MonoTODO]
404                 public bool SelectionBullet {
405                         get {
406                                 return false;
407                         }
408
409                         set {
410                         }
411                 }
412
413                 [Browsable(false)]
414                 [DefaultValue(0)]
415                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
416                 [MonoTODO]
417                 public int SelectionCharOffset {
418                         get {
419                                 return 0;
420                         }
421
422                         set {
423                         }
424                 }
425
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;
434
435                                 start = document.selection_start.tag;
436                                 end = document.selection_end.tag;
437                                 color = ((SolidBrush)document.selection_start.tag.color).Color;
438
439                                 tag = start;
440                                 while (true) {
441                                         if (!color.Equals(((SolidBrush)tag.color).Color)) {
442                                                 return Color.Empty;
443                                         }
444
445                                         if (tag == end) {
446                                                 break;
447                                         }
448
449                                         tag = document.NextTag(tag);
450
451                                         if (tag == null) {
452                                                 break;
453                                         }
454                                 }
455
456                                 return color;
457                         }
458
459                         set {
460                                 FontDefinition  attributes;
461                                 int             sel_start;
462                                 int             sel_end;
463
464                                 attributes = new FontDefinition();
465                                 attributes.color = value;
466
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);
469
470                                 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
471
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);
474
475                                 document.UpdateView(document.selection_start.line, 0);
476                                 document.AlignCaret();
477                         }
478                 }
479
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;
488
489                                 start = document.selection_start.tag;
490                                 end = document.selection_end.tag;
491                                 font = document.selection_start.tag.font;
492
493                                 tag = start;
494                                 while (true) {
495                                         if (!font.Equals(tag.font)) {
496                                                 return null;
497                                         }
498
499                                         if (tag == end) {
500                                                 break;
501                                         }
502
503                                         tag = document.NextTag(tag);
504
505                                         if (tag == null) {
506                                                 break;
507                                         }
508                                 }
509
510                                 return font;
511                         }
512
513                         set {
514                                 FontDefinition  attributes;
515                                 int             sel_start;
516                                 int             sel_end;
517
518                                 attributes = new FontDefinition();
519                                 attributes.font_obj = value;
520
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);
523
524                                 document.FormatText(document.selection_start.line, document.selection_start.pos + 1, document.selection_end.line, document.selection_end.pos + 1, attributes);
525
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);
528
529                                 document.UpdateView(document.selection_start.line, 0);
530                                 document.AlignCaret();
531
532                         }
533                 }
534
535                 [Browsable(false)]
536                 [DefaultValue(0)]
537                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
538                 [MonoTODO]
539                 public int SelectionHangingIndent {
540                         get {
541                                 return 0;
542                         }
543
544                         set {
545                         }
546                 }
547
548                 [Browsable(false)]
549                 [DefaultValue(0)]
550                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
551                 [MonoTODO]
552                 public int SelectionIndent {
553                         get {
554                                 return 0;
555                         }
556
557                         set {
558                         }
559                 }
560
561                 [Browsable(false)]
562                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
563                 public override int SelectionLength {
564                         get {
565                                 return base.SelectionLength;
566                         }
567
568                         set {
569                                 base.SelectionLength = value;
570                         }
571                 }
572
573                 [Browsable(false)]
574                 [DefaultValue(false)]
575                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
576                 [MonoTODO]
577                 public bool SelectionProtected {
578                         get {
579                                 return false;
580                         }
581
582                         set {
583                         }
584                 }
585
586                 [Browsable(false)]
587                 [DefaultValue(0)]
588                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
589                 [MonoTODO]
590                 public int SelectionRightIndent {
591                         get {
592                                 return 0;
593                         }
594
595                         set {
596                         }
597                 }
598
599                 [Browsable(false)]
600                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
601                 [MonoTODO]
602                 public int[] SelectionTabs {
603                         get {
604                                 return new int[0];
605                         }
606
607                         set {
608                         }
609                 }
610
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;
617                                 }
618
619                                 // Lazy, but works
620                                 if (SelectedText.Length > 1) {
621                                         return RichTextBoxSelectionTypes.MultiChar | RichTextBoxSelectionTypes.Text;
622                                 }
623
624                                 return RichTextBoxSelectionTypes.Text;
625                         }
626                 }
627
628                 [DefaultValue(false)]
629                 [MonoTODO]
630                 public bool ShowSelectionMargin {
631                         get {
632                                 return false;
633                         }
634
635                         set {
636                         }
637                 }
638
639                 [Localizable(true)]
640                 public override string Text {
641                         get {
642                                 return base.Text;
643                         }
644
645                         set {
646                                 base.Text = value;
647                         }
648                 }
649
650                 [Browsable(false)]
651                 public override int TextLength {
652                         get {
653                                 return base.TextLength;
654                         }
655                 }
656
657                 [Browsable(false)]
658                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
659                 public string UndoActionName {
660                         get {
661                                 return document.undo.UndoName;
662                         }
663                 }
664
665                 [Localizable(true)]
666                 [DefaultValue(1)]
667                 public float ZoomFactor {
668                         get {
669                                 return zoom;
670                         }
671
672                         set {
673                                 zoom = value;
674                         }
675                 }
676                 #endregion      // Public Instance Properties
677
678                 #region Protected Instance Properties
679                 protected override CreateParams CreateParams {
680                         get {
681                                 return base.CreateParams;
682                         }
683                 }
684
685                 protected override Size DefaultSize {
686                         get {
687                                 return new Size(100, 96);
688                         }
689                 }
690                 #endregion      // Protected Instance Properties
691
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;
698                         }
699                         return false;
700                 }
701
702                 public int Find(char[] characterSet) {
703                         return Find(characterSet, -1, -1);
704                 }
705
706                 public int Find(char[] characterSet, int start) {
707                         return Find(characterSet, start, -1);
708                 }
709
710                 public int Find(char[] characterSet, int start, int end) {
711                         Document.Marker start_mark;
712                         Document.Marker end_mark;
713                         Document.Marker result;
714
715                         if (start == -1) {
716                                 document.GetMarker(out start_mark, true);
717                         } else {
718                                 Line line;
719                                 LineTag tag;
720                                 int pos;
721
722                                 start_mark = new Document.Marker();
723
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;
728                         }
729
730                         if (end == -1) {
731                                 document.GetMarker(out end_mark, false);
732                         } else {
733                                 Line line;
734                                 LineTag tag;
735                                 int pos;
736
737                                 end_mark = new Document.Marker();
738
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;
743                         }
744
745                         if (document.FindChars(characterSet, start_mark, end_mark, out result)) {
746                                 return document.LineTagToCharIndex(result.line, result.pos);
747                         }
748
749                         return -1;
750                 }
751
752                 public int Find(string str) {
753                         return Find(str, -1, -1, RichTextBoxFinds.None);
754                 }
755
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;
760
761                         if (start == -1) {
762                                 document.GetMarker(out start_mark, true);
763                         } else {
764                                 Line line;
765                                 LineTag tag;
766                                 int pos;
767
768                                 start_mark = new Document.Marker();
769
770                                 document.CharIndexToLineTag(start, out line, out tag, out pos);
771
772                                 start_mark.line = line;
773                                 start_mark.tag = tag;
774                                 start_mark.pos = pos;
775                         }
776
777                         if (end == -1) {
778                                 document.GetMarker(out end_mark, false);
779                         } else {
780                                 Line line;
781                                 LineTag tag;
782                                 int pos;
783
784                                 end_mark = new Document.Marker();
785
786                                 document.CharIndexToLineTag(end, out line, out tag, out pos);
787
788                                 end_mark.line = line;
789                                 end_mark.tag = tag;
790                                 end_mark.pos = pos;
791                         }
792
793                         if (document.Find(str, start_mark, end_mark, out result, options)) {
794                                 return document.LineTagToCharIndex(result.line, result.pos);
795                         }
796
797                         return -1;
798                 }
799
800                 public int Find(string str, int start, RichTextBoxFinds options) {
801                         return Find(str, start, -1, options);
802                 }
803
804                 public int Find(string str, RichTextBoxFinds options) {
805                         return Find(str, -1, -1, options);
806                 }
807
808                 public char GetCharFromPosition(Point pt) {
809                         LineTag tag;
810                         int     pos;
811
812                         PointToTagPos(pt, out tag, out pos);
813
814                         if (pos >= tag.line.text.Length) {
815                                 return '\n';
816                         }
817
818                         return tag.line.text[pos];
819                         
820                 }
821
822                 public int GetCharIndexFromPosition(Point pt) {
823                         LineTag tag;
824                         int     pos;
825
826                         PointToTagPos(pt, out tag, out pos);
827
828                         return document.LineTagToCharIndex(tag.line, pos);
829                 }
830
831                 public int GetLineFromCharIndex(int index) {
832                         Line    line;
833                         LineTag tag;
834                         int     pos;
835
836                         document.CharIndexToLineTag(index, out line, out tag, out pos);
837
838                         return line.LineNo - 1;
839                 }
840
841                 public Point GetPositionFromCharIndex(int index) {
842                         Line    line;
843                         LineTag tag;
844                         int     pos;
845
846                         document.CharIndexToLineTag(index, out line, out tag, out pos);
847
848                         return new Point((int)line.widths[pos] + 1, line.Y + 1);
849                 }
850
851                 public void LoadFile(System.IO.Stream data, RichTextBoxStreamType fileType) {
852                         document.Empty();
853
854                         // FIXME - ignoring unicode
855                         if (fileType == RichTextBoxStreamType.PlainText) {
856                                 StringBuilder   sb;
857                                 int             count;
858                                 byte[]          buffer;
859
860                                 try {
861                                         sb = new StringBuilder((int)data.Length);
862                                         buffer = new byte[1024];
863                                 }
864
865                                 catch {
866                                         throw new IOException("Not enough memory to load document");
867                                 }
868
869                                 count = 0;
870                                 while (count < data.Length) {
871                                         count += data.Read(buffer, count, 1024);
872                                         sb.Append(buffer);
873                                 }
874                                 base.Text = sb.ToString();
875                                 return;
876                         }
877
878                         InsertRTFFromStream(data, 0, 1);
879                 }
880
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);
887                         }
888                 }
889
890                 public void LoadFile(string path, RichTextBoxStreamType fileType) {
891                         FileStream      data;
892
893                         data = null;
894
895                         try {
896                                 data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024);
897                                 LoadFile(data, fileType);
898                         }
899
900                         catch {
901                                 throw new IOException("Could not open file " + path);
902                         }
903
904                         finally {
905                                 if (data != null) {
906                                         data.Close();
907                                 }
908                         }
909                 }
910
911                 public void Paste(DataFormats.Format clipFormat) {
912                         base.Paste(Clipboard.GetDataObject(), clipFormat, false);
913                 }
914
915                 [MonoTODO()]
916                 public void Redo() {
917                 }
918
919                 public void SaveFile(Stream data, RichTextBoxStreamType fileType) {
920                         Encoding        encoding;
921                         int             i;
922                         Byte[]          bytes;
923
924
925                         if (fileType == RichTextBoxStreamType.UnicodePlainText) {
926                                 encoding = Encoding.Unicode;
927                         } else {
928                                 encoding = Encoding.ASCII;
929                         }
930
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;
939                                         }
940
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);
944                                         }
945                                         bytes = encoding.GetBytes(document.GetLine(document.Lines).text.ToString());
946                                         data.Write(bytes, 0, bytes.Length);
947                                         return;
948                                 }
949                         }
950
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;
957
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];
963
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);
971                                 }
972                                 data.Write(bytes, 0, current);
973                         }
974                 }
975
976                 public void SaveFile(string path) {
977                         if (path.EndsWith(".rtf")) {
978                                 SaveFile(path, RichTextBoxStreamType.RichText);
979                         } else {
980                                 SaveFile(path, RichTextBoxStreamType.PlainText);
981                         }
982                 }
983
984                 public void SaveFile(string path, RichTextBoxStreamType fileType) {
985                         FileStream      data;
986
987                         data = null;
988
989 //                      try {
990                                 data = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 1024, false);
991                                 SaveFile(data, fileType);
992 //                      }
993
994 //                      catch {
995 //                              throw new IOException("Could not write document to file " + path);
996 //                      }
997
998 //                      finally {
999                                 if (data != null) {
1000                                         data.Close();
1001                                 }
1002 //                      }
1003                 }
1004
1005                 #endregion      // Public Instance Methods
1006
1007                 #region Protected Instance Methods
1008                 protected virtual object CreateRichEditOleCallback() {
1009                         throw new NotImplementedException();
1010                 }
1011
1012                 protected override void OnBackColorChanged(EventArgs e) {
1013                         base.OnBackColorChanged (e);
1014                 }
1015
1016                 protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
1017                         if (ContentsResized != null) {
1018                                 ContentsResized(this, e);
1019                         }
1020                 }
1021
1022                 protected override void OnContextMenuChanged(EventArgs e) {
1023                         base.OnContextMenuChanged (e);
1024                 }
1025
1026                 protected override void OnHandleCreated(EventArgs e) {
1027                         base.OnHandleCreated (e);
1028                 }
1029
1030                 protected override void OnHandleDestroyed(EventArgs e) {
1031                         base.OnHandleDestroyed (e);
1032                 }
1033
1034                 protected virtual void OnHScroll(EventArgs e) {
1035                         if (HScroll != null) {
1036                                 HScroll(this, e);
1037                         }
1038                 }
1039
1040                 [MonoTODO("Determine when to call this")]
1041                 protected virtual void OnImeChange(EventArgs e) {
1042                         if (ImeChange != null) {
1043                                 ImeChange(this, e);
1044                         }
1045                 }
1046
1047                 protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
1048                         if (LinkClicked != null) {
1049                                 LinkClicked(this, e);
1050                         }
1051                 }
1052
1053                 protected virtual void OnProtected(EventArgs e) {
1054                         if (Protected != null) {
1055                                 Protected(this, e);
1056                         }
1057                 }
1058
1059                 protected override void OnRightToLeftChanged(EventArgs e) {
1060                         base.OnRightToLeftChanged (e);
1061                 }
1062
1063                 protected virtual void OnSelectionChanged(EventArgs e) {
1064                         if (SelectionChanged != null) {
1065                                 SelectionChanged(this, e);
1066                         }
1067                 }
1068
1069                 protected override void OnSystemColorsChanged(EventArgs e) {
1070                         base.OnSystemColorsChanged (e);
1071                 }
1072
1073                 protected override void OnTextChanged(EventArgs e) {
1074                         base.OnTextChanged (e);
1075                 }
1076
1077                 protected virtual void OnVScroll(EventArgs e) {
1078                         if (VScroll != null) {
1079                                 VScroll(this, e);
1080                         }
1081                 }
1082
1083                 protected override void WndProc(ref Message m) {
1084                         base.WndProc (ref m);
1085                 }
1086                 #endregion      // Protected Instance Methods
1087
1088                 #region Events
1089                 [Browsable(false)]
1090                 [EditorBrowsable(EditorBrowsableState.Never)]
1091                 public event EventHandler                       BackgroundImageChanged;
1092
1093                 public event ContentsResizedEventHandler        ContentsResized;
1094
1095                 [Browsable(false)]
1096                 [EditorBrowsable(EditorBrowsableState.Never)]
1097                 public event EventHandler                       DoubleClick;
1098
1099                 [Browsable(false)]
1100                 [EditorBrowsable(EditorBrowsableState.Never)]
1101                 public event DragEventHandler DragDrop {
1102                         add {
1103                                 base.DragDrop += value;
1104                         }
1105
1106                         remove {
1107                                 base.DragDrop -= value;
1108                         }
1109                 }
1110
1111                 [Browsable(false)]
1112                 [EditorBrowsable(EditorBrowsableState.Never)]
1113                 public event DragEventHandler DragEnter {
1114                         add {
1115                                 base.DragEnter += value;
1116                         }
1117
1118                         remove {
1119                                 base.DragEnter -= value;
1120                         }
1121                 }
1122
1123                 [Browsable(false)]
1124                 [EditorBrowsable(EditorBrowsableState.Never)]
1125                 public event EventHandler DragLeave {
1126                         add {
1127                                 base.DragLeave += value;
1128                         }
1129
1130                         remove {
1131                                 base.DragLeave -= value;
1132                         }
1133                 }
1134
1135
1136                 [Browsable(false)]
1137                 [EditorBrowsable(EditorBrowsableState.Never)]
1138                 public event DragEventHandler DragOver {
1139                         add {
1140                                 base.DragOver += value;
1141                         }
1142
1143                         remove {
1144                                 base.DragOver -= value;
1145                         }
1146                 }
1147
1148
1149                 [Browsable(false)]
1150                 [EditorBrowsable(EditorBrowsableState.Never)]
1151                 public event GiveFeedbackEventHandler           GiveFeedback;
1152
1153                 public event EventHandler                       HScroll;
1154                 public event EventHandler                       ImeChange;
1155                 public event LinkClickedEventHandler            LinkClicked;
1156                 public event EventHandler                       Protected;
1157
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
1164
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;
1174                                                 }
1175
1176                                                 case Minor.UnicodeChar: {
1177                                                         rtf_skip_count += rtf_skip_width;
1178                                                         rtf_line.Append((char)rtf.Param);
1179                                                         break;
1180                                                 }
1181                                         }
1182                                         break;
1183                                 }
1184
1185                                 case RTF.Major.Destination: {
1186 //                                      Console.Write("[Got Destination control {0}]", rtf.Minor);
1187                                         rtf.SkipGroup();
1188                                         break;
1189                                 }
1190
1191                                 case RTF.Major.CharAttr: {
1192                                         switch(rtf.Minor) {
1193                                                 case Minor.ForeColor: {
1194                                                         System.Windows.Forms.RTF.Color  color;
1195
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));
1203                                                                 }
1204                                                         }
1205                                                         break;
1206                                                 }
1207
1208                                                 case Minor.FontSize: {
1209                                                         FlushText(rtf, false);
1210                                                         this.rtf_rtffont_size = rtf.Param / 2;
1211                                                         break;
1212                                                 }
1213
1214                                                 case Minor.FontNum: {
1215                                                         System.Windows.Forms.RTF.Font   font;
1216
1217                                                         font = System.Windows.Forms.RTF.Font.GetFont(rtf, rtf.Param);
1218                                                         if (font != null) {
1219                                                                 FlushText(rtf, false);
1220                                                                 this.rtf_rtffont = font;
1221                                                         }
1222                                                         break;
1223                                                 }
1224
1225                                                 case Minor.Plain: {
1226                                                         FlushText(rtf, false);
1227                                                         rtf_rtfstyle = FontStyle.Regular;
1228                                                         break;
1229                                                 }
1230
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;
1237                                                         }
1238                                                         break;
1239                                                 }
1240
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;
1247                                                         }
1248                                                         break;
1249                                                 }
1250
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;
1257                                                         }
1258                                                         break;
1259                                                 }
1260
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;
1267                                                         }
1268                                                         break;
1269                                                 }
1270
1271                                                 case Minor.NoUnderline: {
1272                                                         FlushText(rtf, false);
1273                                                         rtf_rtfstyle &= ~FontStyle.Underline;
1274                                                         break;
1275                                                 }
1276                                         }
1277                                         break;
1278                                 }
1279
1280                                 case RTF.Major.SpecialChar: {
1281                                         //Console.Write("[Got SpecialChar control {0}]", rtf.Minor);
1282                                         SpecialChar(rtf);
1283                                         break;
1284                                 }
1285                         }
1286                 }
1287
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;
1297                                 }
1298
1299                                 case Minor.Cell: {
1300                                         Console.Write(" ");
1301                                         break;
1302                                 }
1303
1304                                 case Minor.NoBrkSpace: {
1305                                         Console.Write(" ");
1306                                         break;
1307                                 }
1308
1309                                 case Minor.Tab: {
1310                                         rtf_line.Append ("\t");
1311 //                                      FlushText (rtf, false);
1312                                         break;
1313                                 }
1314
1315                                 case Minor.NoReqHyphen:
1316                                 case Minor.NoBrkHyphen: {
1317                                         rtf_line.Append ("-");
1318 //                                      FlushText (rtf, false);
1319                                         break;
1320                                 }
1321
1322                                 case Minor.Bullet: {
1323                                         Console.WriteLine("*");
1324                                         break;
1325                                 }
1326
1327                         case Minor.WidowCtrl:
1328                                 break;
1329
1330                                 case Minor.EmDash: {
1331                                 rtf_line.Append ("\u2014");
1332                                         break;
1333                                 }
1334
1335                                 case Minor.EnDash: {
1336                                         rtf_line.Append ("\u2013");
1337                                         break;
1338                                 }
1339 /*
1340                                 case Minor.LQuote: {
1341                                         Console.Write("\u2018");
1342                                         break;
1343                                 }
1344
1345                                 case Minor.RQuote: {
1346                                         Console.Write("\u2019");
1347                                         break;
1348                                 }
1349
1350                                 case Minor.LDblQuote: {
1351                                         Console.Write("\u201C");
1352                                         break;
1353                                 }
1354
1355                                 case Minor.RDblQuote: {
1356                                         Console.Write("\u201D");
1357                                         break;
1358                                 }
1359 */
1360                                 default: {
1361 //                                      Console.WriteLine ("skipped special char:   {0}", rtf.Minor);
1362 //                                      rtf.SkipGroup();
1363                                         break;
1364                                 }
1365                         }
1366                 }
1367
1368                 private void HandleText(RTF.RTF rtf) {
1369                         if (rtf_skip_count > 0) {
1370                                 rtf_skip_count--;
1371                                 return;
1372                         }
1373
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);
1382                                 }
1383                         }
1384                 }
1385
1386                 private void FlushText(RTF.RTF rtf, bool newline) {
1387                         int             length;
1388                         Font            font;
1389
1390                         length = rtf_line.Length;
1391                         if (!newline && (length == 0)) {
1392                                 return;
1393                         }
1394
1395                         if (rtf_rtffont == null) {
1396                                 // First font in table is default
1397                                 rtf_rtffont = System.Windows.Forms.RTF.Font.GetFont(rtf, 0);
1398                         }
1399
1400                         font = new Font(rtf_rtffont.Name, rtf_rtffont_size, rtf_rtfstyle);
1401
1402                         if (rtf_color == null) {
1403                                 System.Windows.Forms.RTF.Color color;
1404
1405                                 // First color in table is default
1406                                 color = System.Windows.Forms.RTF.Color.GetColor(rtf, 0);
1407
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));
1412                                 }
1413                         }
1414
1415                         rtf_chars += rtf_line.Length;
1416
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;
1421
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
1426                                 }
1427                                 if (newline) {
1428                                         document.Split(line, rtf_cursor_x + length);
1429                                 }
1430                         }
1431
1432                         if (newline) {
1433                                 rtf_cursor_x = 0;
1434                                 rtf_cursor_y++;
1435                         } else {
1436                                 rtf_cursor_x += length;
1437                         }
1438                         rtf_line.Length = 0;    // Empty line
1439                 }
1440
1441                 private void InsertRTFFromStream(Stream data, int cursor_x, int cursor_y) {
1442                         int     x;
1443                         int     y;
1444                         int     chars;
1445
1446                         InsertRTFFromStream(data, cursor_x, cursor_y, out x, out y, out chars);
1447                 }
1448
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;
1451
1452                         rtf = new RTF.RTF(data);
1453
1454                         // Prepare
1455                         rtf.ClassCallback[RTF.TokenClass.Text] = new RTF.ClassDelegate(HandleText);
1456                         rtf.ClassCallback[RTF.TokenClass.Control] = new RTF.ClassDelegate(HandleControl);
1457
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);
1470
1471                         rtf_text_map = new RTF.TextMap();
1472                         RTF.TextMap.SetupStandardTable(rtf_text_map.Table);
1473
1474                         document.NoRecalc = true;
1475
1476                         try {
1477                                 rtf.Read();     // That's it
1478                                 FlushText(rtf, false);
1479                         }
1480
1481                         catch (RTF.RTFException e) {
1482                                 // Seems to be plain text or broken RTF
1483                                 Console.WriteLine("RTF Parsing failure: {0}", e.Message);
1484                         }
1485
1486                         to_x = rtf_cursor_x;
1487                         to_y = rtf_cursor_y;
1488                         chars = rtf_chars;
1489
1490                         document.RecalculateDocument(CreateGraphicsInternal(), cursor_y, document.Lines, false);
1491                         document.NoRecalc = false;
1492
1493                         document.Invalidate(document.GetLine(cursor_y), 0, document.GetLine(document.Lines), -1);
1494                 }
1495
1496                 private void RichTextBox_HScrolled(object sender, EventArgs e) {
1497                         OnHScroll(e);
1498                 }
1499
1500                 private void RichTextBox_VScrolled(object sender, EventArgs e) {
1501                         OnVScroll(e);
1502                 }
1503
1504                 private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
1505                         Point p;
1506
1507                         p = pt;
1508
1509                         if (p.X >= document.ViewPortWidth) {
1510                                 p.X = document.ViewPortWidth - 1;
1511                         } else if (p.X < 0) {
1512                                 p.X = 0;
1513                         }
1514
1515                         if (p.Y >= document.ViewPortHeight) {
1516                                 p.Y = document.ViewPortHeight - 1;
1517                         } else if (p.Y < 0) {
1518                                 p.Y = 0;
1519                         }
1520
1521                         tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
1522                 }
1523
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
1527                         }
1528
1529                         if ((prev_font == null) || (prev_font.Size != font.Size)) {
1530                                 rtf.Append(String.Format("\\fs{0}", (int)(font.Size * 2)));             // Font size
1531                         }
1532
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");
1539                                         }
1540                                 }
1541                         }
1542
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");
1549                                         }
1550                                 }
1551                         }
1552
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");
1559                                         }
1560                                 }
1561                         }
1562
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");
1569                                         }
1570                                 }
1571                         }
1572                 }
1573
1574                 [MonoTODO("Emit unicode and other special characters properly")]
1575                 private void EmitRTFText(StringBuilder rtf, string text) {
1576                         rtf.Append(text);
1577                 }
1578
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;
1593
1594                         sb = new StringBuilder();
1595                         fonts = new ArrayList(10);
1596                         colors = new ArrayList(10);
1597
1598                         // Two runs, first we parse to determine tables;
1599                         // and unlike most of our processing here we work on tags
1600
1601                         line = start_line;
1602                         line_no = start_line.line_no;
1603                         pos = start_pos;
1604
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);
1612
1613                         while (line_no <= end_line.line_no) {
1614                                 line = document.GetLine(line_no);
1615                                 tag = LineTag.FindTag(line, pos);
1616
1617                                 if (line_no != end_line.line_no) {
1618                                         line_len = line.text.Length;
1619                                 } else {
1620                                         line_len = end_pos;
1621                                 }
1622
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);
1628                                                 }
1629                                         }
1630
1631                                         if (((SolidBrush)tag.color).Color != color) {
1632                                                 color = ((SolidBrush)tag.color).Color;
1633                                                 if (!colors.Contains(color)) {
1634                                                         colors.Add(color);
1635                                                 }
1636                                         }
1637
1638                                         pos = tag.start + tag.length - 1;
1639                                         tag = tag.next;
1640                                 }
1641                                 pos = 0;
1642                                 line_no++;
1643                         }
1644
1645                         // We have the tables, emit the header
1646                         sb.Append("{\\rtf1\\ansi");
1647                         sb.Append("\\ansicpg1252");     // FIXME - is this correct?
1648
1649                         // Default Font
1650                         sb.Append(String.Format("\\deff{0}", fonts.IndexOf(this.Font.Name)));
1651
1652                         // Default Language 
1653                         sb.Append("\\deflang1033\n");   // FIXME - always 1033?
1654
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(";}");                        // }
1663                         }
1664                         sb.Append("}\n");
1665
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(";");
1674                                 }
1675                                 sb.Append("}\n");
1676                         }
1677
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
1684
1685                         font = tag.font;
1686                         color = (Color)colors[0];
1687                         line = start_line;
1688                         line_no = start_line.line_no;
1689                         pos = start_pos;
1690
1691                         while (line_no <= end_line.line_no) {
1692                                 line = document.GetLine(line_no);
1693                                 tag = LineTag.FindTag(line, pos);
1694
1695                                 if (line_no != end_line.line_no) {
1696                                         line_len = line.text.Length;
1697                                 } else {
1698                                         line_len = end_pos;
1699                                 }
1700
1701                                 while (pos < line_len) {
1702                                         length = sb.Length;
1703
1704                                         if (tag.font != font) {
1705                                                 EmitRTFFontProperties(sb, fonts.IndexOf(font.Name), fonts.IndexOf(tag.font.Name), font, tag.font);
1706                                                 font = tag.font;
1707                                         }
1708
1709                                         if (((SolidBrush)tag.color).Color != color) {
1710                                                 color = ((SolidBrush)tag.color).Color;
1711                                                 sb.Append(String.Format("\\cf{0}", colors.IndexOf(color)));
1712                                         }
1713                                         if (length != sb.Length) {
1714                                                 sb.Append(" "); // Emit space to separate keywords from text
1715                                         }
1716
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));
1726                                                 }
1727                                         }
1728
1729                                         pos = tag.start + tag.length - 1;
1730                                         tag = tag.next;
1731                                 }
1732                                 if (pos >= line.text.Length) {
1733                                         if (!line.soft_break) {
1734                                                 sb.Append("\\par\n");
1735                                         }
1736                                 }
1737                                 pos = 0;
1738                                 line_no++;
1739                         }
1740
1741                         sb.Append("}\n");
1742
1743                         return sb;
1744                 }
1745                 #endregion      // Private Methods
1746         }
1747 }