2006-03-12 Peter Dennis 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-2006 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 // NOT COMPLETE
28 #undef Debug
29 #undef DebugClick
30
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
33 using System.Drawing;
34 using System.Drawing.Text;
35 using System.Text;
36 using System.Runtime.InteropServices;
37
38 namespace System.Windows.Forms {
39         [DefaultEvent("TextChanged")]
40         [Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + Consts.AssemblySystem_Design)]
41         public abstract class TextBoxBase : Control {
42                 #region Local Variables
43                 internal HorizontalAlignment    alignment;
44                 internal bool                   accepts_tab;
45                 internal bool                   accepts_return;
46                 internal bool                   auto_size;
47                 internal CharacterCasing        character_casing;
48                 internal bool                   undo;
49                 internal bool                   hide_selection;
50                 internal int                    max_length;
51                 internal bool                   modified;
52                 internal bool                   multiline;
53                 internal char                   password_char;
54                 internal bool                   read_only;
55                 internal bool                   word_wrap;
56                 internal Document               document;
57                 internal LineTag                caret_tag;              // tag our cursor is in
58                 internal int                    caret_pos;              // position on the line our cursor is in (can be 0 = beginning of line)
59                 internal ImplicitHScrollBar     hscroll;
60                 internal ImplicitVScrollBar     vscroll;
61                 internal RichTextBoxScrollBars  scrollbars;
62                 internal bool                   richtext;
63                 internal int                    requested_height;
64                 internal int                    canvas_width;
65                 internal int                    canvas_height;
66                 static internal int             track_width = 20;
67                 static internal int             track_border = 5;
68                 internal DateTime               click_last;
69                 internal CaretSelection         click_mode;
70                 internal Bitmap                 bmp;
71                 #if Debug
72                 internal static bool    draw_lines = false;
73                 #endif
74
75                 #endregion      // Local Variables
76
77                 #region Internal Constructor
78                 // Constructor will go when complete, only for testing - pdb
79                 internal TextBoxBase() {
80                         alignment = HorizontalAlignment.Left;
81                         accepts_return = false;
82                         accepts_tab = false;
83                         auto_size = true;
84                         border_style = BorderStyle.Fixed3D;
85                         character_casing = CharacterCasing.Normal;
86                         undo = false;
87                         hide_selection = true;
88                         max_length = 32767;
89                         modified = false;
90                         multiline = false;
91                         password_char = '\0';
92                         read_only = false;
93                         word_wrap = true;
94                         richtext = false;
95                         document = new Document(this);
96                         document.WidthChanged += new EventHandler(document_WidthChanged);
97                         document.HeightChanged += new EventHandler(document_HeightChanged);
98                         //document.CaretMoved += new EventHandler(CaretMoved);
99                         document.Wrap = false;
100                         requested_height = -1;
101                         click_last = DateTime.Now;
102                         click_mode = CaretSelection.Position;
103                         bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
104
105                         MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
106                         MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
107                         MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
108                         SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
109                         FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
110                         ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
111                         MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
112                         
113                         scrollbars = RichTextBoxScrollBars.None;
114
115                         hscroll = new ImplicitHScrollBar();
116                         hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
117                         hscroll.control_style &= ~ControlStyles.Selectable;
118                         hscroll.Enabled = false;
119                         hscroll.Visible = false;
120
121                         vscroll = new ImplicitVScrollBar();
122                         vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
123                         vscroll.control_style &= ~ControlStyles.Selectable;
124                         vscroll.Enabled = false;
125                         vscroll.Visible = false;
126
127                         SuspendLayout ();
128                         this.Controls.AddImplicit (hscroll);
129                         this.Controls.AddImplicit (vscroll);
130                         ResumeLayout ();
131                         
132                         //SetStyle(ControlStyles.ResizeRedraw, true);
133                         SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick, false);
134
135                         canvas_width = ClientSize.Width;
136                         canvas_height = ClientSize.Height;
137
138                         CalculateScrollBars();
139                 }
140                 #endregion      // Internal Constructor
141
142                 #region Private and Internal Methods
143                 internal string CaseAdjust(string s) {
144                         if (character_casing == CharacterCasing.Normal) {
145                                 return s;
146                         }
147                         if (character_casing == CharacterCasing.Lower) {
148                                 return s.ToLower();
149                         } else {
150                                 return s.ToUpper();
151                         }
152                 }
153                 #endregion      // Private and Internal Methods
154
155                 #region Public Instance Properties
156                 [DefaultValue(false)]
157                 public bool AcceptsTab {
158                         get {
159                                 return accepts_tab;
160                         }
161
162                         set {
163                                 if (value != accepts_tab) {
164                                         accepts_tab = value;
165                                         OnAcceptsTabChanged(EventArgs.Empty);
166                                 }
167                         }
168                 }
169
170                 [DefaultValue(true)]
171                 [Localizable(true)]
172                 [RefreshProperties(RefreshProperties.Repaint)]
173                 public virtual bool AutoSize {
174                         get {
175                                 return auto_size;
176                         }
177
178                         set {
179                                 if (value != auto_size) {
180                                         auto_size = value;
181                                         if (auto_size) {
182                                                 if (PreferredHeight != ClientSize.Height) {
183                                                         ClientSize = new Size(ClientSize.Width, PreferredHeight);
184                                                 }
185                                         }
186                                         OnAutoSizeChanged(EventArgs.Empty);
187                                 }
188                         }
189                 }
190
191                 [DispId(-501)]
192                 public override System.Drawing.Color BackColor {
193                         get {
194                                 return base.BackColor;
195                         }
196                         set {
197                                 base.BackColor = value;
198                         }
199                 }
200
201                 [Browsable(false)]
202                 [EditorBrowsable(EditorBrowsableState.Never)]
203                 public override System.Drawing.Image BackgroundImage {
204                         get {
205                                 return base.BackgroundImage;
206                         }
207                         set {
208                                 base.BackgroundImage = value;
209                         }
210                 }
211
212                 [DefaultValue(BorderStyle.Fixed3D)]
213                 [DispId(-504)]
214                 public BorderStyle BorderStyle {
215                         get { return InternalBorderStyle; }
216                         set { 
217                                 InternalBorderStyle = value; 
218                                 OnBorderStyleChanged(EventArgs.Empty);
219                         }
220                 }
221
222                 [Browsable(false)]
223                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
224                 public bool CanUndo {
225                         get {
226                                 return undo;
227                         }
228                 }
229
230                 [DispId(-513)]
231                 public override System.Drawing.Color ForeColor {
232                         get {
233                                 return base.ForeColor;
234                         }
235                         set {
236                                 base.ForeColor = value;
237                         }
238                 }
239
240                 [DefaultValue(true)]
241                 public bool HideSelection {
242                         get {
243                                 return hide_selection;
244                         }
245
246                         set {
247                                 if (value != hide_selection) {
248                                         hide_selection = value;
249                                         OnHideSelectionChanged(EventArgs.Empty);
250                                 }
251                                 if (hide_selection) {
252                                         document.selection_visible = false;
253                                 } else {
254                                         document.selection_visible = true;
255                                 }
256                                 document.InvalidateSelectionArea();
257
258                         }
259                 }
260
261                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
262                 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
263                 [Localizable(true)]
264                 public string[] Lines {
265                         get {
266                                 string[]        lines;
267                                 int             i;
268                                 int             l;
269
270                                 l = document.Lines;
271                                 lines = new string[l];
272
273                                 for (i = 1; i <= l; i++) {
274                                         lines[i - 1] = document.GetLine(i).text.ToString();
275                                 }
276
277                                 return lines;
278                         }
279
280                         set {
281                                 int     i;
282                                 int     l;
283                                 Brush   brush;
284
285                                 document.Empty();
286
287                                 l = value.Length;
288                                 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
289
290                                 for (i = 0; i < l; i++) {
291                                         document.Add(i+1, CaseAdjust(value[i]), alignment, Font, brush);
292                                 }
293                                 CalculateDocument();
294                                 OnTextChanged(EventArgs.Empty);
295                         }
296                 }
297
298                 [DefaultValue(32767)]
299                 [Localizable(true)]
300                 public virtual int MaxLength {
301                         get {
302                                 if (max_length == 2147483646) { // We don't distinguish between single and multi-line limits
303                                         return 0;
304                                 }
305                                 return max_length;
306                         }
307
308                         set {
309                                 if (value != max_length) {
310                                         max_length = value;
311                                 }
312                         }
313                 }
314
315                 [Browsable(false)]
316                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
317                 public bool Modified {
318                         get {
319                                 return modified;
320                         }
321
322                         set {
323                                 if (value != modified) {
324                                         modified = value;
325                                         OnModifiedChanged(EventArgs.Empty);
326                                 }
327                         }
328                 }
329
330                 [DefaultValue(false)]
331                 [Localizable(true)]
332                 [RefreshProperties(RefreshProperties.All)]
333                 public virtual bool Multiline {
334                         get {
335                                 return multiline;
336                         }
337
338                         set {
339                                 if (value != multiline) {
340                                         multiline = value;
341                                         // Make sure we update our size; the user may have already set the size before going to multiline
342                                         if (multiline && requested_height != -1) {
343                                                 Height = requested_height;
344                                                 requested_height = -1;
345                                         }
346
347                                         OnMultilineChanged(EventArgs.Empty);
348                                 }
349
350                                 document.multiline = multiline;
351
352                                 if (multiline) {
353                                         document.Wrap = word_wrap;
354                                         document.PasswordChar = "";
355
356                                 } else {
357                                         document.Wrap = false;
358                                         if (this.password_char != '\0') {
359                                                 document.PasswordChar = password_char.ToString();
360                                         } else {
361                                                 document.PasswordChar = "";
362                                         }
363                                 }
364                         }
365                 }
366
367                 [Browsable(false)]
368                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
369                 [EditorBrowsable(EditorBrowsableState.Advanced)]
370                 public int PreferredHeight {
371                         get {
372                                 return this.Font.Height + 7;    // FIXME - consider border style as well
373                         }
374                 }
375
376                 [DefaultValue(false)]
377                 public bool ReadOnly {
378                         get {
379                                 return read_only;
380                         }
381
382                         set {
383                                 if (value != read_only) {
384                                         read_only = value;
385                                         OnReadOnlyChanged(EventArgs.Empty);
386                                 }
387                         }
388                 }
389
390                 [Browsable(false)]
391                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
392                 public virtual string SelectedText {
393                         get {
394                                 return document.GetSelection();
395                         }
396
397                         set {
398                                 if (!read_only) {
399                                         document.ReplaceSelection(CaseAdjust(value));
400                                         ScrollToCaret();
401                                         OnTextChanged(EventArgs.Empty);
402                                 }
403                         }
404                 }
405
406                 [Browsable(false)]
407                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
408                 public virtual int SelectionLength {
409                         get {
410                                 return document.SelectionLength();
411                         }
412
413                         set {
414                                 if (value != 0) {
415                                         int     start;
416                                         Line    line;
417                                         LineTag tag;
418                                         int     pos;
419
420                                         start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
421
422                                         document.CharIndexToLineTag(start + value, out line, out tag, out pos);
423                                         document.SetSelectionEnd(line, pos);
424                                         document.PositionCaret(line, pos);
425                                 } else {
426                                         document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos);
427                                         document.PositionCaret(document.selection_start.line, document.selection_start.pos);
428                                 }
429                         }
430                 }
431
432                 [Browsable(false)]
433                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
434                 public int SelectionStart {
435                         get {
436                                 int index;
437
438                                 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
439
440                                 return index;
441                         }
442
443                         set {
444                                 Line    line;
445                                 LineTag tag;
446                                 int     pos;
447
448                                 document.CharIndexToLineTag(value, out line, out tag, out pos);
449                                 document.SetSelectionStart(line, pos);
450                         }
451                 }
452
453                 [Localizable(true)]
454                 public override string Text {
455                         get {
456                                 if (document == null || document.Root == null || document.Root.text == null) {
457                                         return string.Empty;
458                                 }
459
460                                 if (!multiline) {
461                                         return document.Root.text.ToString();
462                                 } else {
463                                         StringBuilder   sb;
464                                         int             i;
465
466                                         sb = new StringBuilder();
467
468                                         for (i = 1; i < document.Lines; i++) {
469                                                 sb.Append(document.GetLine(i).text.ToString() + Environment.NewLine);
470                                         }
471                                         sb.Append(document.GetLine(document.Lines).text.ToString());
472                                         return sb.ToString();
473                                 }
474                         }
475
476                         set {
477                                 if (value == base.Text) {
478                                         return;
479                                 }
480
481                                 if ((value != null) && (value != "")) {
482                                         Line    line;
483
484                                         if (multiline) {
485                                                 string[]        lines;
486
487                                                 lines = value.Split(new char[] {'\n'});
488
489                                                 for (int i = 0; i < lines.Length; i++) {
490                                                         if (lines[i].EndsWith("\r")) {
491                                                                 lines[i] = lines[i].Substring(0, lines[i].Length - 1);
492                                                         }
493                                                 }
494                                                 this.Lines = lines;
495
496                                                 line = document.GetLine(1);
497                                                 document.SetSelectionStart(line, 0);
498
499                                                 line = document.GetLine(document.Lines);
500                                                 document.SetSelectionEnd(line, line.text.Length);
501                                                 document.PositionCaret(line, line.text.Length);
502                                         } else {
503                                                 document.Clear();
504                                                 document.Add(1, CaseAdjust(value), alignment, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
505                                                 CalculateDocument();
506                                                 line = document.GetLine(1);
507                                                 document.SetSelectionStart(line, 0);
508                                                 document.SetSelectionEnd(line, value.Length);
509                                                 document.PositionCaret(line, value.Length);
510                                         }
511                                 } else {
512                                         document.Empty();
513                                         CalculateDocument();
514                                 }
515                                 base.Text = value;
516                                 // Not needed, base.Text already fires it
517                                 // OnTextChanged(EventArgs.Empty);
518                         }
519                 }
520
521                 [Browsable(false)]
522                 public virtual int TextLength {
523                         get {
524                                 if (document == null || document.Root == null || document.Root.text == null) {
525                                         return 0;
526                                 }
527
528                                 if (!multiline) {
529                                         return document.Root.text.Length;
530                                 } else {
531                                         int     total;
532                                         int     i;
533
534                                         total = 0;
535                                         for (i = 1; i < document.Lines; i++) {
536                                                 total += document.GetLine(i).text.Length + Environment.NewLine.Length;
537                                         }
538                                         total += document.GetLine(i).text.Length;
539
540                                         return total;
541                                 }
542                         }
543                 }
544
545                 [DefaultValue(true)]
546                 [Localizable(true)]
547                 public bool WordWrap {
548                         get {
549                                 return word_wrap;
550                         }
551
552                         set {
553                                 if (value != word_wrap) {
554                                         if (multiline) {
555                                                 word_wrap = value;
556                                                 document.Wrap = value;
557                                         }
558                                 }
559                         }
560                 }
561                 #endregion      // Public Instance Properties
562
563                 #region Protected Instance Properties
564                 protected override CreateParams CreateParams {
565                         get {
566                                 return base.CreateParams;
567                         }
568                 }
569
570                 protected override System.Drawing.Size DefaultSize {
571                         get {
572                                 return new Size(100, 20);
573                         }
574                 }
575                 #endregion      // Protected Instance Properties
576
577                 #region Public Instance Methods
578                 public void AppendText(string text) {
579                         if (multiline) {
580                                 // Grab the formatting for the last element
581                                 document.MoveCaret(CaretDirection.CtrlEnd);
582                                 // grab the end tag
583                                 if (document.CaretTag.next != null) {
584                                         document.CaretTag = document.CaretTag.next;
585                                 }
586                                 document.Insert(document.CaretLine, document.CaretTag, document.CaretPosition, false, text);
587
588                                 CalculateDocument();
589                         } else {
590                                 document.MoveCaret(CaretDirection.CtrlEnd);
591                                 document.InsertStringAtCaret(text, true);
592
593                                 Invalidate();
594                         }
595
596                         document.MoveCaret(CaretDirection.CtrlEnd);
597                         document.SetSelectionStart(document.CaretLine, document.CaretPosition);
598                         document.SetSelectionEnd(document.CaretLine, document.CaretPosition);
599
600                         OnTextChanged(EventArgs.Empty);
601                 }
602
603                 public void Clear() {
604                         Text = null;
605                 }
606
607                 public void ClearUndo() {
608                         document.undo.Clear();
609                 }
610
611                 public void Copy() {
612                         DataObject      o;
613
614                         o = new DataObject(DataFormats.Text, SelectedText);
615                         if (this is RichTextBox) {
616                                 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
617                         }
618                         Clipboard.SetDataObject(o);
619                 }
620
621                 public void Cut() {
622                         DataObject      o;
623
624                         o = new DataObject(DataFormats.Text, SelectedText);
625                         if (this is RichTextBox) {
626                                 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
627                         }
628                         Clipboard.SetDataObject(o);
629                         document.ReplaceSelection("");
630                 }
631
632                 public void Paste() {
633                         Paste(null, false);
634                 }
635
636                 public void ScrollToCaret() {
637                         this.CaretMoved(this, EventArgs.Empty);
638                 }
639
640                 public void Select(int start, int length) {
641                         SelectionStart = start;
642                         SelectionLength = length;
643                 }
644
645
646                 public void SelectAll() {
647                         Line    last;
648
649                         last = document.GetLine(document.Lines);
650                         document.SetSelectionStart(document.GetLine(1), 0);
651                         document.SetSelectionEnd(last, last.text.Length);
652                 }
653
654                 public override string ToString() {
655                         return Text;
656                 }
657
658                 public void Undo() {
659                         document.undo.Undo();
660                 }
661                 #endregion      // Public Instance Methods
662
663                 #region Protected Instance Methods
664                 protected override void CreateHandle() {
665                         base.CreateHandle ();
666                 }
667
668                 protected override bool IsInputKey(Keys keyData) {
669                         if ((keyData & Keys.Alt) != 0) {
670                                 return base.IsInputKey(keyData);
671                         }
672
673                         switch (keyData & Keys.KeyCode) {
674                                 case Keys.Enter: {
675                                         if (multiline && accepts_return) {
676                                                 return true;
677                                         }
678                                         return false;
679                                 }
680
681                                 case Keys.Tab: {
682                                         if (accepts_tab && multiline) {
683                                                 if ((keyData & Keys.Control) == 0) {
684                                                         return true;
685                                                 }
686                                         }
687                                         return false;
688                                 }
689
690                                 case Keys.Left:
691                                 case Keys.Right:
692                                 case Keys.Up:
693                                 case Keys.Down:
694                                 case Keys.PageUp:
695                                 case Keys.PageDown:
696                                 case Keys.Home:
697                                 case Keys.End: {
698                                         return true;
699                                 }
700                         }
701                         return false;
702                 }
703
704
705                 protected virtual void OnAcceptsTabChanged(EventArgs e) {
706                         if (AcceptsTabChanged != null) {
707                                 AcceptsTabChanged(this, e);
708                         }
709                 }
710
711                 protected virtual void OnAutoSizeChanged(EventArgs e) {
712                         if (AutoSizeChanged != null) {
713                                 AutoSizeChanged(this, e);
714                         }
715                 }
716
717                 protected virtual void OnBorderStyleChanged(EventArgs e) {
718                         if (BorderStyleChanged != null) {
719                                 BorderStyleChanged(this, e);
720                         }
721                 }
722
723                 protected override void OnFontChanged(EventArgs e) {
724                         base.OnFontChanged (e);
725
726                         if (auto_size) {
727                                 if (PreferredHeight != ClientSize.Height) {
728                                         Height = PreferredHeight;
729                                 }
730                         }
731                 }
732
733                 protected override void OnHandleCreated(EventArgs e) {
734                         base.OnHandleCreated (e);
735                 }
736
737                 protected override void OnHandleDestroyed(EventArgs e) {
738                         base.OnHandleDestroyed (e);
739                 }
740
741                 protected virtual void OnHideSelectionChanged(EventArgs e) {
742                         if (HideSelectionChanged != null) {
743                                 HideSelectionChanged(this, e);
744                         }
745                 }
746
747                 protected virtual void OnModifiedChanged(EventArgs e) {
748                         if (ModifiedChanged != null) {
749                                 ModifiedChanged(this, e);
750                         }
751                 }
752
753                 protected virtual void OnMultilineChanged(EventArgs e) {
754                         if (MultilineChanged != null) {
755                                 MultilineChanged(this, e);
756                         }
757                 }
758
759                 protected virtual void OnReadOnlyChanged(EventArgs e) {
760                         if (ReadOnlyChanged != null) {
761                                 ReadOnlyChanged(this, e);
762                         }
763                 }
764
765                 protected override bool ProcessDialogKey(Keys keyData) {
766                         return base.ProcessDialogKey(keyData);
767                 }
768
769                 private bool ProcessKey(Keys keyData) {
770                         bool control;
771                         bool shift;
772
773                         control = (Control.ModifierKeys & Keys.Control) != 0;
774                         shift = (Control.ModifierKeys & Keys.Shift) != 0;
775
776                         switch (keyData & Keys.KeyCode) {
777                                 case Keys.X: {  // Cut (Ctrl-X)
778                                         if (control) {
779                                                 Cut();
780                                                 return true;
781                                         }
782                                         return false;
783                                 }
784
785                                 case Keys.C: {  // Copy (Ctrl-C)
786                                         if (control) {
787                                                 Copy();
788                                                 return true;
789                                         }
790                                         return false;
791                                 }
792
793                                 case Keys.V: {  // Paste (Ctrl-V)
794                                         if (control) {
795                                                 return Paste(null, true);
796                                         }
797                                         return false;
798                                 }
799
800                                 case Keys.Z: {  // Undo (Ctrl-Z)
801                                         if (control) {
802                                                 Undo();
803                                                 return true;
804                                         }
805                                         return false;
806                                 }
807
808                                 case Keys.Left: {
809                                         if (control) {
810                                                 document.MoveCaret(CaretDirection.WordBack);
811                                         } else {
812                                                 if (!document.selection_visible || shift) {
813                                                         document.MoveCaret(CaretDirection.CharBack);
814                                                 } else {
815                                                         document.MoveCaret(CaretDirection.SelectionStart);
816                                                 }
817                                         }
818
819                                         if (!shift) {
820                                                 document.SetSelectionToCaret(true);
821                                         } else {
822                                                 document.SetSelectionToCaret(false);
823                                         }
824
825                                         CaretMoved(this, null);
826                                         return true;
827                                 }
828
829                                 case Keys.Right: {
830                                         if (control) {
831                                                 document.MoveCaret(CaretDirection.WordForward);
832                                         } else {
833                                                 if (!document.selection_visible || shift) {
834                                                         document.MoveCaret(CaretDirection.CharForward);
835                                                 } else {
836                                                         document.MoveCaret(CaretDirection.SelectionEnd);
837                                                 }
838                                         }
839                                         if (!shift) {
840                                                 document.SetSelectionToCaret(true);
841                                         } else {
842                                                 document.SetSelectionToCaret(false);
843                                         }
844
845                                         CaretMoved(this, null);
846                                         return true;
847                                 }
848
849                                 case Keys.Up: {
850                                         if (control) {
851                                                 if (document.CaretPosition == 0) {
852                                                         document.MoveCaret(CaretDirection.LineUp);
853                                                 } else {
854                                                         document.MoveCaret(CaretDirection.Home);
855                                                 }
856                                         } else {
857                                                 document.MoveCaret(CaretDirection.LineUp);
858                                         }
859
860                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
861                                                 document.SetSelectionToCaret(true);
862                                         } else {
863                                                 document.SetSelectionToCaret(false);
864                                         }
865
866                                         CaretMoved(this, null);
867                                         return true;
868                                 }
869
870                                 case Keys.Down: {
871                                         if (control) {
872                                                 if (document.CaretPosition == document.CaretLine.Text.Length) {
873                                                         document.MoveCaret(CaretDirection.LineDown);
874                                                 } else {
875                                                         document.MoveCaret(CaretDirection.End);
876                                                 }
877                                         } else {
878                                                 document.MoveCaret(CaretDirection.LineDown);
879                                         }
880
881                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
882                                                 document.SetSelectionToCaret(true);
883                                         } else {
884                                                 document.SetSelectionToCaret(false);
885                                         }
886
887                                         CaretMoved(this, null);
888                                         return true;
889                                 }
890
891                                 case Keys.Home: {
892                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
893                                                 document.MoveCaret(CaretDirection.CtrlHome);
894                                         } else {
895                                                 document.MoveCaret(CaretDirection.Home);
896                                         }
897
898                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
899                                                 document.SetSelectionToCaret(true);
900                                         } else {
901                                                 document.SetSelectionToCaret(false);
902                                         }
903
904                                         CaretMoved(this, null);
905                                         return true;
906                                 }
907
908                                 case Keys.End: {
909                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
910                                                 document.MoveCaret(CaretDirection.CtrlEnd);
911                                         } else {
912                                                 document.MoveCaret(CaretDirection.End);
913                                         }
914
915                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
916                                                 document.SetSelectionToCaret(true);
917                                         } else {
918                                                 document.SetSelectionToCaret(false);
919                                         }
920
921                                         CaretMoved(this, null);
922                                         return true;
923                                 }
924
925                                 case Keys.Enter: {
926                                         // ignoring accepts_return, fixes bug #76355
927                                         if (!read_only && multiline && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
928                                                 Line    line;
929
930                                                 if (document.selection_visible) {
931                                                         document.ReplaceSelection("");
932                                                 }
933                                                 document.SetSelectionToCaret(true);
934
935                                                 line = document.CaretLine;
936
937                                                 document.Split(document.CaretLine, document.CaretTag, document.CaretPosition, false);
938                                                 OnTextChanged(EventArgs.Empty);
939                                                 document.UpdateView(line, 2, 0);
940                                                 document.MoveCaret(CaretDirection.CharForward);
941                                                 CaretMoved(this, null);
942                                                 return true;
943                                         }
944                                         break;
945                                 }
946
947                                 case Keys.Tab: {
948                                         if (!read_only && accepts_tab && multiline) {
949                                                 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
950                                                 if (document.selection_visible) {
951                                                         document.ReplaceSelection("");
952                                                 }
953                                                 document.SetSelectionToCaret(true);
954
955                                                 OnTextChanged(EventArgs.Empty);
956                                                 CaretMoved(this, null);
957                                                 return true;
958                                         }
959                                         break;
960                                 }
961
962
963                                 case Keys.Back: {
964                                         if (read_only) {
965                                                 break;
966                                         }
967
968                                         // delete only deletes on the line, doesn't do the combine
969                                         if (document.selection_visible) {
970                                                 document.ReplaceSelection("");
971                                         }
972                                         document.SetSelectionToCaret(true);
973
974                                         if (document.CaretPosition == 0) {
975                                                 if (document.CaretLine.LineNo > 1) {
976                                                         Line    line;
977                                                         int     new_caret_pos;
978
979                                                         line = document.GetLine(document.CaretLine.LineNo - 1);
980                                                         new_caret_pos = line.text.Length;
981
982                                                         document.Combine(line, document.CaretLine);
983                                                         document.UpdateView(line, 1, 0);
984                                                         document.PositionCaret(line, new_caret_pos);
985                                                         //document.MoveCaret(CaretDirection.CharForward);
986                                                         document.UpdateCaret();
987                                                         OnTextChanged(EventArgs.Empty);
988                                                 }
989                                         } else {
990                                                 if (!control || document.CaretPosition == 0) {
991                                                         document.DeleteChar(document.CaretTag, document.CaretPosition, false);
992                                                         document.MoveCaret(CaretDirection.CharBack);
993                                                 } else {
994                                                         int start_pos;
995
996                                                         start_pos = document.CaretPosition - 1;
997
998                                                         while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
999                                                                 start_pos--;
1000                                                         }
1001                                                         document.DeleteChars(document.CaretTag, start_pos, document.CaretPosition - start_pos);
1002                                                         document.PositionCaret(document.CaretLine, start_pos);
1003                                                 }
1004                                                 document.UpdateCaret();
1005                                                 OnTextChanged(EventArgs.Empty);
1006                                         }
1007                                         CaretMoved(this, null);
1008                                         return true;
1009                                 }
1010
1011                                 case Keys.Insert: {
1012                                         if (shift) {
1013                                                 Paste(null, true);
1014                                                 return true;
1015                                         }
1016
1017                                         if (control) {
1018                                                 Copy();
1019                                                 return true;
1020                                         }
1021
1022                                         // FIXME - need overwrite/insert toggle?
1023                                         return false;
1024                                 }
1025
1026                                 case Keys.Delete: {
1027                                         if (shift) {
1028                                                 Cut();
1029                                                 return true;
1030                                         }
1031
1032                                         if (read_only) {
1033                                                 break;
1034                                         }
1035
1036                                         if (document.selection_visible) {
1037                                                 document.ReplaceSelection("");
1038                                         } else {
1039                                                 // DeleteChar only deletes on the line, doesn't do the combine
1040                                                 if (document.CaretPosition == document.CaretLine.Text.Length) {
1041                                                         if (document.CaretLine.LineNo < document.Lines) {
1042                                                                 Line    line;
1043
1044                                                                 line = document.GetLine(document.CaretLine.LineNo + 1);
1045                                                                 document.Combine(document.CaretLine, line);
1046                                                                 document.UpdateView(document.CaretLine, 2, 0);
1047
1048                                                                 #if not_Debug
1049                                                                 Line    check_first;
1050                                                                 Line    check_second;
1051
1052                                                                 check_first = document.GetLine(document.CaretLine.LineNo);
1053                                                                 check_second = document.GetLine(check_first.line_no + 1);
1054
1055                                                                 Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
1056                                                                 #endif
1057
1058                                                                 // Caret doesn't move
1059                                                         }
1060                                                 } else {
1061                                                         if (!control) {
1062                                                                 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
1063                                                         } else {
1064                                                                 int end_pos;
1065
1066                                                                 end_pos = document.CaretPosition;
1067
1068                                                                 while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
1069                                                                         end_pos++;
1070                                                                 }
1071
1072                                                                 if (end_pos < document.CaretLine.Text.Length) {
1073                                                                         end_pos++;
1074                                                                 }
1075                                                                 document.DeleteChars(document.CaretTag, document.CaretPosition, end_pos - document.CaretPosition);
1076                                                         }
1077                                                 }
1078                                         }
1079
1080                                         OnTextChanged(EventArgs.Empty);
1081                                         document.AlignCaret();
1082                                         document.UpdateCaret();
1083                                         CaretMoved(this, null);
1084                                         return true;
1085                                 }
1086                         }
1087                         return false;
1088                 }
1089
1090                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
1091                         // Make sure we don't get sized bigger than we want to be
1092                         if (!richtext) {
1093                                 if (!multiline) {
1094                                         if (height != PreferredHeight) {
1095                                                 requested_height = height;
1096                                                 height = PreferredHeight;
1097                                                 specified |= BoundsSpecified.Height;
1098                                         }
1099                                 }
1100                         }
1101
1102                         base.SetBoundsCore (x, y, width, height, specified);
1103
1104                         TextBoxBase_SizeChanged(this, EventArgs.Empty);
1105                         CalculateDocument();
1106                 }
1107
1108                 protected override void WndProc(ref Message m) {
1109                         switch ((Msg)m.Msg) {
1110                                 case Msg.WM_PAINT: {
1111                                         PaintEventArgs  paint_event;
1112
1113                                         paint_event = XplatUI.PaintEventStart(Handle, true);
1114                                         
1115                                         PaintControl(paint_event);
1116                                         XplatUI.PaintEventEnd(Handle, true);
1117                                         DefWndProc(ref m);
1118                                         return;
1119                                 }
1120
1121                                 case Msg.WM_SETFOCUS: {
1122                                         // Set caret
1123                                         document.CaretHasFocus();
1124                                         base.WndProc(ref m);
1125                                         return;
1126                                 }
1127
1128                                 case Msg.WM_KILLFOCUS: {
1129                                         // Kill caret
1130                                         document.CaretLostFocus();
1131                                         base.WndProc(ref m);
1132                                         return;
1133                                 }
1134
1135                                 case Msg.WM_KEYDOWN: {
1136                                         if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
1137                                                 m.Result = IntPtr.Zero;
1138                                                 return;
1139                                         }
1140                                         DefWndProc (ref m);
1141                                         return;
1142                                 }
1143
1144                                 case Msg.WM_CHAR: {
1145                                         if (ProcessKeyMessage(ref m)) {
1146                                                 m.Result = IntPtr.Zero;
1147                                                 return;
1148                                         }
1149
1150                                         // Ctrl-Backspace generates a real char, whack it
1151                                         if (m.WParam.ToInt32() == 127) {
1152                                                 base.WndProc(ref m);
1153                                                 return;
1154                                         }
1155
1156                                         if (!read_only && (m.WParam.ToInt32() >= 32)) { // FIXME, tabs should probably go through
1157                                                 if (document.selection_visible) {
1158                                                         document.ReplaceSelection("");
1159                                                 }
1160
1161                                                 switch (character_casing) {
1162                                                         case CharacterCasing.Normal: {
1163                                                                 if (document.Length < max_length) {
1164                                                                         document.InsertCharAtCaret((char)m.WParam, true);
1165                                                                         OnTextChanged(EventArgs.Empty);
1166                                                                         CaretMoved(this, null);
1167                                                                 } else {
1168                                                                         XplatUI.AudibleAlert();
1169                                                                 }
1170                                                                 return;
1171                                                         }
1172
1173                                                         case CharacterCasing.Lower: {
1174                                                                 if (document.Length < max_length) {
1175                                                                         document.InsertCharAtCaret(Char.ToLower((char)m.WParam), true);
1176                                                                         OnTextChanged(EventArgs.Empty);
1177                                                                         CaretMoved(this, null);
1178                                                                 } else {
1179                                                                         XplatUI.AudibleAlert();
1180                                                                 }
1181                                                                 return;
1182                                                         }
1183
1184                                                         case CharacterCasing.Upper: {
1185                                                                 if (document.Length < max_length) {
1186                                                                         document.InsertCharAtCaret(Char.ToUpper((char)m.WParam), true);
1187                                                                         OnTextChanged(EventArgs.Empty);
1188                                                                         CaretMoved(this, null);
1189                                                                 } else {
1190                                                                         XplatUI.AudibleAlert();
1191                                                                 }
1192                                                                 return;
1193                                                         }
1194                                                 }
1195                                         }
1196                                         return;
1197                                 }
1198
1199                                 default: {
1200                                         base.WndProc(ref m);
1201                                         return;
1202                                 }
1203                         }
1204                 }
1205
1206                 #endregion      // Protected Instance Methods
1207
1208                 #region Events
1209                 public event EventHandler       AcceptsTabChanged;
1210                 public event EventHandler       AutoSizeChanged;
1211                 [Browsable(false)]
1212                 [EditorBrowsable(EditorBrowsableState.Never)]
1213                 public new event EventHandler BackgroundImageChanged {
1214                         add { base.BackgroundImageChanged += value; }
1215                         remove { base.BackgroundImageChanged -= value; }
1216                 }
1217                 public event EventHandler       BorderStyleChanged;
1218                 [Browsable(false)]
1219                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1220                 public event EventHandler       Click;
1221                 public event EventHandler       HideSelectionChanged;
1222                 public event EventHandler       ModifiedChanged;
1223                 public event EventHandler       MultilineChanged;
1224                 [Browsable(false)]
1225                 [EditorBrowsable(EditorBrowsableState.Never)]
1226                 public event PaintEventHandler  Paint;
1227                 public event EventHandler       ReadOnlyChanged;
1228
1229                 internal event EventHandler     HScrolled;
1230                 internal event EventHandler     VScrolled;
1231                 #endregion      // Events
1232
1233                 #region Private Methods
1234                 internal Document Document {
1235                         get {
1236                                 return document;
1237                         }
1238
1239                         set {
1240                                 document = value;
1241                         }
1242                 }
1243
1244                 internal Graphics CreateGraphicsInternal() {
1245                         if (IsHandleCreated) {
1246                                 return base.CreateGraphics();
1247                         }
1248
1249                         return Graphics.FromImage(bmp);
1250                 }
1251
1252                 #if Debug
1253                 static int current;
1254                 #endif
1255
1256                 private void PaintControl(PaintEventArgs pevent) {
1257                         // Fill background
1258                         pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1259                         pevent.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
1260
1261                         // Draw the viewable document
1262                         document.Draw(pevent.Graphics, pevent.ClipRectangle);
1263
1264                         Rectangle       rect = ClientRectangle;
1265                         rect.Width--;
1266                         rect.Height--;
1267                         //pevent.Graphics.DrawRectangle(ThemeEngine.Current.ResPool.GetPen(ThemeEngine.Current.ColorControlDark), rect);
1268
1269                         #if Debug
1270                                 int             start;
1271                                 int             end;
1272                                 Line            line;
1273                                 int             line_no;
1274                                 Pen             p;
1275
1276                                 p = new Pen(Color.Red, 1);
1277
1278                                 // First, figure out from what line to what line we need to draw
1279                                 start = document.GetLineByPixel(pevent.ClipRectangle.Top - document.ViewPortY, false).line_no;
1280                                 end = document.GetLineByPixel(pevent.ClipRectangle.Bottom - document.ViewPortY, false).line_no;
1281
1282                                 //Console.WriteLine("Starting drawing on line '{0}'", document.GetLine(start));
1283                                 //Console.WriteLine("Ending drawing on line '{0}'", document.GetLine(end));
1284
1285                                 line_no = start;
1286                                 while (line_no <= end) {
1287                                         line = document.GetLine(line_no);
1288
1289                                         if (draw_lines) {
1290                                                 for (int i = 0; i < line.text.Length; i++) {
1291                                                         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);
1292                                                 }
1293                                         }
1294
1295                                         line_no++;
1296                                 }
1297                         #endif
1298                 }
1299
1300                 private void TextBoxBase_MouseDown(object sender, MouseEventArgs e) {
1301                         if (e.Button == MouseButtons.Left) {
1302                                 TimeSpan interval;
1303
1304                                 interval = DateTime.Now - click_last;
1305                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1306                                 this.Capture = true;
1307                                 
1308                                 // Handle place caret/select word/select line behaviour
1309                                 if (e.Clicks == 1) {
1310                                         if (SystemInformation.DoubleClickTime < interval.TotalMilliseconds) {
1311                                                 #if DebugClick
1312                                                         Console.WriteLine("Single Click Invalidating from char {0} to char {1} ({2})", document.selection_start.pos, document.selection_end.pos, document.selection_start.line.text.ToString(document.selection_start.pos, document.selection_end.pos - document.selection_start.pos));
1313                                                 #endif
1314                                                 document.SetSelectionToCaret(true);
1315                                                 click_mode = CaretSelection.Position;
1316                                         } else {
1317                                                 #if DebugClick
1318                                                         Console.WriteLine("Tripple Click Selecting line");
1319                                                 #endif
1320                                                 document.ExpandSelection(CaretSelection.Line, false);
1321                                                 click_mode = CaretSelection.Line;
1322                                         }
1323                                 } else {
1324                                         // We select the line if the word is already selected, and vice versa
1325                                         if (click_mode != CaretSelection.Word) {
1326                                                 if (click_mode == CaretSelection.Line) {
1327                                                         document.Invalidate(document.selection_start.line, 0, document.selection_start.line, document.selection_start.line.text.Length);
1328                                                 }
1329                                                 click_mode = CaretSelection.Word;
1330                                                 document.ExpandSelection(CaretSelection.Word, false);   // Setting initial selection
1331                                         } else {
1332                                                 click_mode = CaretSelection.Line;
1333                                                 document.ExpandSelection(CaretSelection.Line, false);   // Setting initial selection
1334                                         }
1335                                 }
1336
1337                                 // Reset
1338                                 click_last = DateTime.Now;
1339                                 return;
1340                         }
1341
1342                         #if Debug
1343                                 LineTag tag;
1344                                 Line    line;
1345                                 int     pos;
1346
1347                                 if (e.Button == MouseButtons.Right) {
1348                                         draw_lines = !draw_lines;
1349                                         this.Invalidate();
1350                                         Console.WriteLine("SelectedText: {0}, length {1}", this.SelectedText, this.SelectionLength);
1351                                         Console.WriteLine("Selection start: {0}", this.SelectionStart);
1352
1353                                         this.SelectionStart = 10;
1354                                         this.SelectionLength = 5;
1355
1356                                         return;
1357                                 }
1358
1359                                 tag = document.FindTag(e.X + document.ViewPortX, e.Y + document.ViewPortY, out pos, false);
1360
1361                                 Console.WriteLine("Click found tag {0}, character {1}", tag, pos);
1362                                 line = tag.line;
1363                                 switch(current) {
1364                                         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;
1365                                         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;
1366                                         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;
1367                                         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;
1368                                         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;
1369                                         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;
1370                                 }
1371                                 current++;
1372                                 if (current==6) {
1373                                         current=0;
1374                                 }
1375
1376                                 // Update/Recalculate what we see
1377                                 document.UpdateView(line, 0);
1378
1379                                 // Make sure our caret is properly positioned and sized
1380                                 document.AlignCaret();
1381                         #endif
1382                 }
1383
1384                 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1385                         this.Capture = false;
1386                         if (e.Button == MouseButtons.Left) {
1387                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1388                                 if (click_mode == CaretSelection.Position) {
1389                                         document.SetSelectionToCaret(false);
1390                                         document.DisplayCaret();
1391                                 } else {
1392                                         document.ExpandSelection(click_mode, true);
1393                                 }
1394                                 return;
1395                         }
1396                 }
1397
1398                 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1399                         canvas_width = ClientSize.Width;
1400                         canvas_height = ClientSize.Height;
1401                         document.ViewPortWidth = canvas_width;
1402                         document.ViewPortHeight = canvas_height;
1403
1404                         // We always move them, they just might not be displayed
1405                         hscroll.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hscroll.Height, ClientSize.Width - (vscroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0), hscroll.Height);
1406                         vscroll.Bounds = new Rectangle (ClientRectangle.Right - vscroll.Width, ClientRectangle.Top, vscroll.Width, ClientSize.Height - (hscroll.Visible ? SystemInformation.HorizontalScrollBarHeight : 0));
1407                         
1408                 }
1409
1410                 private void TextBoxBase_MouseWheel(object sender, MouseEventArgs e) {
1411                         Line    line;
1412                         int     line_no;
1413                         int     target;
1414
1415                         if (!vscroll.Enabled) {
1416                                 return;
1417                         }
1418
1419                         if (e.Delta < 0) {
1420                                 line_no = document.GetLineByPixel(document.ViewPortY, false).line_no + SystemInformation.MouseWheelScrollLines;
1421                                 if (line_no > document.Lines) {
1422                                         line_no = document.Lines;
1423                                 }
1424                         } else {
1425                                 line_no = document.GetLineByPixel(document.ViewPortY, false).line_no - SystemInformation.MouseWheelScrollLines;
1426                                 if (line_no < 1) {
1427                                         line_no = 1;
1428                                 }
1429                         }
1430
1431                         line = document.GetLine(line_no);
1432                         if (line.Y < vscroll.Maximum) {
1433                                 vscroll.Value = line.Y;
1434                         } else {
1435                                 vscroll.Value = vscroll.Maximum;
1436                         }
1437                 }
1438
1439                 internal void CalculateDocument() {
1440                         if (!IsHandleCreated) {
1441                                 return;
1442                         }
1443                         document.RecalculateDocument(CreateGraphicsInternal());
1444                         CalculateScrollBars();
1445                         Invalidate();   // FIXME - do we need this?
1446                 }
1447
1448                 internal void CalculateScrollBars() {
1449                         // FIXME - need separate calculations for center and right alignment
1450                         // No scrollbars for a single line
1451                         if (document.Width >= ClientSize.Width) {
1452                                 hscroll.Enabled = true;
1453                                 hscroll.Minimum = 0;
1454                                 hscroll.Maximum = document.Width - ClientSize.Width + track_border;
1455                         } else {
1456                                 hscroll.Maximum = document.ViewPortWidth;
1457                                 hscroll.Enabled = false;
1458                         }
1459
1460                         if (document.Height >= ClientSize.Height) {
1461                                 vscroll.Enabled = true;
1462                                 vscroll.Minimum = 0;
1463                                 vscroll.Maximum = document.Height - ClientSize.Height + 1;
1464                         } else {
1465                                 vscroll.Maximum = document.ViewPortHeight;
1466                                 vscroll.Enabled = false;
1467                         }
1468
1469
1470                         if (!multiline) {
1471                                 return;
1472                         }
1473
1474                         if (!WordWrap) {
1475                                 if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1476                                         if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1477                                                 hscroll.Visible = true;
1478                                         }
1479                                 }
1480                         }
1481
1482                         if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1483                                 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1484                                         vscroll.Visible = true;
1485                                 }
1486                         }
1487
1488                         if (hscroll.Visible) {
1489                                 vscroll.Maximum += hscroll.Height;
1490                                 canvas_height = ClientSize.Height - hscroll.Height;
1491                         }
1492
1493                         if (vscroll.Visible) {
1494                                 hscroll.Maximum += vscroll.Width * 2;
1495                                 canvas_width = ClientSize.Width - vscroll.Width * 2;
1496                         }
1497
1498                         TextBoxBase_SizeChanged(this, EventArgs.Empty);
1499                 }
1500
1501                 private void document_WidthChanged(object sender, EventArgs e) {
1502                         CalculateScrollBars();
1503                 }
1504
1505                 private void document_HeightChanged(object sender, EventArgs e) {
1506                         CalculateScrollBars();
1507                 }
1508
1509                 private void hscroll_ValueChanged(object sender, EventArgs e) {
1510                         XplatUI.ScrollWindow(this.Handle, document.ViewPortX-this.hscroll.Value, 0, false);
1511                         document.ViewPortX = this.hscroll.Value;
1512                         document.UpdateCaret();
1513
1514                         if (HScrolled != null) {
1515                                 HScrolled(this, EventArgs.Empty);
1516                         }
1517                 }
1518
1519                 private void vscroll_ValueChanged(object sender, EventArgs e) {
1520                         XplatUI.ScrollWindow(this.Handle, 0, document.ViewPortY-this.vscroll.Value, false);
1521                         document.ViewPortY = this.vscroll.Value;
1522                         document.UpdateCaret();
1523
1524                         if (VScrolled != null) {
1525                                 VScrolled(this, EventArgs.Empty);
1526                         }
1527                 }
1528
1529                 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1530                         // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1531                         if (Capture) {
1532                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1533                                 if (click_mode == CaretSelection.Position) {
1534                                         document.SetSelectionToCaret(false);
1535                                         document.DisplayCaret();
1536                                 } else {
1537                                         document.ExpandSelection(click_mode, true);
1538                                 }
1539                         }
1540                 }
1541                                                                               
1542                 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1543                         if (!richtext) {
1544                                 Line    line;
1545
1546                                 // Font changes apply to the whole document
1547                                 for (int i = 1; i <= document.Lines; i++) {
1548                                         line = document.GetLine(i);
1549                                         LineTag.FormatText(line, 1, line.text.Length, Font, ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor));
1550                                         document.UpdateView(line, 0);
1551                                 }
1552                                 // Make sure the caret height is matching the new font height
1553                                 document.AlignCaret();
1554                         }
1555                 }
1556
1557                 /// <summary>Ensure the caret is always visible</summary>
1558                 internal void CaretMoved(object sender, EventArgs e) {
1559                         Point   pos;
1560                         int     height;
1561                         
1562                         pos = document.Caret;
1563                         //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);
1564
1565                         // Handle horizontal scrolling
1566                         if (document.CaretLine.alignment == HorizontalAlignment.Left) {
1567                                 if (pos.X < (document.ViewPortX + track_width)) {
1568                                         do {
1569                                                 if ((hscroll.Value - track_width) >= hscroll.Minimum) {
1570                                                         hscroll.Value -= track_width;
1571                                                 } else {
1572                                                         hscroll.Value = hscroll.Minimum;
1573                                                 }
1574                                         } while (hscroll.Value > pos.X);
1575                                 }
1576
1577                                 if ((pos.X > (this.canvas_width + document.ViewPortX - track_width)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1578                                         do {
1579                                                 if ((hscroll.Value + track_width) <= hscroll.Maximum) {
1580                                                         hscroll.Value += track_width;
1581                                                 } else {
1582                                                         hscroll.Value = hscroll.Maximum;
1583                                                 }
1584                                         } while (pos.X > (hscroll.Value + this.canvas_width));
1585                                 }
1586                         } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
1587                                 if (pos.X < document.ViewPortX) {
1588                                         if (pos.X > hscroll.Minimum) {
1589                                                 hscroll.Value = pos.X;
1590                                         } else {
1591                                                 hscroll.Value = hscroll.Minimum;
1592                                         }
1593                                 }
1594
1595                                 if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1596                                         hscroll.Value = hscroll.Maximum;
1597                                 }
1598                         } else {
1599                         }
1600
1601                         if (!multiline) {
1602                                 return;
1603                         }
1604
1605                         // Handle vertical scrolling
1606                         height = document.CaretLine.Height + 1;
1607
1608                         if (pos.Y < document.ViewPortY) {
1609                                 vscroll.Value = pos.Y;
1610                         }
1611
1612                         if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {
1613                                 vscroll.Value = pos.Y - canvas_height + height;
1614                         }
1615                 }
1616
1617                 internal bool Paste(DataFormats.Format format, bool obey_length) {
1618                         IDataObject     clip;
1619                         string          s;
1620
1621                         clip = Clipboard.GetDataObject();
1622                         if (clip == null)
1623                                 return false;
1624                         
1625                         if (format == null) {
1626                                 if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
1627                                         format = DataFormats.GetFormat(DataFormats.Rtf);
1628                                 } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
1629                                         format = DataFormats.GetFormat(DataFormats.UnicodeText);
1630                                 } else if (clip.GetDataPresent(DataFormats.Text)) {
1631                                         format = DataFormats.GetFormat(DataFormats.Text);
1632                                 } else {
1633                                         return false;
1634                                 }
1635                         } else {
1636                                 if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
1637                                         return false;
1638                                 }
1639
1640                                 if (!clip.GetDataPresent(format.Name)) {
1641                                         return false;
1642                                 }
1643                         }
1644
1645                         if (format.Name == DataFormats.Rtf) {
1646                                 ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
1647                                 return true;
1648                         } else if (format.Name == DataFormats.UnicodeText) {
1649                                 s = (string)clip.GetData(DataFormats.UnicodeText);
1650                         } else if (format.Name == DataFormats.Text) {
1651                                 s = (string)clip.GetData(DataFormats.Text);
1652                         } else {
1653                                 return false;
1654                         }
1655
1656                         if (!obey_length) {
1657                                 this.SelectedText = s;
1658                         } else {
1659                                 if ((s.Length + document.Length) < max_length) {
1660                                         this.SelectedText = s;
1661                                 } else if (document.Length < max_length) {
1662                                         this.SelectedText = s.Substring(0, max_length - document.Length);
1663                                 }
1664                         }
1665
1666                         return true;
1667                 }
1668                 #endregion      // Private Methods
1669         }
1670 }