2005-06-21 Peter Bartok <pbartok@novell.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextBoxBase.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) 2004-2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 // NOT COMPLETE
28 #define Debug
29
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
32 using System.Drawing;
33 using System.Drawing.Text;
34 using System.Text;
35 using System.Runtime.InteropServices;
36
37 namespace System.Windows.Forms {
38         [DefaultEvent("TextChanged")]
39         [Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + Consts.AssemblySystem_Design)]
40         public abstract class TextBoxBase : Control {
41                 #region Local Variables
42                 internal HorizontalAlignment    alignment;
43                 internal bool                   accepts_tab;
44                 internal bool                   accepts_return;
45                 internal bool                   auto_size;
46                 internal CharacterCasing        character_casing;
47                 internal bool                   undo;
48                 internal bool                   hide_selection;
49                 internal int                    max_length;
50                 internal bool                   modified;
51                 internal bool                   multiline;
52                 internal bool                   read_only;
53                 internal bool                   word_wrap;
54                 internal Document               document;
55                 internal LineTag                caret_tag;              // tag our cursor is in
56                 internal int                    caret_pos;              // position on the line our cursor is in (can be 0 = beginning of line)
57                 internal HScrollBar             hscroll;
58                 internal VScrollBar             vscroll;
59                 internal RichTextBoxScrollBars  scrollbars;
60                 internal bool                   grabbed;
61                 internal bool                   richtext;
62                 internal int                    requested_height;
63                 internal int                    canvas_width;
64                 internal int                    canvas_height;
65                 internal int                    track_width = 20;
66                 #if Debug
67                 internal static bool    draw_lines = false;
68                 #endif
69
70                 #endregion      // Local Variables
71
72                 #region Internal Constructor
73                 // Constructor will go when complete, only for testing - pdb
74                 internal TextBoxBase() {
75                         alignment = HorizontalAlignment.Left;
76                         accepts_return = false;
77                         accepts_tab = false;
78                         auto_size = true;
79                         border_style = BorderStyle.Fixed3D;
80                         character_casing = CharacterCasing.Normal;
81                         undo = false;
82                         hide_selection = true;
83                         max_length = 32767;
84                         modified = false;
85                         multiline = false;
86                         read_only = false;
87                         word_wrap = true;
88                         richtext = false;
89                         document = new Document(this);
90                         document.WidthChanged += new EventHandler(document_WidthChanged);
91                         //document.CaretMoved += new EventHandler(CaretMoved);
92                         document.Wrap = true;
93                         requested_height = -1;
94
95                         MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
96                         MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
97                         MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
98                         SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
99                         FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
100                         ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
101                         
102                         scrollbars = RichTextBoxScrollBars.None;
103
104                         hscroll = new HScrollBar();
105                         hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
106                         hscroll.control_style &= ~ControlStyles.Selectable;
107                         hscroll.Enabled = false;
108                         hscroll.Visible = false;
109
110                         vscroll = new VScrollBar();
111                         vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
112                         vscroll.control_style &= ~ControlStyles.Selectable;
113                         vscroll.Enabled = false;
114                         vscroll.Visible = false;
115
116                         this.Controls.Add(hscroll);
117                         this.Controls.Add(vscroll);
118
119                         //SetStyle(ControlStyles.ResizeRedraw, true);
120                         SetStyle(ControlStyles.AllPaintingInWmPaint, true);
121                         SetStyle(ControlStyles.UserPaint, true);
122
123                         canvas_width = this.Width;
124                         canvas_height = this.Height;
125
126                         CalculateScrollBars();
127                 }
128                 #endregion      // Internal Constructor
129
130                 #region Private and Internal Methods
131                 internal string CaseAdjust(string s) {
132                         if (character_casing == CharacterCasing.Normal) {
133                                 return s;
134                         }
135                         if (character_casing == CharacterCasing.Lower) {
136                                 return s.ToLower();
137                         } else {
138                                 return s.ToUpper();
139                         }
140                 }
141                 #endregion      // Private and Internal Methods
142
143                 #region Public Instance Properties
144                 [DefaultValue(false)]
145                 public bool AcceptsTab {
146                         get {
147                                 return accepts_tab;
148                         }
149
150                         set {
151                                 if (value != accepts_tab) {
152                                         accepts_tab = value;
153                                         OnAcceptsTabChanged(EventArgs.Empty);
154                                 }
155                         }
156                 }
157
158                 [DefaultValue(true)]
159                 [Localizable(true)]
160                 [RefreshProperties(RefreshProperties.Repaint)]
161                 public virtual bool AutoSize {
162                         get {
163                                 return auto_size;
164                         }
165
166                         set {
167                                 if (value != auto_size) {
168                                         auto_size = value;
169                                         if (auto_size) {
170                                                 if (PreferredHeight != Height) {
171                                                         Height = PreferredHeight;
172                                                 }
173                                         }
174                                         OnAutoSizeChanged(EventArgs.Empty);
175                                 }
176                         }
177                 }
178
179                 [DispId(-501)]
180                 public override System.Drawing.Color BackColor {
181                         get {
182                                 return base.BackColor;
183                         }
184                         set {
185                                 base.BackColor = value;
186                         }
187                 }
188
189                 [Browsable(false)]
190                 [EditorBrowsable(EditorBrowsableState.Never)]
191                 public override System.Drawing.Image BackgroundImage {
192                         get {
193                                 return base.BackgroundImage;
194                         }
195                         set {
196                                 base.BackgroundImage = value;
197                         }
198                 }
199
200                 [DefaultValue(BorderStyle.Fixed3D)]
201                 [DispId(-504)]
202                 public BorderStyle BorderStyle {
203                         get {
204                                 return border_style;
205                         }
206
207                         set {
208                                 if (value != border_style) {
209                                         border_style = value;
210                                         OnBorderStyleChanged(EventArgs.Empty);
211                                 }
212                         }
213                 }
214
215                 [Browsable(false)]
216                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
217                 public bool CanUndo {
218                         get {
219                                 return undo;
220                         }
221                 }
222
223                 [DispId(-513)]
224                 public override System.Drawing.Color ForeColor {
225                         get {
226                                 return base.ForeColor;
227                         }
228                         set {
229                                 base.ForeColor = value;
230                         }
231                 }
232
233                 [DefaultValue(true)]
234                 public bool HideSelection {
235                         get {
236                                 return hide_selection;
237                         }
238
239                         set {
240                                 if (value != hide_selection) {
241                                         hide_selection = value;
242                                         OnHideSelectionChanged(EventArgs.Empty);
243                                 }
244                                 if (hide_selection) {
245                                         document.selection_visible = false;
246                                 } else {
247                                         document.selection_visible = true;
248                                 }
249                                 document.InvalidateSelectionArea();
250
251                         }
252                 }
253
254                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
255                 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
256                 [Localizable(true)]
257                 public string[] Lines {
258                         get {
259                                 string[]        lines;
260                                 int             i;
261                                 int             l;
262
263                                 l = document.Lines;
264                                 lines = new string[l];
265
266                                 for (i = 1; i <= l; i++) {
267                                         lines[i - 1] = document.GetLine(i).text.ToString();
268                                 }
269
270                                 return lines;
271                         }
272
273                         set {
274                                 int     i;
275                                 int     l;
276                                 Brush   brush;
277
278                                 document.Empty();
279
280                                 l = value.Length;
281                                 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
282
283                                 for (i = 0; i < l; i++) {
284                                         document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
285                                 }
286                                 CalculateDocument();
287                                 OnTextChanged(EventArgs.Empty);
288                         }
289                 }
290
291                 [DefaultValue(32767)]
292                 [Localizable(true)]
293                 public virtual int MaxLength {
294                         get {
295                                 return max_length;
296                         }
297
298                         set {
299                                 if (value != max_length) {
300                                         max_length = value;
301                                 }
302                         }
303                 }
304
305                 [Browsable(false)]
306                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
307                 public bool Modified {
308                         get {
309                                 return modified;
310                         }
311
312                         set {
313                                 if (value != modified) {
314                                         modified = value;
315                                         OnModifiedChanged(EventArgs.Empty);
316                                 }
317                         }
318                 }
319
320                 [DefaultValue(false)]
321                 [Localizable(true)]
322                 [RefreshProperties(RefreshProperties.All)]
323                 public virtual bool Multiline {
324                         get {
325                                 return multiline;
326                         }
327
328                         set {
329                                 if (value != multiline) {
330                                         multiline = value;
331
332                                         // Make sure we update our size; the user may have already set the size before going to multiline
333                                         if (multiline && requested_height != -1) {
334                                                 Height = requested_height;
335                                                 requested_height = -1;
336                                         }
337
338                                         OnMultilineChanged(EventArgs.Empty);
339                                 }
340
341                                 document.multiline = multiline;
342                         }
343                 }
344
345                 [Browsable(false)]
346                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
347                 [EditorBrowsable(EditorBrowsableState.Advanced)]
348                 public int PreferredHeight {
349                         get {
350                                 return this.Font.Height + 7;    // FIXME - consider border style as well
351                         }
352                 }
353
354                 [DefaultValue(false)]
355                 public bool ReadOnly {
356                         get {
357                                 return read_only;
358                         }
359
360                         set {
361                                 if (value != read_only) {
362                                         read_only = value;
363                                         OnReadOnlyChanged(EventArgs.Empty);
364                                 }
365                         }
366                 }
367
368                 [Browsable(false)]
369                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
370                 public virtual string SelectedText {
371                         get {
372                                 return document.GetSelection();
373                         }
374
375                         set {
376                                 if (!read_only) {
377                                         document.ReplaceSelection(CaseAdjust(value));
378                                         OnTextChanged(EventArgs.Empty);
379                                 }
380                         }
381                 }
382
383                 [Browsable(false)]
384                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
385                 public virtual int SelectionLength {
386                         get {
387                                 return document.SelectionLength();
388                         }
389
390                         set {
391                                 if (value != 0) {
392                                         int     start;
393                                         Line    line;
394                                         LineTag tag;
395                                         int     pos;
396
397                                         start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
398
399                                         document.CharIndexToLineTag(start + value, out line, out tag, out pos);
400                                         document.SetSelectionEnd(line, pos);
401                                         document.PositionCaret(line, pos);
402                                 } else {
403                                         document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos);
404                                         document.PositionCaret(document.selection_start.line, document.selection_start.pos);
405                                 }
406                         }
407                 }
408
409                 [Browsable(false)]
410                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
411                 public int SelectionStart {
412                         get {
413                                 int index;
414
415                                 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
416
417                                 return index;
418                         }
419
420                         set {
421                                 Line    line;
422                                 LineTag tag;
423                                 int     pos;
424
425                                 document.CharIndexToLineTag(value, out line, out tag, out pos);
426                                 document.SetSelectionStart(line, pos);
427                         }
428                 }
429
430                 [Localizable(true)]
431                 public override string Text {
432                         get {
433                                 if (document == null || document.Root == null || document.Root.text == null) {
434                                         return string.Empty;
435                                 }
436
437                                 if (!multiline) {
438                                         return document.Root.text.ToString();
439                                 } else {
440                                         StringBuilder   sb;
441                                         int             i;
442
443                                         sb = new StringBuilder();
444
445                                         for (i = 1; i < document.Lines; i++) {
446                                                 sb.Append(document.GetLine(i).text.ToString() + Environment.NewLine);
447                                         }
448                                         return sb.ToString();
449                                 }
450                         }
451
452                         set {
453                                 if (value == base.Text) {
454                                         return;
455                                 }
456
457                                 if (value != null) {
458                                         Line    line;
459
460                                         if (multiline) {
461                                                 string[]        lines;
462
463                                                 lines = value.Split(new char[] {'\n'});
464                                                 for (int i = 0; i < lines.Length; i++) {
465                                                         if (lines[i].EndsWith("\r")) {
466                                                                 lines[i] = lines[i].Substring(0, lines[i].Length - 1);
467                                                         }
468                                                 }
469                                                 this.Lines = lines;
470
471                                                 line = document.GetLine(1);
472                                                 document.SetSelectionStart(line, 0);
473
474                                                 line = document.GetLine(document.Lines);
475                                                 document.SetSelectionEnd(line, line.text.Length);
476                                                 document.PositionCaret(line, line.text.Length);
477                                         } else {
478                                                 document.Clear();
479                                                 document.Add(1, CaseAdjust(value), alignment, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
480                                                 CalculateDocument();
481                                                 line = document.GetLine(1);
482                                                 document.SetSelectionStart(line, 0);
483                                                 document.SetSelectionEnd(line, value.Length);
484                                                 document.PositionCaret(line, value.Length);
485                                         }
486                                 }
487                                 base.Text = value;
488                                 OnTextChanged(EventArgs.Empty);
489                         }
490                 }
491
492                 [Browsable(false)]
493                 public virtual int TextLength {
494                         get {
495                                 if (document == null || document.Root == null || document.Root.text == null) {
496                                         return 0;
497                                 }
498
499                                 if (!multiline) {
500                                         return document.Root.text.Length;
501                                 } else {
502                                         int     total;
503                                         int     i;
504
505                                         total = 0;
506                                         for (i = 1; i < document.Lines; i++) {
507                                                 total += document.GetLine(i).text.Length + Environment.NewLine.Length;
508                                         }
509
510                                         return total;
511                                 }
512                         }
513                 }
514
515                 [DefaultValue(true)]
516                 [Localizable(true)]
517                 public bool WordWrap {
518                         get {
519                                 return word_wrap;
520                         }
521
522                         set {
523                                 if (value != word_wrap) {
524                                         word_wrap = value;
525                                         document.Wrap = value;
526                                 }
527                         }
528                 }
529                 #endregion      // Public Instance Properties
530
531                 #region Protected Instance Properties
532                 protected override CreateParams CreateParams {
533                         get {
534                                 return base.CreateParams;
535                         }
536                 }
537
538                 protected override System.Drawing.Size DefaultSize {
539                         get {
540                                 return base.DefaultSize;
541                         }
542                 }
543                 #endregion      // Protected Instance Properties
544
545                 #region Public Instance Methods
546                 public void AppendText(string text) {
547                         if (multiline) {
548                                 string[]        lines;
549                                 int             linecount;
550
551                                 // Break the string into separate lines
552                                 lines = text.Split(new char[] {'\n'});
553                                 linecount = lines.Length;
554                                 for (int i = 0; i < linecount; i++) {
555                                         if (lines[i].EndsWith("\r")) {
556                                                 lines[i] = lines[i].Substring(0, lines[i].Length - 1);
557                                         }
558                                 }
559
560                                 // Grab the formatting for the last element
561                                 document.MoveCaret(CaretDirection.CtrlEnd);
562
563                                 // Insert the first line
564                                 document.InsertString(document.CaretLine, document.CaretPosition, lines[0]);
565
566                                 for (int i = 1; i < linecount; i++) {
567                                         document.Add(document.CaretLine.LineNo+i, CaseAdjust(lines[i]), alignment, document.CaretTag.font, document.CaretTag.color);
568                                 }
569
570                                 CalculateDocument();
571                                 document.MoveCaret(CaretDirection.CtrlEnd);
572                         } else {
573                                 document.MoveCaret(CaretDirection.CtrlEnd);
574                                 document.InsertStringAtCaret(text, true);
575                                 Invalidate();
576                         }
577                         OnTextChanged(EventArgs.Empty);
578                 }
579
580                 public void Clear() {
581                         Text = null;
582                 }
583
584                 public void ClearUndo() {
585                         // FIXME
586                         throw new NotImplementedException();
587                 }
588
589                 public void Copy() {
590                         // FIXME
591                         throw new NotImplementedException();
592                 }
593
594                 public void Cut() {
595                         // FIXME
596                         throw new NotImplementedException();
597                 }
598
599                 public void Paste() {
600                         // FIXME
601                         throw new NotImplementedException();
602                 }
603
604                 public void ScrollToCaret() {
605                         // FIXME
606                         throw new NotImplementedException();
607                 }
608
609                 public void Select(int start, int length) {
610                         SelectionStart = start;
611                         SelectionLength = length;
612                 }
613
614
615                 public void SelectAll() {
616                         Line    last;
617
618                         last = document.GetLine(document.Lines);
619                         document.SetSelectionStart(document.GetLine(1), 0);
620                         document.SetSelectionEnd(last, last.text.Length);
621                 }
622
623                 public override string ToString() {
624                         StringBuilder   sb;
625                         int             i;
626                         int             end;
627
628                         if (document == null) {
629                                 return String.Empty;
630                         }
631
632                         sb = new StringBuilder();
633
634                         end = document.Lines;
635
636                         for (i = 1; i < end; i++) {
637                                 sb.Append(document.GetLine(i).text.ToString() + "\n");
638                         }
639
640                         return sb.ToString();
641                 }
642
643                 public void Undo() {
644                         return;
645                 }
646                 #endregion      // Public Instance Methods
647
648                 #region Protected Instance Methods
649                 protected override void CreateHandle() {
650                         base.CreateHandle ();
651                 }
652
653                 protected override bool IsInputKey(Keys keyData) {
654                         switch (keyData) {
655 #if not
656                                 // We handle Enter in ProcessDialogKey
657                                 case Keys.Enter: {
658                                         if (multiline && (accepts_return || ((keyData & Keys.Control) != 0))) {
659                                                 return true;
660                                         }
661                                         return false;
662                                 }
663 #endif
664
665                                 case Keys.Tab: {
666                                         if (accepts_tab) {
667                                                 return true;
668                                         }
669                                         return false;
670                                 }
671                         }
672                         return false;
673                 }
674
675
676                 protected virtual void OnAcceptsTabChanged(EventArgs e) {
677                         if (AcceptsTabChanged != null) {
678                                 AcceptsTabChanged(this, e);
679                         }
680                 }
681
682                 protected virtual void OnAutoSizeChanged(EventArgs e) {
683                         if (AutoSizeChanged != null) {
684                                 AutoSizeChanged(this, e);
685                         }
686                 }
687
688                 protected virtual void OnBorderStyleChanged(EventArgs e) {
689                         if (BorderStyleChanged != null) {
690                                 BorderStyleChanged(this, e);
691                         }
692                 }
693
694                 protected override void OnFontChanged(EventArgs e) {
695                         base.OnFontChanged (e);
696
697                         if (auto_size) {
698                                 if (PreferredHeight != Height) {
699                                         Height = PreferredHeight;
700                                 }
701                         }
702                 }
703
704                 protected override void OnHandleCreated(EventArgs e) {
705                         base.OnHandleCreated (e);
706                 }
707
708                 protected override void OnHandleDestroyed(EventArgs e) {
709                         base.OnHandleDestroyed (e);
710                 }
711
712                 protected virtual void OnHideSelectionChanged(EventArgs e) {
713                         if (HideSelectionChanged != null) {
714                                 HideSelectionChanged(this, e);
715                         }
716                 }
717
718                 protected virtual void OnModifiedChanged(EventArgs e) {
719                         if (ModifiedChanged != null) {
720                                 ModifiedChanged(this, e);
721                         }
722                 }
723
724                 protected virtual void OnMultilineChanged(EventArgs e) {
725                         if (MultilineChanged != null) {
726                                 MultilineChanged(this, e);
727                         }
728                 }
729
730                 protected virtual void OnReadOnlyChanged(EventArgs e) {
731                         if (ReadOnlyChanged != null) {
732                                 ReadOnlyChanged(this, e);
733                         }
734                 }
735
736                 protected override bool ProcessDialogKey(Keys keyData) {
737                         switch (keyData & Keys.KeyCode) {
738                                 case Keys.Left: {
739                                         document.SetSelectionToCaret(true);
740
741                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
742                                                 document.MoveCaret(CaretDirection.WordBack);
743                                         } else {
744                                                 document.MoveCaret(CaretDirection.CharBack);
745                                         }
746                                         CaretMoved(this, null);
747                                         return true;
748                                 }
749
750                                 case Keys.Right: {
751                                         document.SetSelectionToCaret(true);
752
753                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
754                                                 document.MoveCaret(CaretDirection.WordForward);
755                                         } else {
756                                                 document.MoveCaret(CaretDirection.CharForward);
757                                         }
758                                         CaretMoved(this, null);
759                                         return true;
760                                 }
761
762                                 case Keys.Up: {
763                                         document.SetSelectionToCaret(true);
764                                         document.MoveCaret(CaretDirection.LineUp);
765                                         CaretMoved(this, null);
766                                         return true;
767                                 }
768
769                                 case Keys.Down: {
770                                         document.SetSelectionToCaret(true);
771                                         document.MoveCaret(CaretDirection.LineDown);
772                                         CaretMoved(this, null);
773                                         return true;
774                                 }
775
776                                 case Keys.Home: {
777                                         document.SetSelectionToCaret(true);
778
779                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
780                                                 document.MoveCaret(CaretDirection.CtrlHome);
781                                         } else {
782                                                 document.MoveCaret(CaretDirection.Home);
783                                         }
784                                         CaretMoved(this, null);
785                                         return true;
786                                 }
787
788                                 case Keys.End: {
789                                         document.SetSelectionToCaret(true);
790
791                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
792                                                 document.MoveCaret(CaretDirection.CtrlEnd);
793                                         } else {
794                                                 document.MoveCaret(CaretDirection.End);
795                                         }
796                                         CaretMoved(this, null);
797                                         return true;
798                                 }
799
800                                 case Keys.Enter: {
801                                         if (!read_only && multiline && (accepts_return || ((Control.ModifierKeys & Keys.Control) != 0))) {
802                                                 Line    line;
803
804                                                 if (document.selection_visible) {
805                                                         document.ReplaceSelection("");
806                                                 }
807                                                 document.SetSelectionToCaret(true);
808
809                                                 line = document.CaretLine;
810
811                                                 document.Split(document.CaretLine, document.CaretTag, document.CaretPosition, false);
812                                                 OnTextChanged(EventArgs.Empty);
813                                                 document.UpdateView(line, 2, 0);
814                                                 document.MoveCaret(CaretDirection.CharForward);
815                                                 CaretMoved(this, null);
816                                                 return true;
817                                         }
818                                         break;
819                                 }
820
821                                 case Keys.Tab: {
822                                         if (!read_only && accepts_tab) {
823                                                 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
824                                                 if (document.selection_visible) {
825                                                         document.ReplaceSelection("");
826                                                 }
827                                                 document.SetSelectionToCaret(true);
828
829                                                 OnTextChanged(EventArgs.Empty);
830                                                 CaretMoved(this, null);
831                                                 return true;
832                                         }
833                                         break;
834                                 }
835
836
837                                 case Keys.Back: {
838                                         if (read_only) {
839                                                 break;
840                                         }
841
842                                         // delete only deletes on the line, doesn't do the combine
843                                         if (document.selection_visible) {
844                                                 document.ReplaceSelection("");
845                                         }
846                                         document.SetSelectionToCaret(true);
847                                         if (document.CaretPosition == 0) {
848                                                 if (document.CaretLine.LineNo > 1) {
849                                                         Line    line;
850                                                         int     new_caret_pos;
851
852                                                         line = document.GetLine(document.CaretLine.LineNo - 1);
853                                                         new_caret_pos = line.text.Length;
854
855                                                         document.Combine(line, document.CaretLine);
856                                                         document.UpdateView(line, 1, 0);
857                                                         document.PositionCaret(line, new_caret_pos);
858                                                         document.UpdateCaret();
859                                                         OnTextChanged(EventArgs.Empty);
860                                                 }
861                                         } else {
862                                                 document.DeleteChar(document.CaretTag, document.CaretPosition, false);
863                                                 document.MoveCaret(CaretDirection.CharBack);
864                                                 OnTextChanged(EventArgs.Empty);
865                                         }
866                                         CaretMoved(this, null);
867                                         return true;
868                                 }
869
870                                 case Keys.Delete: {
871                                         if (read_only) {
872                                                 break;
873                                         }
874
875                                         // delete only deletes on the line, doesn't do the combine
876                                         if (document.CaretPosition == document.CaretLine.text.Length) {
877                                                 if (document.CaretLine.LineNo < document.Lines) {
878                                                         Line    line;
879
880                                                         line = document.GetLine(document.CaretLine.LineNo + 1);
881                                                         document.Combine(document.CaretLine, line);
882                                                         document.UpdateView(document.CaretLine, 2, 0);
883                                                         OnTextChanged(EventArgs.Empty);
884
885 #if Debug
886                                                         Line    check_first;
887                                                         Line    check_second;
888
889                                                         check_first = document.GetLine(document.CaretLine.LineNo);
890                                                         check_second = document.GetLine(check_first.line_no + 1);
891
892                                                         Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
893 #endif
894
895                                                         // Caret doesn't move
896                                                 }
897                                         } else {
898                                                 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
899                                                 OnTextChanged(EventArgs.Empty);
900                                         }
901                                         CaretMoved(this, null);
902                                         return true;
903                                 }
904                         }
905                         return base.ProcessDialogKey (keyData);
906                 }
907
908                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
909                         // Make sure we don't get sized bigger than we want to be
910                         if (!richtext) {
911                                 if (!multiline) {
912                                         if (height > PreferredHeight) {
913                                                 requested_height = height;
914                                                 height = PreferredHeight;
915                                                 specified |= BoundsSpecified.Height;
916                                         }
917                                 }
918                         }
919
920                         document.ViewPortWidth = this.Width;
921                         document.ViewPortHeight = this.Height;
922
923                         CalculateDocument();
924
925                         base.SetBoundsCore (x, y, width, height, specified);
926                 }
927
928                 protected override void WndProc(ref Message m) {
929                         switch ((Msg)m.Msg) {
930                                 case Msg.WM_PAINT: {
931                                         PaintEventArgs  paint_event;
932
933                                         paint_event = XplatUI.PaintEventStart(Handle);
934                                         
935                                         PaintControl(paint_event);
936                                         XplatUI.PaintEventEnd(Handle);
937                                         DefWndProc(ref m);
938                                         return;
939                                 }
940
941                                 case Msg.WM_SETFOCUS: {
942                                         // Set caret
943                                         document.CaretHasFocus();
944 Console.WriteLine("Creating caret");
945                                         base.WndProc(ref m);
946                                         return;
947                                 }
948
949                                 case Msg.WM_KILLFOCUS: {
950                                         // Kill caret
951                                         document.CaretLostFocus();
952 Console.WriteLine("Destroying caret");
953                                         base.WndProc(ref m);
954                                         return;
955                                 }
956
957                                 case Msg.WM_CHAR: {
958                                         if (ProcessKeyEventArgs(ref m)) {
959                                                 return;
960                                         }
961
962                                         if (PreProcessMessage(ref m)) {
963                                                 return;
964                                         }
965
966                                         if (ProcessKeyMessage(ref m)) {
967                                                 return;
968                                         }
969
970                                         if (!read_only && (m.WParam.ToInt32() >= 32)) { // FIXME, tabs should probably go through
971                                                 if (document.selection_visible) {
972                                                         document.ReplaceSelection("");
973                                                 }
974
975                                                 switch (character_casing) {
976                                                         case CharacterCasing.Normal: {
977                                                                 document.InsertCharAtCaret((char)m.WParam, true);
978                                                                 OnTextChanged(EventArgs.Empty);
979                                                                 CaretMoved(this, null);
980                                                                 return;
981                                                         }
982
983                                                         case CharacterCasing.Lower: {
984                                                                 document.InsertCharAtCaret(Char.ToLower((char)m.WParam), true);
985                                                                 OnTextChanged(EventArgs.Empty);
986                                                                 CaretMoved(this, null);
987                                                                 return;
988                                                         }
989
990                                                         case CharacterCasing.Upper: {
991                                                                 document.InsertCharAtCaret(Char.ToUpper((char)m.WParam), true);
992                                                                 OnTextChanged(EventArgs.Empty);
993                                                                 CaretMoved(this, null);
994                                                                 return;
995                                                         }
996                                                 }
997                                         }
998                                         DefWndProc(ref m);
999                                         return;
1000                                 }
1001
1002                                 default: {
1003                                         base.WndProc(ref m);
1004                                         return;
1005                                 }
1006                         }
1007                 }
1008
1009                 #endregion      // Protected Instance Methods
1010
1011                 #region Events
1012                 public event EventHandler       AcceptsTabChanged;
1013                 public event EventHandler       AutoSizeChanged;
1014                 [Browsable(false)]
1015                 [EditorBrowsable(EditorBrowsableState.Never)]
1016                 public new event EventHandler BackgroundImageChanged {
1017                         add { base.BackgroundImageChanged += value; }
1018                         remove { base.BackgroundImageChanged -= value; }
1019                 }
1020                 public event EventHandler       BorderStyleChanged;
1021                 [Browsable(false)]
1022                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1023                 public event EventHandler       Click;
1024                 public event EventHandler       HideSelectionChanged;
1025                 public event EventHandler       ModifiedChanged;
1026                 public event EventHandler       MultilineChanged;
1027                 [Browsable(false)]
1028                 [EditorBrowsable(EditorBrowsableState.Never)]
1029                 public event PaintEventHandler  Paint;
1030                 public event EventHandler       ReadOnlyChanged;
1031                 #endregion      // Events
1032
1033                 #region Private Methods
1034                 internal Document Document {
1035                         get {
1036                                 return document;
1037                         }
1038
1039                         set {
1040                                 document = value;
1041                         }
1042                 }
1043
1044 static int current;
1045
1046                 private void PaintControl(PaintEventArgs pevent) {
1047                         // Fill background
1048                         pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1049                         pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
1050
1051                         // Draw the viewable document
1052                         document.Draw(pevent.Graphics, pevent.ClipRectangle);
1053
1054                         Rectangle       rect = ClientRectangle;
1055                         rect.Width--;
1056                         rect.Height--;
1057                         //pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorButtonShadow), rect);
1058
1059                         #if Debug
1060                                 int             start;
1061                                 int             end;
1062                                 Line            line;
1063                                 int             line_no;
1064                                 Pen             p;
1065
1066                                 p = new Pen(Color.Red, 1);
1067
1068                                 // First, figure out from what line to what line we need to draw
1069                                 start = document.GetLineByPixel(pevent.ClipRectangle.Top - document.ViewPortY, false).line_no;
1070                                 end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - document.ViewPortY, false).line_no;
1071
1072                                 //Console.WriteLine("Starting drawing on line '{0}'", document.GetLine(start));
1073                                 //Console.WriteLine("Ending drawing on line '{0}'", document.GetLine(end));
1074
1075                                 line_no = start;
1076                                 while (line_no <= end) {
1077                                         line = document.GetLine(line_no);
1078
1079                                         if (draw_lines) {
1080                                                 for (int i = 0; i < line.text.Length; i++) {
1081                                                         pevent.Graphics.DrawLine(p, (int)line.widths[i] - document.ViewPortX, line.Y - document.ViewPortY, (int)line.widths[i] - document.ViewPortX, line.Y + line.height  - document.ViewPortY);
1082                                                 }
1083                                         }
1084
1085                                         line_no++;
1086                                 }
1087                         #endif
1088                 }
1089
1090                 private void TextBoxBase_MouseDown(object sender, MouseEventArgs e) {
1091                         LineTag tag;
1092                         Line    line;
1093                         int     pos;
1094
1095                         if (e.Button == MouseButtons.Left) {
1096                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1097                                 document.SetSelectionToCaret(true);
1098                                 this.grabbed = true;
1099                                 this.Capture = true;
1100                                 return;
1101                         }
1102
1103                         #if Debug
1104                                 if (e.Button == MouseButtons.Right) {
1105                                         draw_lines = !draw_lines;
1106                                         this.Invalidate();
1107                                         Console.WriteLine("SelectedText: {0}, length {1}", this.SelectedText, this.SelectionLength);
1108                                         Console.WriteLine("Selection start: {0}", this.SelectionStart);
1109
1110                                         this.SelectionStart = 10;
1111                                         this.SelectionLength = 5;
1112
1113                                         return;
1114                                 }
1115
1116                                 tag = document.FindTag(e.X + document.ViewPortX, e.Y + document.ViewPortY, out pos, false);
1117
1118                                 Console.WriteLine("Click found tag {0}, character {1}", tag, pos);
1119                                 line = tag.line;
1120                                 switch(current) {
1121                                         case 4: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("impact", 20, FontStyle.Bold, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.Red)); break;
1122                                         case 1: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("arial unicode ms", 24, FontStyle.Italic, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.DarkGoldenrod)); break;
1123                                         case 2: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("arial", 10, FontStyle.Regular, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.Aquamarine)); break;
1124                                         case 3: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("times roman", 16, FontStyle.Underline, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.Turquoise)); break;
1125                                         case 0: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, new Font("times roman", 64, FontStyle.Italic | FontStyle.Bold, GraphicsUnit.Pixel), ThemeEngine.Current.ResPool.GetSolidBrush(Color.LightSeaGreen)); break;
1126                                         case 5: LineTag.FormatText(tag.line, pos, (pos+10)<line.Text.Length ? 10 : line.Text.Length - pos+1, ((TextBoxBase)sender).Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor)); break;
1127                                 }
1128                                 current++;
1129                                 if (current==6) {
1130                                         current=0;
1131                                 }
1132
1133                                 // Update/Recalculate what we see
1134                                 document.UpdateView(line, 0);
1135
1136                                 // Make sure our caret is properly positioned and sized
1137                                 document.AlignCaret();
1138                         #endif
1139                 }
1140
1141                 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1142                         this.Capture = false;
1143                         this.grabbed = false;
1144                         if (e.Button == MouseButtons.Left) {
1145                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1146                                 document.SetSelectionToCaret(false);
1147                                 document.DisplayCaret();
1148                                 return;
1149                         }
1150                 }
1151                 #endregion      // Private Methods
1152
1153
1154                 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1155                         canvas_width = this.Width;
1156                         canvas_height = this.Height;
1157                         // We always move them, they just might not be displayed
1158                         hscroll.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hscroll.Height, Width, hscroll.Height);
1159                         vscroll.Bounds = new Rectangle (ClientRectangle.Right - vscroll.Width, ClientRectangle.Top, vscroll.Width, Height);
1160                         
1161                 }
1162
1163                 private void CalculateDocument() {
1164                         document.RecalculateDocument(CreateGraphics());
1165                         CalculateScrollBars();
1166                         Invalidate();   // FIXME - do we need this?
1167                 }
1168
1169                 protected void CalculateScrollBars() {
1170                         // No scrollbars for a single line
1171                         if (document.Width >= this.Width) {
1172                                 hscroll.Enabled = true;
1173                                 hscroll.Minimum = 0;
1174                                 hscroll.Maximum = document.Width - this.Width;
1175                         } else {
1176                                 hscroll.Enabled = false;
1177                         }
1178
1179                         if (document.Height >= this.Height) {
1180                                 vscroll.Enabled = true;
1181                                 vscroll.Minimum = 0;
1182                                 vscroll.Maximum = document.Height - this.Height;
1183                         } else {
1184                                 vscroll.Enabled = false;
1185                         }
1186
1187
1188                         if (!multiline) {
1189                                 return;
1190                         }
1191
1192                         if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1193                                 if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1194                                         hscroll.Visible = true;
1195                                 }
1196                         }
1197
1198                         if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1199                                 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1200                                         vscroll.Visible = true;
1201                                 }
1202                         }
1203
1204                         if (hscroll.Visible) {
1205                                 vscroll.Maximum += hscroll.Height;
1206                                 canvas_height = this.Height - hscroll.Height;
1207                         }
1208
1209                         if (vscroll.Visible) {
1210                                 hscroll.Maximum += vscroll.Width * 2;
1211                                 canvas_width = this.Width - vscroll.Width * 2;
1212                         }
1213                 }
1214
1215                 private void document_WidthChanged(object sender, EventArgs e) {\r
1216                         CalculateScrollBars();\r
1217                 }\r
1218
1219                 private void hscroll_ValueChanged(object sender, EventArgs e) {
1220                         XplatUI.ScrollWindow(this.Handle, document.ViewPortX-this.hscroll.Value, 0, false);
1221                         document.ViewPortX = this.hscroll.Value;
1222                         document.UpdateCaret();
1223                         //Console.WriteLine("Dude scrolled horizontal");
1224                 }
1225
1226                 private void vscroll_ValueChanged(object sender, EventArgs e) {\r
1227                         XplatUI.ScrollWindow(this.Handle, 0, document.ViewPortY-this.vscroll.Value, false);
1228                         document.ViewPortY = this.vscroll.Value;
1229                         document.UpdateCaret();
1230                         //Console.WriteLine("Dude scrolled vertical");
1231                 }\r
1232
1233                 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1234                         // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1235                         if (grabbed) {
1236                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1237                                 document.SetSelectionToCaret(false);
1238                                 document.DisplayCaret();
1239                         }
1240                 }
1241                                                                               
1242                 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1243                         if (!richtext) {
1244                                 Line    line;
1245
1246                                 // Font changes apply to the whole document
1247                                 for (int i = 1; i <= document.Lines; i++) {
1248                                         line = document.GetLine(i);
1249                                         LineTag.FormatText(line, 1, line.text.Length, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
1250                                         document.UpdateView(line, 0);
1251                                 }
1252                                 // Make sure the caret height is matching the new font height
1253                                 document.AlignCaret();
1254                         }
1255                 }\r
1256 \r
1257                 /// <summary>Ensure the caret is always visible</summary>\r
1258                 internal void CaretMoved(object sender, EventArgs e) {\r
1259                         Point   pos;\r
1260                         int     height;\r
1261                         \r
1262                         pos = document.Caret;\r
1263                         //Console.WriteLine("Caret now at {0} (Thumb: {1}x{2}, Canvas: {3}x{4}, Document {5}x{6})", pos, hscroll.Value, vscroll.Value, canvas_width, canvas_height, document.Width, document.Height);\r
1264 \r
1265                         // Handle horizontal scrolling\r
1266                         if (pos.X < (document.ViewPortX + track_width)) {\r
1267                                 do {\r
1268                                         if ((hscroll.Value - track_width) >= hscroll.Minimum) {\r
1269                                                 hscroll.Value -= track_width;\r
1270                                         } else {\r
1271                                                 hscroll.Value = hscroll.Minimum;\r
1272                                         }\r
1273                                 } while (hscroll.Value > pos.X);\r
1274                         }\r
1275 \r
1276                         if ((pos.X > (this.canvas_width + document.ViewPortX - track_width)) && (hscroll.Value != hscroll.Maximum)) {\r
1277                                 do {\r
1278                                         if ((hscroll.Value + track_width) <= hscroll.Maximum) {\r
1279                                                 hscroll.Value += track_width;\r
1280                                         } else {\r
1281                                                 hscroll.Value = hscroll.Maximum;\r
1282                                         }\r
1283                                 } while (pos.X > (hscroll.Value + this.canvas_width));\r
1284                         }\r
1285 \r
1286                         if (!multiline) {\r
1287                                 return;\r
1288                         }\r
1289 \r
1290                         // Handle vertical scrolling\r
1291                         height = document.CaretLine.Height;\r
1292 \r
1293                         if (pos.Y < document.ViewPortY) {\r
1294                                 vscroll.Value = pos.Y;\r
1295                         }\r
1296 \r
1297                         if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {\r
1298                                 vscroll.Value = pos.Y - canvas_height + height;\r
1299                         }\r
1300                 }\r
1301         }
1302 }