2007-03-29 Jonathan Pobst <monkey@jpobst.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 using System.Collections;
38
39 namespace System.Windows.Forms {
40         [DefaultEvent("TextChanged")]
41         [Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + Consts.AssemblySystem_Design)]
42         public abstract class TextBoxBase : Control {
43                 #region Local Variables
44                 internal HorizontalAlignment    alignment;
45                 internal bool                   accepts_tab;
46                 internal bool                   accepts_return;
47                 internal bool                   auto_size;
48                 internal bool                   backcolor_set;
49                 internal CharacterCasing        character_casing;
50                 internal bool                   hide_selection;
51                 internal int                    max_length;
52                 internal bool                   modified;
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 Timer                  scroll_timer;
63                 internal bool                   richtext;
64                 internal bool                   show_selection;         // set to true to always show selection, even if no focus is set
65                 internal int                    selection_length = -1;  // set to the user-specified selection length, or -1 if none
66                 internal bool show_caret_w_selection;  // TextBox shows the caret when the selection is visible
67                 internal int                    requested_height;
68                 internal int                    canvas_width;
69                 internal int                    canvas_height;
70                 static internal int             track_width = 2;        //
71                 static internal int             track_border = 5;       //
72                 internal DateTime               click_last;
73                 internal int                    click_point_x;
74                 internal int                    click_point_y;
75                 internal CaretSelection         click_mode;
76                 internal Bitmap                 bmp;
77                 internal BorderStyle actual_border_style;
78                 internal bool shortcuts_enabled = true;
79                 #if Debug
80                 internal static bool    draw_lines = false;
81                 #endif
82
83                 #endregion      // Local Variables
84
85                 #region Internal Constructor
86                 // Constructor will go when complete, only for testing - pdb
87                 internal TextBoxBase() {
88                         alignment = HorizontalAlignment.Left;
89                         accepts_return = false;
90                         accepts_tab = false;
91                         auto_size = true;
92                         border_style = BorderStyle.Fixed3D;
93                         actual_border_style = BorderStyle.Fixed3D;
94                         character_casing = CharacterCasing.Normal;
95                         hide_selection = true;
96                         max_length = 32767;
97                         modified = true;
98                         password_char = '\0';
99                         read_only = false;
100                         word_wrap = true;
101                         richtext = false;
102                         show_selection = false;
103                         show_caret_w_selection = (this is TextBox);
104                         document = new Document(this);
105                         document.WidthChanged += new EventHandler(document_WidthChanged);
106                         document.HeightChanged += new EventHandler(document_HeightChanged);
107                         //document.CaretMoved += new EventHandler(CaretMoved);
108                         document.Wrap = false;
109                         requested_height = -1;
110                         click_last = DateTime.Now;
111                         click_mode = CaretSelection.Position;
112                         bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
113
114                         MouseDown += new MouseEventHandler(TextBoxBase_MouseDown);
115                         MouseUp += new MouseEventHandler(TextBoxBase_MouseUp);
116                         MouseMove += new MouseEventHandler(TextBoxBase_MouseMove);
117                         SizeChanged += new EventHandler(TextBoxBase_SizeChanged);
118                         FontChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
119                         ForeColorChanged += new EventHandler(TextBoxBase_FontOrColorChanged);
120                         MouseWheel += new MouseEventHandler(TextBoxBase_MouseWheel);
121                         
122                         scrollbars = RichTextBoxScrollBars.None;
123
124                         hscroll = new ImplicitHScrollBar();
125                         hscroll.ValueChanged += new EventHandler(hscroll_ValueChanged);
126                         hscroll.SetStyle (ControlStyles.Selectable, false);
127                         hscroll.Enabled = false;
128                         hscroll.Visible = false;
129                         hscroll.Maximum = Int32.MaxValue;
130
131                         vscroll = new ImplicitVScrollBar();
132                         vscroll.ValueChanged += new EventHandler(vscroll_ValueChanged);
133                         vscroll.SetStyle (ControlStyles.Selectable, false);
134                         vscroll.Enabled = false;
135                         vscroll.Visible = false;
136                         vscroll.Maximum = Int32.MaxValue;
137
138                         SuspendLayout ();
139                         this.Controls.AddImplicit (hscroll);
140                         this.Controls.AddImplicit (vscroll);
141                         ResumeLayout ();
142                         
143                         SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick, false);
144 #if NET_2_0
145                         SetStyle(ControlStyles.UseTextForAccessibility, false);
146 #endif
147
148                         canvas_width = ClientSize.Width;
149                         canvas_height = ClientSize.Height;
150                         document.ViewPortWidth = canvas_width;
151                         document.ViewPortHeight = canvas_height;
152
153                         Cursor = Cursors.IBeam;
154
155                         CalculateScrollBars();
156                 }
157                 #endregion      // Internal Constructor
158
159                 #region Private and Internal Methods
160                 internal string CaseAdjust(string s) {
161                         if (character_casing == CharacterCasing.Normal) {
162                                 return s;
163                         }
164                         if (character_casing == CharacterCasing.Lower) {
165                                 return s.ToLower();
166                         } else {
167                                 return s.ToUpper();
168                         }
169                 }
170
171                 internal override void HandleClick(int clicks, MouseEventArgs me) {
172                         // MS seems to fire the click event in spite of the styles they set
173                         bool click_set = GetStyle (ControlStyles.StandardClick);
174                         bool doubleclick_set = GetStyle (ControlStyles.StandardDoubleClick);
175
176                         // so explicitly set them to true first
177                         SetStyle (ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, true);
178
179                         base.HandleClick (clicks, me);
180
181                         // then revert to our previous state
182                         if (!click_set)
183                                 SetStyle (ControlStyles.StandardClick, false);
184                         if (!doubleclick_set)
185                                 SetStyle (ControlStyles.StandardDoubleClick, false);
186                 }
187
188                 #endregion      // Private and Internal Methods
189
190                 #region Public Instance Properties
191                 [DefaultValue(false)]
192                 [MWFCategory("Behavior")]
193                 public bool AcceptsTab {
194                         get {
195                                 return accepts_tab;
196                         }
197
198                         set {
199                                 if (value != accepts_tab) {
200                                         accepts_tab = value;
201                                         OnAcceptsTabChanged(EventArgs.Empty);
202                                 }
203                         }
204                 }
205
206                 [DefaultValue(true)]
207                 [Localizable(true)]
208                 [RefreshProperties(RefreshProperties.Repaint)]
209                 [MWFCategory("Behavior")]
210                 public
211 #if NET_2_0
212                 override
213 #else
214                 virtual
215 #endif
216                 bool AutoSize {
217                         get {
218                                 return auto_size;
219                         }
220
221                         set {
222                                 if (value != auto_size) {
223                                         auto_size = value;
224                                         if (auto_size) {
225                                                 if (PreferredHeight != ClientSize.Height) {
226                                                         ClientSize = new Size(ClientSize.Width, PreferredHeight);
227                                                 }
228                                         }
229                                         OnAutoSizeChanged(EventArgs.Empty);
230                                 }
231                         }
232                 }
233
234                 [DispId(-501)]
235                 public override System.Drawing.Color BackColor {
236                         get {
237                                 return base.BackColor;
238                         }
239                         set {
240                                 backcolor_set = true;
241                                 base.BackColor = value;
242                         }
243                 }
244
245                 [Browsable(false)]
246                 [EditorBrowsable(EditorBrowsableState.Never)]
247                 public override System.Drawing.Image BackgroundImage {
248                         get {
249                                 return base.BackgroundImage;
250                         }
251                         set {
252                                 base.BackgroundImage = value;
253                         }
254                 }
255
256                 [DefaultValue(BorderStyle.Fixed3D)]
257                 [DispId(-504)]
258                 [MWFCategory("Appearance")]
259                 public BorderStyle BorderStyle {
260                         get { return actual_border_style; }
261                         set {
262                                 if (value == actual_border_style)
263                                         return;
264
265                                 if (actual_border_style != BorderStyle.Fixed3D || value != BorderStyle.Fixed3D)
266                                         Invalidate ();
267
268                                 actual_border_style = value;
269                                 if (value != BorderStyle.Fixed3D)
270                                         value = BorderStyle.None;
271
272                                 InternalBorderStyle = value; 
273                                 OnBorderStyleChanged(EventArgs.Empty);
274                         }
275                 }
276
277                 [Browsable(false)]
278                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
279                 public bool CanUndo {
280                         get {
281                                 return document.undo.CanUndo;
282                         }
283                 }
284
285                 [DispId(-513)]
286                 public override System.Drawing.Color ForeColor {
287                         get {
288                                 return base.ForeColor;
289                         }
290                         set {
291                                 base.ForeColor = value;
292                         }
293                 }
294
295                 [DefaultValue(true)]
296                 [MWFCategory("Behavior")]
297                 public bool HideSelection {
298                         get {
299                                 return hide_selection;
300                         }
301
302                         set {
303                                 if (value != hide_selection) {
304                                         hide_selection = value;
305                                         OnHideSelectionChanged(EventArgs.Empty);
306                                 }
307                                 if (hide_selection) {
308                                         document.selection_visible = false;
309                                 } else {
310                                         document.selection_visible = true;
311                                 }
312                                 document.InvalidateSelectionArea();
313
314                         }
315                 }
316
317                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
318                 [Editor("System.Windows.Forms.Design.StringArrayEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
319                 [Localizable(true)]
320                 [MWFCategory("Appearance")]
321                 public string[] Lines {
322                         get {
323                                 int count;
324                                 ArrayList lines;
325
326                                 count = document.Lines;
327
328                                 // Handle empty document
329                                 if ((count == 1) && (document.GetLine (1).text.Length == 0)) {
330                                         return new string [0];
331                                 }
332
333                                 lines = new ArrayList ();
334
335                                 int i = 1;
336                                 while (i <= count) {
337                                         Line line;
338                                         StringBuilder lt = new StringBuilder ();
339
340                                         do {
341                                                 line = document.GetLine (i++);
342                                                 lt.Append (line.TextWithoutEnding ());
343                                         } while (line.ending == LineEnding.Wrap && i <= count);
344
345                                         lines.Add (lt.ToString ());     
346                                 }
347
348                                 return (string []) lines.ToArray (typeof (string));
349                         }
350
351                         set {
352                                 int     i;
353                                 int     l;
354                                 SolidBrush brush;
355
356                                 document.Empty();
357
358                                 l = value.Length;
359                                 brush = ThemeEngine.Current.ResPool.GetSolidBrush(this.ForeColor);
360
361                                 document.SuspendRecalc ();
362                                 for (i = 0; i < l; i++) {
363
364                                         // Don't add the last line if it is just an empty line feed
365                                         // the line feed is reflected in the previous line's ending 
366                                         if (i == l - 1 && value [i].Length == 0)
367                                                 break;
368
369                                         LineEnding ending = LineEnding.Rich;
370                                         if (value [i].EndsWith ("\r"))
371                                                 ending = LineEnding.Hard;
372
373                                         document.Add (i + 1, CaseAdjust (value [i]), alignment, Font, brush, ending);
374                                 }
375
376                                 document.ResumeRecalc (true);
377
378                                 // CalculateDocument();
379                                 OnTextChanged(EventArgs.Empty);
380                         }
381                 }
382
383                 [DefaultValue(32767)]
384                 [Localizable(true)]
385                 [MWFCategory("Behavior")]
386                 public virtual int MaxLength {
387                         get {
388                                 if (max_length == 2147483646) { // We don't distinguish between single and multi-line limits
389                                         return 0;
390                                 }
391                                 return max_length;
392                         }
393
394                         set {
395                                 if (value != max_length) {
396                                         max_length = value;
397                                 }
398                         }
399                 }
400
401                 [Browsable(false)]
402                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
403                 public bool Modified {
404                         get {
405                                 return modified;
406                         }
407
408                         set {
409                                 if (value != modified) {
410                                         modified = value;
411                                         OnModifiedChanged(EventArgs.Empty);
412                                 }
413                         }
414                 }
415
416                 [DefaultValue(false)]
417                 [Localizable(true)]
418                 [RefreshProperties(RefreshProperties.All)]
419                 [MWFCategory("Behavior")]
420                 public virtual bool Multiline {
421                         get {
422                                 return document.multiline;
423                         }
424
425                         set {
426                                 if (value != document.multiline) {
427                                         document.multiline = value;
428                                         // Make sure we update our size; the user may have already set the size before going to multiline
429                                         if (document.multiline && requested_height != -1) {
430                                                 Height = requested_height;
431                                                 requested_height = -1;
432                                         }
433
434                                         if (Parent != null)
435                                                 Parent.PerformLayout ();
436
437                                         OnMultilineChanged(EventArgs.Empty);
438                                 }
439
440                                 if (document.multiline) {
441                                         document.Wrap = word_wrap;
442                                         document.PasswordChar = "";
443
444                                 } else {
445                                         document.Wrap = false;
446                                         if (this.password_char != '\0') {
447                                                 document.PasswordChar = password_char.ToString();
448                                         } else {
449                                                 document.PasswordChar = "";
450                                         }
451                                 }
452
453                                 if (IsHandleCreated)
454                                         CalculateDocument ();
455                         }
456                 }
457
458                 [Browsable(false)]
459                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
460                 [EditorBrowsable(EditorBrowsableState.Advanced)]
461                 public int PreferredHeight {
462                         get {
463                                 return Font.Height + (BorderStyle != BorderStyle.Fixed3D ? 0 : 7);
464                         }
465                 }
466
467                 [DefaultValue(false)]
468                 [MWFCategory("Behavior")]
469                 public bool ReadOnly {
470                         get {
471                                 return read_only;
472                         }
473
474                         set {
475                                 if (value != read_only) {
476                                         read_only = value;
477 #if NET_2_0
478                                         if (!backcolor_set) {
479                                                 if (read_only)
480                                                         background_color = SystemColors.Control;
481                                                 else
482                                                         background_color = SystemColors.Window;
483                                         }
484 #endif
485                                         OnReadOnlyChanged(EventArgs.Empty);
486                                         Invalidate ();
487                                 }
488                         }
489                 }
490
491                 [Browsable(false)]
492                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
493                 public virtual string SelectedText {
494                         get {
495                                 return document.GetSelection();
496                         }
497
498                         set {
499                                 document.ReplaceSelection(CaseAdjust(value), false);
500
501                                 ScrollToCaret();
502                                 OnTextChanged(EventArgs.Empty);
503                         }
504                 }
505
506                 [Browsable(false)]
507                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
508                 public virtual int SelectionLength {
509                         get {
510                                 int res = document.SelectionLength ();
511                                 if (res == 0)
512                                         res = -1;
513                                 return res;
514                         }
515
516                         set {
517                                 if (value < 0) {
518                                         throw new ArgumentException(String.Format("{0} is not a valid value", value), "value");
519                                 }
520
521                                 if (value != 0) {
522                                         int     start;
523                                         Line    line;
524                                         LineTag tag;
525                                         int     pos;
526
527                                         selection_length = value;
528
529                                         start = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
530
531                                         document.CharIndexToLineTag(start + value, out line, out tag, out pos);
532                                         document.SetSelectionEnd(line, pos, true);
533                                         document.PositionCaret(line, pos);
534                                 } else {
535                                         selection_length = -1;
536
537                                         document.SetSelectionEnd(document.selection_start.line, document.selection_start.pos, true);
538                                         document.PositionCaret(document.selection_start.line, document.selection_start.pos);
539                                 }
540                         }
541                 }
542
543                 [Browsable(false)]
544                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
545                 public int SelectionStart {
546                         get {
547                                 int index;
548
549                                 index = document.LineTagToCharIndex(document.selection_start.line, document.selection_start.pos);
550
551                                 return index;
552                         }
553
554                         set {
555                                 document.SetSelectionStart(value, false);
556                                 if (selection_length > -1 ) {
557                                         document.SetSelectionEnd(value + selection_length, true);
558                                 } else {
559                                         document.SetSelectionEnd(value, true);
560                                 }
561                                 document.PositionCaret(document.selection_start.line, document.selection_start.pos);
562                                 ScrollToCaret();
563                         }
564                 }
565
566 #if NET_2_0
567                 public virtual bool ShortcutsEnabled {
568                         get { return shortcuts_enabled; }
569                         set { shortcuts_enabled = value; }
570                 }
571 #endif
572
573                 [Localizable(true)]
574                 public override string Text {
575                         get {
576                                 if (document == null || document.Root == null || document.Root.text == null) {
577                                         return string.Empty;
578                                 }
579
580                                 StringBuilder sb = new StringBuilder();
581
582                                 Line line = null;
583                                 for (int i = 1; i <= document.Lines; i++) {
584                                         line = document.GetLine (i);
585                                         sb.Append(line.text.ToString ());
586                                 }
587
588                                 return sb.ToString();
589                         }
590
591                         set {
592                                 if (value == Text)
593                                         return;
594
595                                 if ((value != null) && (value != "")) {
596
597                                         document.Empty ();
598
599                                         document.Insert (document.GetLine (1), 0, false, value);
600                                                         
601                                         document.PositionCaret (document.GetLine (1), 0);
602                                         document.SetSelectionToCaret (true);
603
604                                         ScrollToCaret ();
605                                 } else {
606                                         document.Empty();
607                                         if (IsHandleCreated)
608                                                 CalculateDocument ();
609                                 }
610
611                                 // set the var so OnModifiedChanged is not raised
612                                 modified = false;
613                                 OnTextChanged(EventArgs.Empty);
614                         }
615                 }
616
617                 [Browsable(false)]
618                 public virtual int TextLength {
619                         get {
620                                 if (document == null || document.Root == null || document.Root.text == null) {
621                                         return 0;
622                                 }
623                                 return Text.Length;
624                         }
625                 }
626
627                 [DefaultValue(true)]
628                 [Localizable(true)]
629                 [MWFCategory("Behavior")]
630                 public bool WordWrap {
631                         get {
632                                 return word_wrap;
633                         }
634
635                         set {
636                                 if (value != word_wrap) {
637                                         if (document.multiline) {
638                                                 word_wrap = value;
639                                                 document.Wrap = value;
640                                         }
641                                 }
642                         }
643                 }
644                 #endregion      // Public Instance Properties
645
646                 #region Protected Instance Properties
647                 protected override CreateParams CreateParams {
648                         get {
649                                 return base.CreateParams;
650                         }
651                 }
652
653                 protected override System.Drawing.Size DefaultSize {
654                         get {
655                                 return new Size(100, 20);
656                         }
657                 }
658                 #endregion      // Protected Instance Properties
659
660                 #region Public Instance Methods
661                 public void AppendText(string text) {
662                         document.MoveCaret (CaretDirection.CtrlEnd);                            
663                         document.Insert (document.caret.line, document.caret.pos, false, text);
664                         document.MoveCaret (CaretDirection.CtrlEnd);
665                         document.SetSelectionToCaret (true);
666
667                         OnTextChanged(EventArgs.Empty);
668                 }
669
670                 public void Clear() {
671                         Text = null;
672                 }
673
674                 public void ClearUndo() {
675                         document.undo.Clear();
676                 }
677
678                 public void Copy() {
679                         DataObject      o;
680
681                         o = new DataObject(DataFormats.Text, SelectedText);
682                         if (this is RichTextBox) {
683                                 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
684                         }
685                         Clipboard.SetDataObject(o);
686                 }
687
688                 public void Cut() {
689                         DataObject      o;
690
691                         o = new DataObject(DataFormats.Text, SelectedText);
692                         if (this is RichTextBox) {
693                                 o.SetData(DataFormats.Rtf, ((RichTextBox)this).SelectedRtf);
694                         }
695                         Clipboard.SetDataObject (o);
696
697                         document.undo.BeginUserAction (Locale.GetText ("Cut"));
698                         document.ReplaceSelection (String.Empty, false);
699                         document.undo.EndUserAction ();
700                 }
701
702                 public void Paste() {
703                         Paste(Clipboard.GetDataObject(), null, false);
704                 }
705
706                 public void ScrollToCaret() {
707                         if (IsHandleCreated) {
708                                 CaretMoved(this, EventArgs.Empty);
709                         }
710                 }
711
712                 public void Select(int start, int length) {
713                         SelectionStart = start;
714                         SelectionLength = length;
715                 }
716
717
718                 public void SelectAll() {
719                         Line    last;
720
721                         last = document.GetLine(document.Lines);
722                         document.SetSelectionStart(document.GetLine(1), 0, false);
723                         document.SetSelectionEnd(last, last.text.Length, true);
724                         document.PositionCaret (document.selection_end.line, document.selection_end.pos);
725                         selection_length = -1;
726
727                         CaretMoved (this, null);
728
729                         document.DisplayCaret ();
730                 }
731
732                 /// Sync with above (except the invalidation of course)
733                 internal void SelectAllNoInvalidate ()
734                 {
735                         Line last;
736
737                         last = document.GetLine(document.Lines);
738                         document.SetSelectionStart(document.GetLine(1), 0, false);
739                         document.SetSelectionEnd(last, last.text.Length, false);
740                         document.PositionCaret (document.selection_end.line, document.selection_end.pos);
741                         selection_length = -1;
742                 }
743
744                 public override string ToString() {
745                         return String.Concat (base.ToString (), ", Text: ", Text);
746                 }
747
748                 public void Undo() {
749                         document.undo.Undo();
750                 }
751                 #endregion      // Public Instance Methods
752
753                 #region Protected Instance Methods
754                 protected override void CreateHandle() {
755                         CalculateDocument ();
756                         base.CreateHandle ();
757                         document.AlignCaret();
758                         ScrollToCaret();
759                 }
760
761                 protected override bool IsInputKey(Keys keyData) {
762                         if ((keyData & Keys.Alt) != 0) {
763                                 return base.IsInputKey(keyData);
764                         }
765
766                         switch (keyData & Keys.KeyCode) {
767                                 case Keys.Enter: {
768                                         if (accepts_return) {
769                                                 return true;
770                                         }
771                                         return false;
772                                 }
773
774                                 case Keys.Tab: {
775                                         if (accepts_tab && document.multiline) {
776                                                 if ((keyData & Keys.Control) == 0) {
777                                                         return true;
778                                                 }
779                                         }
780                                         return false;
781                                 }
782
783                                 case Keys.Left:
784                                 case Keys.Right:
785                                 case Keys.Up:
786                                 case Keys.Down:
787                                 case Keys.PageUp:
788                                 case Keys.PageDown:
789                                 case Keys.Home:
790                                 case Keys.End: {
791                                         return true;
792                                 }
793                         }
794                         return false;
795                 }
796
797
798                 protected virtual void OnAcceptsTabChanged(EventArgs e) {
799                         EventHandler eh = (EventHandler)(Events [AcceptsTabChangedEvent]);
800                         if (eh != null)
801                                 eh (this, e);
802                 }
803
804                 protected virtual void OnAutoSizeChanged(EventArgs e) {
805                         EventHandler eh = (EventHandler)(Events [AutoSizeChangedEvent]);
806                         if (eh != null)
807                                 eh (this, e);
808                 }
809
810                 protected virtual void OnBorderStyleChanged(EventArgs e) {
811                         EventHandler eh = (EventHandler)(Events [BorderStyleChangedEvent]);
812                         if (eh != null)
813                                 eh (this, e);
814                 }
815
816                 protected override void OnFontChanged(EventArgs e) {
817                         base.OnFontChanged (e);
818
819                         if (auto_size && !document.multiline) {
820                                 if (PreferredHeight != ClientSize.Height) {
821                                         Height = PreferredHeight;
822                                 }
823                         }
824                 }
825
826                 protected override void OnHandleCreated(EventArgs e) {
827                         base.OnHandleCreated (e);
828                 }
829
830                 protected override void OnHandleDestroyed(EventArgs e) {
831                         base.OnHandleDestroyed (e);
832                 }
833
834                 protected virtual void OnHideSelectionChanged(EventArgs e) {
835                         EventHandler eh = (EventHandler)(Events [HideSelectionChangedEvent]);
836                         if (eh != null)
837                                 eh (this, e);
838                 }
839
840                 protected virtual void OnModifiedChanged(EventArgs e) {
841                         EventHandler eh = (EventHandler)(Events [ModifiedChangedEvent]);
842                         if (eh != null)
843                                 eh (this, e);
844                 }
845
846                 protected virtual void OnMultilineChanged(EventArgs e) {
847                         EventHandler eh = (EventHandler)(Events [MultilineChangedEvent]);
848                         if (eh != null)
849                                 eh (this, e);
850                 }
851
852                 protected virtual void OnReadOnlyChanged(EventArgs e) {
853                         EventHandler eh = (EventHandler)(Events [ReadOnlyChangedEvent]);
854                         if (eh != null)
855                                 eh (this, e);
856                 }
857
858                 protected override bool ProcessDialogKey(Keys keyData) {
859                         return base.ProcessDialogKey(keyData);
860                 }
861
862                 private bool ProcessKey(Keys keyData) {
863                         bool control;
864                         bool shift;
865
866                         control = (Control.ModifierKeys & Keys.Control) != 0;
867                         shift = (Control.ModifierKeys & Keys.Shift) != 0;
868
869                         if (shortcuts_enabled) {
870                                 switch (keyData & Keys.KeyCode) {
871                                 case Keys.X:
872                                         if (control) {
873                                                 Cut();
874                                                 return true;
875                                         }
876                                         return false;
877
878                                 case Keys.C:
879                                         if (control) {
880                                                 Copy();
881                                                 return true;
882                                         }
883                                         return false;
884
885                                 case Keys.V:
886                                         if (control) {
887                                                 return Paste(Clipboard.GetDataObject(), null, true);
888                                         }
889                                         return false;
890
891                                 case Keys.Z:
892                                         if (control) {
893                                                 Undo();
894                                                 return true;
895                                         }
896                                         return false;
897
898                                 case Keys.A:
899                                         if (control) {
900                                                 SelectAll();
901                                                 return true;
902                                         }
903                                         return false;
904
905                                 case Keys.Insert:
906                                         if (shift) {
907                                                 Paste(Clipboard.GetDataObject(), null, true);
908                                                 return true;
909                                         }
910
911                                         if (control) {
912                                                 Copy();
913                                                 return true;
914                                         }
915
916                                         return false;
917
918                                 case Keys.Delete:
919                                         if (shift) {
920                                                 Cut();
921                                                 return true;
922                                         }
923
924                                         if (read_only)
925                                                 break;
926
927                                         if (document.selection_visible) {
928                                                 document.ReplaceSelection("", false);
929                                         } else {
930                                                 // DeleteChar only deletes on the line, doesn't do the combine
931                                                 if (document.CaretPosition == document.CaretLine.Text.Length) {
932                                                         if (document.CaretLine.LineNo < document.Lines) {
933                                                                 Line    line;
934
935                                                                 line = document.GetLine(document.CaretLine.LineNo + 1);
936                                                                 document.Combine(document.CaretLine, line);
937                                                                 document.UpdateView(document.CaretLine, 2, 0);
938
939                                                                 #if not_Debug
940                                                                 Line    check_first;
941                                                                 Line    check_second;
942
943                                                                 check_first = document.GetLine(document.CaretLine.LineNo);
944                                                                 check_second = document.GetLine(check_first.line_no + 1);
945
946                                                                 Console.WriteLine("Post-UpdateView: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
947                                                                 #endif
948
949                                                                 // Caret doesn't move
950                                                         }
951                                                 } else {
952                                                         if (!control) {
953                                                                 document.DeleteChar(document.CaretTag, document.CaretPosition, true);
954                                                         } else {
955                                                                 int end_pos;
956
957                                                                 end_pos = document.CaretPosition;
958
959                                                                 while ((end_pos < document.CaretLine.Text.Length) && !Document.IsWordSeparator(document.CaretLine.Text[end_pos])) {
960                                                                         end_pos++;
961                                                                 }
962
963                                                                 if (end_pos < document.CaretLine.Text.Length) {
964                                                                         end_pos++;
965                                                                 }
966                                                                 document.DeleteChars(document.CaretTag, document.CaretPosition, end_pos - document.CaretPosition);
967                                                         }
968                                                 }
969                                         }
970
971                                         OnTextChanged(EventArgs.Empty);
972                                         document.AlignCaret();
973                                         document.UpdateCaret();
974                                         CaretMoved(this, null);
975                                         return true;
976                                 }
977                         }
978
979                         switch (keyData & Keys.KeyCode) {
980                                 case Keys.Left: {
981                                         if (control) {
982                                                 document.MoveCaret(CaretDirection.WordBack);
983                                         } else {
984                                                 if (!document.selection_visible || shift) {
985                                                         document.MoveCaret(CaretDirection.CharBack);
986                                                 } else {
987                                                         document.MoveCaret(CaretDirection.SelectionStart);
988                                                 }
989                                         }
990                                         
991                                         if (!shift) {
992                                                 document.SetSelectionToCaret(true);
993                                         } else {
994                                                 document.SetSelectionToCaret(false);
995                                         }
996
997                                         CaretMoved(this, null);
998                                         return true;
999                                 }
1000
1001                                 case Keys.Right: {
1002                                         if (control) {
1003                                                 document.MoveCaret(CaretDirection.WordForward);
1004                                         } else {
1005                                                 if (!document.selection_visible || shift) {
1006                                                         document.MoveCaret(CaretDirection.CharForward);
1007                                                 } else {
1008                                                         document.MoveCaret(CaretDirection.SelectionEnd);
1009                                                 }
1010                                         }
1011                                         if (!shift) {
1012                                                 document.SetSelectionToCaret(true);
1013                                         } else {
1014                                                 document.SetSelectionToCaret(false);
1015                                         }
1016
1017                                         CaretMoved(this, null);
1018                                         return true;
1019                                 }
1020
1021                                 case Keys.Up: {
1022                                         if (control) {
1023                                                 if (document.CaretPosition == 0) {
1024                                                         document.MoveCaret(CaretDirection.LineUp);
1025                                                 } else {
1026                                                         document.MoveCaret(CaretDirection.Home);
1027                                                 }
1028                                         } else {
1029                                                 document.MoveCaret(CaretDirection.LineUp);
1030                                         }
1031
1032                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
1033                                                 document.SetSelectionToCaret(true);
1034                                         } else {
1035                                                 document.SetSelectionToCaret(false);
1036                                         }
1037
1038                                         CaretMoved(this, null);
1039                                         return true;
1040                                 }
1041
1042                                 case Keys.Down: {
1043                                         if (control) {
1044                                                 if (document.CaretPosition == document.CaretLine.Text.Length) {
1045                                                         document.MoveCaret(CaretDirection.LineDown);
1046                                                 } else {
1047                                                         document.MoveCaret(CaretDirection.End);
1048                                                 }
1049                                         } else {
1050                                                 document.MoveCaret(CaretDirection.LineDown);
1051                                         }
1052
1053                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
1054                                                 document.SetSelectionToCaret(true);
1055                                         } else {
1056                                                 document.SetSelectionToCaret(false);
1057                                         }
1058
1059                                         CaretMoved(this, null);
1060                                         return true;
1061                                 }
1062
1063                                 case Keys.Home: {
1064                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
1065                                                 document.MoveCaret(CaretDirection.CtrlHome);
1066                                         } else {
1067                                                 document.MoveCaret(CaretDirection.Home);
1068                                         }
1069
1070                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
1071                                                 document.SetSelectionToCaret(true);
1072                                         } else {
1073                                                 document.SetSelectionToCaret(false);
1074                                         }
1075
1076                                         CaretMoved(this, null);
1077                                         return true;
1078                                 }
1079
1080                                 case Keys.End: {
1081                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
1082                                                 document.MoveCaret(CaretDirection.CtrlEnd);
1083                                         } else {
1084                                                 document.MoveCaret(CaretDirection.End);
1085                                         }
1086
1087                                         if ((Control.ModifierKeys & Keys.Shift) == 0) {
1088                                                 document.SetSelectionToCaret(true);
1089                                         } else {
1090                                                 document.SetSelectionToCaret(false);
1091                                         }
1092
1093                                         CaretMoved(this, null);
1094                                         return true;
1095                                 }
1096
1097                                 case Keys.Enter: {
1098                                         // ignoring accepts_return, fixes bug #76355
1099                                         if (!read_only && (accepts_return || (FindForm() != null && FindForm().AcceptButton == null) || ((Control.ModifierKeys & Keys.Control) != 0))) {
1100                                                 Line    line;
1101
1102                                                 if (document.selection_visible) {
1103                                                         document.ReplaceSelection("", false);
1104                                                 }
1105
1106                                                 line = document.CaretLine;
1107
1108                                                 document.Split (document.CaretLine, document.CaretTag, document.CaretPosition);
1109                                                 line.ending = LineEnding.Rich;
1110                                                 OnTextChanged(EventArgs.Empty);
1111                                                 document.UpdateView(line, 2, 0);
1112
1113                                                 CaretMoved(this, null);
1114                                                 return true;
1115                                         }
1116                                         break;
1117                                 }
1118
1119                                 case Keys.Tab: {
1120                                         if (!read_only && accepts_tab && document.multiline) {
1121                                                 document.InsertChar(document.CaretLine, document.CaretPosition, '\t');
1122                                                 if (document.selection_visible) {
1123                                                         document.ReplaceSelection("", false);
1124                                                 }
1125                                                 document.SetSelectionToCaret(true);
1126
1127                                                 OnTextChanged(EventArgs.Empty);
1128                                                 CaretMoved(this, null);
1129                                                 return true;
1130                                         }
1131                                         break;
1132                                 }
1133
1134                                 case Keys.PageUp: {
1135                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
1136                                                 document.MoveCaret(CaretDirection.CtrlPgUp);
1137                                         } else {
1138                                                 document.MoveCaret(CaretDirection.PgUp);
1139                                         }
1140                                         return true;
1141                                 }
1142
1143                                 case Keys.PageDown: {
1144                                         if ((Control.ModifierKeys & Keys.Control) != 0) {
1145                                                 document.MoveCaret(CaretDirection.CtrlPgDn);
1146                                         } else {
1147                                                 document.MoveCaret(CaretDirection.PgDn);
1148                                         }
1149                                         return true;
1150                                 }
1151                         }
1152
1153                         return false;
1154                 }
1155
1156                 private void HandleBackspace(bool control) {
1157                         bool    fire_changed;
1158
1159                         fire_changed = false;
1160
1161                         // delete only deletes on the line, doesn't do the combine
1162                         if (document.selection_visible) {
1163                                 document.undo.BeginUserAction (Locale.GetText ("Delete"));
1164                                 document.ReplaceSelection("", false);
1165                                 document.undo.EndUserAction ();
1166                                 fire_changed = true;
1167                         }
1168                         document.SetSelectionToCaret(true);
1169
1170                         if (document.CaretPosition == 0) {
1171                                 if (document.CaretLine.LineNo > 1) {
1172                                         Line    line;
1173                                         int     new_caret_pos;
1174
1175                                         line = document.GetLine(document.CaretLine.LineNo - 1);
1176                                         new_caret_pos = line.text.Length;
1177
1178                                         document.Combine(line, document.CaretLine);
1179                                         document.UpdateView(line, 1, 0);
1180                                         document.PositionCaret(line, new_caret_pos);
1181                                         document.SetSelectionToCaret (true);
1182                                         document.UpdateCaret();
1183                                         fire_changed = true;
1184                                 }
1185                         } else {
1186                                 if (!control || document.CaretPosition == 0) {
1187
1188                                         // Move before we delete because the delete will change positions around
1189                                         // if we cross a wrap border
1190                                         LineTag tag = document.CaretTag;
1191                                         int pos = document.CaretPosition;
1192                                         document.MoveCaret (CaretDirection.CharBack);
1193                                         document.DeleteChar (tag, pos, false);
1194                                         document.SetSelectionToCaret (true);
1195                                 } else {
1196                                         int start_pos;
1197
1198                                         
1199                                         start_pos = document.CaretPosition - 1;
1200                                         while ((start_pos > 0) && !Document.IsWordSeparator(document.CaretLine.Text[start_pos - 1])) {
1201                                                 start_pos--;
1202                                         }
1203
1204                                         document.undo.BeginUserAction (Locale.GetText ("Delete"));
1205                                         document.DeleteChars(document.CaretTag, start_pos, document.CaretPosition - start_pos);
1206                                         document.undo.EndUserAction ();
1207                                         document.PositionCaret(document.CaretLine, start_pos);
1208                                         document.SetSelectionToCaret (true);
1209                                 }
1210                                 document.UpdateCaret();
1211                                 fire_changed = true;
1212                         }
1213
1214                         if (fire_changed) {
1215                                 OnTextChanged(EventArgs.Empty);
1216                         }
1217                         CaretMoved(this, null);
1218                 }
1219
1220                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
1221                         // Make sure we don't get sized bigger than we want to be
1222                         if (!richtext) {
1223                                 if (!document.multiline) {
1224                                         if (height != PreferredHeight) {
1225                                                 requested_height = height;
1226                                                 height = PreferredHeight;
1227                                                 specified |= BoundsSpecified.Height;
1228                                         }
1229                                 }
1230                         }
1231
1232                         base.SetBoundsCore (x, y, width, height, specified);
1233                 }
1234
1235                 protected override void WndProc(ref Message m) {
1236                         switch ((Msg)m.Msg) {
1237                         case Msg.WM_KEYDOWN: {
1238                                 if (ProcessKeyMessage(ref m) || ProcessKey((Keys)m.WParam.ToInt32() | XplatUI.State.ModifierKeys)) {
1239                                         m.Result = IntPtr.Zero;
1240                                         return;
1241                                 }
1242                                 DefWndProc (ref m);
1243                                 return;
1244                         }
1245
1246                         case Msg.WM_CHAR: {
1247                                 int     ch;
1248
1249                                 if (ProcessKeyMessage(ref m)) {
1250                                         m.Result = IntPtr.Zero;
1251                                         return;
1252                                 }
1253
1254                                 if (read_only) {
1255                                         return;
1256                                 }
1257
1258                                 m.Result = IntPtr.Zero;
1259
1260                                 ch = m.WParam.ToInt32();
1261
1262                                 if (ch == 127) {
1263                                         HandleBackspace(true);
1264                                 } else if (ch >= 32) {
1265                                         if (document.selection_visible) {
1266                                                 document.ReplaceSelection("", false);
1267                                         }
1268
1269                                         char c = (char)m.WParam;
1270                                         switch (character_casing) {
1271                                         case CharacterCasing.Upper:
1272                                                 c = Char.ToUpper((char) m.WParam);
1273                                                 break;
1274                                         case CharacterCasing.Lower:
1275                                                 c = Char.ToLower((char) m.WParam);
1276                                                 break;
1277                                         }
1278
1279                                         if (document.Length < max_length) {
1280                                                 document.InsertCharAtCaret(c, true);
1281 #if NET_2_0
1282                                                 OnTextUpdate ();
1283 #endif
1284                                                 OnTextChanged(EventArgs.Empty);
1285                                                 CaretMoved(this, null);
1286                                         } else {
1287                                                 XplatUI.AudibleAlert();
1288                                         }
1289                                         return;
1290                                 } else if (ch == 8) {
1291                                         HandleBackspace(false);
1292                                 }
1293
1294                                 return;
1295                         }
1296
1297                         case Msg.WM_SETFOCUS:
1298                                 base.WndProc(ref m);
1299                                 document.CaretHasFocus ();
1300                                 break;
1301
1302                         case Msg.WM_KILLFOCUS:
1303                                 base.WndProc(ref m);
1304                                 document.CaretLostFocus ();
1305                                 break;
1306
1307                         default:
1308                                 base.WndProc(ref m);
1309                                 return;
1310                         }
1311                 }
1312
1313                 #endregion      // Protected Instance Methods
1314
1315                 #region Events
1316                 static object AcceptsTabChangedEvent = new object ();
1317                 static object AutoSizeChangedEvent = new object ();
1318                 static object BorderStyleChangedEvent = new object ();
1319                 static object HideSelectionChangedEvent = new object ();
1320                 static object ModifiedChangedEvent = new object ();
1321                 static object MultilineChangedEvent = new object ();
1322                 static object ReadOnlyChangedEvent = new object ();
1323                 static object HScrolledEvent = new object ();
1324                 static object VScrolledEvent = new object ();
1325
1326                 public event EventHandler AcceptsTabChanged {
1327                         add { Events.AddHandler (AcceptsTabChangedEvent, value); }
1328                         remove { Events.RemoveHandler (AcceptsTabChangedEvent, value); }
1329                 }
1330
1331                 public new event EventHandler AutoSizeChanged {
1332                         add { Events.AddHandler (AutoSizeChangedEvent, value); }
1333                         remove { Events.RemoveHandler (AutoSizeChangedEvent, value); }
1334                 }
1335
1336                 public event EventHandler BorderStyleChanged {
1337                         add { Events.AddHandler (BorderStyleChangedEvent, value); }
1338                         remove { Events.RemoveHandler (BorderStyleChangedEvent, value); }
1339                 }
1340
1341                 public event EventHandler HideSelectionChanged {
1342                         add { Events.AddHandler (HideSelectionChangedEvent, value); }
1343                         remove { Events.RemoveHandler (HideSelectionChangedEvent, value); }
1344                 }
1345
1346                 public event EventHandler ModifiedChanged {
1347                         add { Events.AddHandler (ModifiedChangedEvent, value); }
1348                         remove { Events.RemoveHandler (ModifiedChangedEvent, value); }
1349                 }
1350
1351                 public event EventHandler MultilineChanged {
1352                         add { Events.AddHandler (MultilineChangedEvent, value); }
1353                         remove { Events.RemoveHandler (MultilineChangedEvent, value); }
1354                 }
1355
1356                 public event EventHandler ReadOnlyChanged {
1357                         add { Events.AddHandler (ReadOnlyChangedEvent, value); }
1358                         remove { Events.RemoveHandler (ReadOnlyChangedEvent, value); }
1359                 }
1360
1361                 internal event EventHandler HScrolled {
1362                         add { Events.AddHandler (HScrolledEvent, value); }
1363                         remove { Events.RemoveHandler (HScrolledEvent, value); }
1364                 }
1365
1366                 internal event EventHandler VScrolled {
1367                         add { Events.AddHandler (VScrolledEvent, value); }
1368                         remove { Events.RemoveHandler (VScrolledEvent, value); }
1369                 }
1370
1371                 [Browsable(false)]
1372                 [EditorBrowsable(EditorBrowsableState.Never)]
1373                 public new event EventHandler BackgroundImageChanged {
1374                         add { base.BackgroundImageChanged += value; }
1375                         remove { base.BackgroundImageChanged -= value; }
1376                 }
1377                 [Browsable(false)]
1378                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1379                 public new event EventHandler Click {
1380                         add { base.Click += value; }
1381                         remove { base.Click -= value; }
1382                 }
1383
1384                 // XXX should this not manipulate base.Paint?
1385                 [Browsable(false)]
1386                 [EditorBrowsable(EditorBrowsableState.Never)]
1387                 public new event PaintEventHandler Paint;
1388                 #endregion      // Events
1389
1390                 #region Private Methods
1391                 internal Document Document {
1392                         get {
1393                                 return document;
1394                         }
1395
1396                         set {
1397                                 document = value;
1398                         }
1399                 }
1400
1401                 internal bool ShowSelection {
1402                         get {
1403                                 if (show_selection || !hide_selection) {
1404                                         return true;
1405                                 }
1406
1407                                 return has_focus;
1408                         }
1409
1410                         set {
1411                                 if (show_selection == value)
1412                                         return;
1413
1414                                 show_selection = value;
1415                                 // Currently InvalidateSelectionArea is commented out so do a full invalidate
1416                                 document.InvalidateSelectionArea();
1417                         }
1418                 }
1419
1420                 internal Graphics CreateGraphicsInternal() {
1421                         if (IsHandleCreated) {
1422                                 return base.CreateGraphics();
1423                         }
1424
1425                         return Graphics.FromImage(bmp);
1426                 }
1427
1428                 #if Debug
1429                 static int current;
1430                 #endif
1431
1432                 internal override void OnPaintInternal (PaintEventArgs pevent) {
1433                         // Fill background
1434                         if (backcolor_set || (Enabled && !read_only)) {
1435                                 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), pevent.ClipRectangle);
1436                         } else {
1437                                 pevent.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorControl), pevent.ClipRectangle);
1438                         }
1439                         
1440                         // Draw the viewable document
1441                         document.Draw(pevent.Graphics, pevent.ClipRectangle);
1442
1443                         //
1444                         // OnPaint does not get raised on MS (see bug #80639)
1445                         // 
1446                         pevent.Handled = true;
1447                 }
1448
1449                 private bool IsDoubleClick (MouseEventArgs e)
1450                 {
1451                         TimeSpan interval = DateTime.Now - click_last;
1452                         if (interval.TotalMilliseconds > SystemInformation.DoubleClickTime)
1453                                 return false;
1454                         Size dcs = SystemInformation.DoubleClickSize;
1455                         if (e.X < click_point_x - dcs.Width / 2 || e.X > click_point_x + dcs.Width / 2)
1456                                 return false;
1457                         if (e.Y < click_point_y - dcs.Height / 2 || e.Y > click_point_y + dcs.Height / 2)
1458                                 return false;
1459                         return true;
1460                 }
1461
1462                 private void TextBoxBase_MouseDown (object sender, MouseEventArgs e)
1463                 {
1464                         if (e.Button == MouseButtons.Left) {
1465
1466                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1467
1468                                 if (IsDoubleClick (e)) {
1469                                         switch (click_mode) {
1470                                         case CaretSelection.Position:
1471                                                 SelectWord ();
1472                                                 click_mode = CaretSelection.Word;
1473                                                 break;
1474                                         case CaretSelection.Word:
1475
1476                                                 if (this is TextBox) {
1477                                                         document.SetSelectionToCaret (true);
1478                                                         click_mode = CaretSelection.Position;
1479                                                         break;
1480                                                 }
1481
1482                                                 document.ExpandSelection (CaretSelection.Line, false);
1483                                                 click_mode = CaretSelection.Line;
1484                                                 break;
1485                                         case CaretSelection.Line:
1486
1487                                                 // Gotta do this first because Exanding to a word
1488                                                 // from a line doesn't really work
1489                                                 document.SetSelectionToCaret (true);
1490
1491                                                 SelectWord ();
1492                                                 click_mode = CaretSelection.Word;
1493                                                 break;
1494                                         }
1495                                 } else {
1496                                         document.SetSelectionToCaret (true);
1497                                         click_mode = CaretSelection.Position;
1498                                 }
1499
1500                                 click_point_x = e.X;
1501                                 click_point_y = e.Y;
1502                                 click_last = DateTime.Now;
1503                         }
1504
1505                         if ((e.Button == MouseButtons.Middle) && (((int)Environment.OSVersion.Platform == 4) || ((int)Environment.OSVersion.Platform == 128))) {
1506                                 Document.Marker marker;
1507
1508                                 marker.tag = document.FindCursor(e.X + document.ViewPortX, e.Y + document.ViewPortY, out marker.pos);
1509                                 marker.line = marker.tag.line;
1510                                 marker.height = marker.tag.height;
1511
1512                                 document.SetSelection(marker.line, marker.pos, marker.line, marker.pos);
1513                                 Paste (Clipboard.GetDataObject (true), null, true);
1514
1515                         }
1516                 }
1517
1518                 private void TextBoxBase_MouseUp(object sender, MouseEventArgs e) {
1519                         if (e.Button == MouseButtons.Left) {
1520                                 if (click_mode == CaretSelection.Position) {
1521                                         document.SetSelectionToCaret(false);
1522                                         document.DisplayCaret();
1523                                 }
1524
1525                                 if (scroll_timer != null) {
1526                                         scroll_timer.Enabled = false;
1527                                 }
1528                                 return;
1529                         }
1530                 }
1531
1532                 private void PositionControls ()
1533                 {
1534                         if (hscroll.Visible) {
1535                                 //vscroll.Maximum += hscroll.Height;
1536                                 canvas_height = ClientSize.Height - hscroll.Height;
1537                         } else {
1538                                 canvas_height = ClientSize.Height;
1539                         }
1540
1541                         if (vscroll.Visible) {
1542                                 //hscroll.Maximum += vscroll.Width;
1543                                 canvas_width = ClientSize.Width - vscroll.Width;
1544                         } else {
1545                                 canvas_width = ClientSize.Width;
1546                         }
1547
1548
1549                         document.ViewPortWidth = canvas_width;
1550                         document.ViewPortHeight = canvas_height;
1551
1552                         if (canvas_height < 1 || canvas_width < 1)
1553                                 return;
1554
1555                         // We always move them, they just might not be displayed
1556                         hscroll.Bounds = new Rectangle (ClientRectangle.Left,
1557                                         Math.Max (0, ClientRectangle.Height - hscroll.Height),
1558                                         Math.Max (0, ClientSize.Width - (vscroll.Visible ? vscroll.Width : 0)),
1559                                         hscroll.Height);
1560
1561                         vscroll.Bounds = new Rectangle (Math.Max (0, ClientRectangle.Right - vscroll.Width),
1562                                         ClientRectangle.Top, vscroll.Width,
1563                                         Math.Max (0, ClientSize.Height - (hscroll.Visible ? hscroll.Height : 0)));
1564                         
1565                 }
1566
1567                 private void TextBoxBase_SizeChanged(object sender, EventArgs e) {
1568                         if (IsHandleCreated)
1569                                 CalculateDocument ();
1570                 }
1571
1572                 private void TextBoxBase_MouseWheel(object sender, MouseEventArgs e) {
1573
1574                         if (!vscroll.Enabled) {
1575                                 return;
1576                         }
1577
1578                         if (e.Delta < 0)
1579                                 vscroll.Value = Math.Min (vscroll.Value + SystemInformation.MouseWheelScrollLines,
1580                                                 Math.Max (0, vscroll.Maximum - document.ViewPortHeight + 1));
1581                         else
1582                                 vscroll.Value = Math.Max (0, vscroll.Value - SystemInformation.MouseWheelScrollLines);
1583                 }
1584
1585                 internal virtual void SelectWord ()
1586                 {
1587                         StringBuilder s = document.caret.line.text;
1588                         int start = document.caret.pos;
1589                         int end = document.caret.pos;
1590
1591                         if (s.Length < 1) {
1592                                 if (document.caret.line.line_no >= document.Lines)
1593                                         return;
1594                                 Line line = document.GetLine (document.caret.line.line_no + 1);
1595                                 document.PositionCaret (line, 0);
1596                                 return;
1597                         }
1598
1599                         if (start > 0) {
1600                                 start--;
1601                                 end--;
1602                         }
1603
1604                         // skip whitespace until we hit a word
1605                         while (start > 0 && s [start] == ' ')
1606                                 start--;
1607                         if (start > 0) {
1608                                 while (start > 0 && (s [start] != ' '))
1609                                         start--;
1610                                 if (s [start] == ' ')
1611                                         start++;
1612                         }
1613
1614                         if (s [end] == ' ') {
1615                                 while (end < s.Length && s [end] == ' ')
1616                                         end++;
1617                         } else {
1618                                 while (end < s.Length && s [end] != ' ')
1619                                         end++;
1620                                 while (end < s.Length && s [end] == ' ')
1621                                         end++;
1622                         }
1623
1624                         document.SetSelection (document.caret.line, start, document.caret.line, end);
1625                         document.PositionCaret (document.selection_end.line, document.selection_end.pos);
1626                         document.DisplayCaret();
1627                 }
1628
1629                 internal void CalculateDocument() {
1630
1631                         document.RecalculateDocument(CreateGraphicsInternal());
1632                         CalculateScrollBars();
1633
1634                          if (document.caret.line != null && document.caret.line.Y < document.ViewPortHeight) {
1635                                 // The window has probably been resized, making the entire thing visible, so
1636                                 // we need to set the scroll position back to zero.
1637                                 vscroll.Value = 0;
1638                          }
1639
1640                          Invalidate();
1641                 }
1642
1643                 internal void CalculateScrollBars () {
1644                         // FIXME - need separate calculations for center and right alignment
1645
1646                         if (!document.multiline) {
1647                                 PositionControls ();
1648                                 return;
1649                         }
1650
1651                         if (document.Width >= document.ViewPortWidth) {
1652                                 hscroll.SetValues (0, Math.Max (1, document.Width), -1,
1653                                                 document.ViewPortWidth < 0 ? 0 : document.ViewPortWidth);
1654                                 hscroll.Enabled = true;
1655                         } else {
1656                                 hscroll.Enabled = false;
1657                                 hscroll.Maximum = document.ViewPortWidth;
1658                         }
1659
1660                         if (document.Height >= document.ViewPortHeight) {
1661                                 vscroll.SetValues (0, Math.Max (1, document.Height), -1,
1662                                                 document.ViewPortHeight < 0 ? 0 : document.ViewPortHeight);
1663                                 vscroll.Enabled = true;
1664                         } else {
1665                                 vscroll.Enabled = false;
1666                                 vscroll.Maximum = document.ViewPortHeight;
1667                         }
1668
1669
1670                         if (!WordWrap) {
1671                                 if ((scrollbars & RichTextBoxScrollBars.Horizontal) != 0) {
1672                                         if (((scrollbars & RichTextBoxScrollBars.ForcedHorizontal) != 0) || hscroll.Enabled) {
1673                                                 hscroll.Visible = true;
1674                                         } else {
1675                                                 hscroll.Visible = false;
1676                                         }
1677                                 } else {
1678                                         hscroll.Visible = false;
1679                                 }
1680                         }
1681
1682                         if ((scrollbars & RichTextBoxScrollBars.Vertical) != 0) {
1683                                 if (((scrollbars & RichTextBoxScrollBars.ForcedVertical) != 0) || vscroll.Enabled) {
1684                                         vscroll.Visible = true;
1685                                 } else {
1686                                         vscroll.Visible = false;
1687                                 }
1688                         } else {
1689                                 vscroll.Visible = false;
1690                         }
1691
1692                         PositionControls ();
1693                 }
1694
1695                 private void document_WidthChanged(object sender, EventArgs e) {
1696                         CalculateScrollBars();
1697                 }
1698
1699                 private void document_HeightChanged(object sender, EventArgs e) {
1700                         CalculateScrollBars();
1701                 }
1702
1703                 private void hscroll_ValueChanged(object sender, EventArgs e) {
1704                         int old_viewport_x;
1705
1706                         old_viewport_x = document.ViewPortX;
1707                         document.ViewPortX = this.hscroll.Value;
1708
1709                         //
1710                         // Before scrolling we want to destroy the caret, then draw a new one after the scroll
1711                         // the reason for this is that scrolling changes the coordinates of the caret, and we
1712                         // will get tracers if we don't 
1713                         //
1714                         if (Focused)
1715                                 document.CaretLostFocus ();
1716
1717                         if (vscroll.Visible) {
1718                                 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width - vscroll.Width, ClientSize.Height), old_viewport_x - this.hscroll.Value, 0, false);
1719                         } else {
1720                                 XplatUI.ScrollWindow(this.Handle, ClientRectangle, old_viewport_x - this.hscroll.Value, 0, false);
1721                         }
1722
1723                         if (Focused)
1724                                 document.CaretHasFocus ();
1725
1726                         EventHandler eh = (EventHandler)(Events [HScrolledEvent]);
1727                         if (eh != null)
1728                                 eh (this, EventArgs.Empty);
1729                 }
1730
1731                 private void vscroll_ValueChanged(object sender, EventArgs e) {
1732                         int old_viewport_y;
1733
1734                         old_viewport_y = document.ViewPortY;
1735                         document.ViewPortY = this.vscroll.Value;
1736
1737                         //
1738                         // Before scrolling we want to destroy the caret, then draw a new one after the scroll
1739                         // the reason for this is that scrolling changes the coordinates of the caret, and we
1740                         // will get tracers if we don't 
1741                         //
1742                         if (Focused)
1743                                 document.CaretLostFocus ();
1744
1745                         if (hscroll.Visible) {
1746                                 XplatUI.ScrollWindow(this.Handle, new Rectangle(0, 0, ClientSize.Width, ClientSize.Height - hscroll.Height), 0, old_viewport_y - this.vscroll.Value, false);
1747                         } else {
1748                                 XplatUI.ScrollWindow(this.Handle, ClientRectangle, 0, old_viewport_y - this.vscroll.Value, false);
1749                         }
1750
1751                         if (Focused)
1752                                 document.CaretHasFocus ();
1753
1754                         EventHandler eh = (EventHandler)(Events [VScrolledEvent]);
1755                         if (eh != null)
1756                                 eh (this, EventArgs.Empty);
1757                 }
1758
1759                 private void TextBoxBase_MouseMove(object sender, MouseEventArgs e) {
1760                         // FIXME - handle auto-scrolling if mouse is to the right/left of the window
1761                         if (e.Button == MouseButtons.Left && Capture) {
1762                                 if (!ClientRectangle.Contains (e.X, e.Y)) {
1763                                         if (scroll_timer == null) {
1764                                                 scroll_timer = new Timer ();
1765                                                 scroll_timer.Interval = 100;
1766                                                 scroll_timer.Tick += new EventHandler (ScrollTimerTickHandler);
1767                                         }
1768
1769                                         if (!scroll_timer.Enabled) {
1770                                                 scroll_timer.Start ();
1771
1772                                                 // Force the first tick
1773                                                 ScrollTimerTickHandler (null, EventArgs.Empty);
1774                                         }
1775                                 }
1776
1777                                 document.PositionCaret(e.X + document.ViewPortX, e.Y + document.ViewPortY);
1778                                 if (click_mode == CaretSelection.Position) {
1779                                         document.SetSelectionToCaret(false);
1780                                         document.DisplayCaret();
1781                                 }
1782                         }
1783                 }
1784                                                                               
1785                 private void TextBoxBase_FontOrColorChanged(object sender, EventArgs e) {
1786                         if (!richtext) {
1787                                 Line    line;
1788
1789                                 document.SuspendRecalc ();
1790                                 // Font changes apply to the whole document
1791                                 for (int i = 1; i <= document.Lines; i++) {
1792                                         line = document.GetLine(i);
1793                                         LineTag.FormatText(line, 1, line.text.Length, Font,
1794                                                         ThemeEngine.Current.ResPool.GetSolidBrush(ForeColor),
1795                                                         null, FormatSpecified.Font | FormatSpecified.Color);
1796                                 }
1797                                 document.ResumeRecalc (false);
1798
1799                                 // Make sure the caret height is matching the new font height
1800                                 document.AlignCaret();
1801                         }
1802                 }
1803
1804                 private void ScrollTimerTickHandler (object sender, EventArgs e)
1805                 {
1806                         Point pt = Cursor.Position;
1807
1808                         pt = PointToClient (pt);
1809
1810                         if (pt.X < ClientRectangle.Left) {
1811                                 document.MoveCaret(CaretDirection.CharBackNoWrap);
1812                                 document.SetSelectionToCaret(false);
1813                                 
1814                                 CaretMoved(this, null);
1815                         } else if (pt.X > ClientRectangle.Right) {
1816                                 document.MoveCaret(CaretDirection.CharForwardNoWrap);
1817                                 document.SetSelectionToCaret(false);
1818                                 
1819                                 CaretMoved(this, null);
1820                         } else if (pt.Y > ClientRectangle.Bottom) {
1821                                 document.MoveCaret(CaretDirection.LineDown);
1822                                 document.SetSelectionToCaret(false);
1823                                 
1824                                 CaretMoved(this, null);
1825                         } else if (pt.Y < ClientRectangle.Top) {
1826                                 document.MoveCaret(CaretDirection.LineUp);
1827                                 document.SetSelectionToCaret(false);
1828                                 
1829                                 CaretMoved(this, null);
1830                         }
1831                 }
1832
1833                 /// <summary>Ensure the caret is always visible</summary>
1834                 internal void CaretMoved(object sender, EventArgs e) {
1835                         Point   pos;
1836                         int     height;
1837
1838                         if (canvas_width < 1 || canvas_height < 1)
1839                                 return;
1840
1841                         document.MoveCaretToTextTag ();
1842                         pos = document.Caret;
1843
1844                         //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);
1845
1846
1847                         // Horizontal scrolling:
1848                         // If the caret moves to the left outside the visible area, we jump the document into view, not just one
1849                         // character, but 1/3 of the width of the document
1850                         // If the caret moves to the right outside the visible area, we scroll just enough to keep the caret visible
1851
1852                         // Handle horizontal scrolling
1853                         if (document.CaretLine.alignment == HorizontalAlignment.Left) {
1854                                 // Check if we moved out of view to the left
1855                                 if (pos.X < (document.ViewPortX)) {
1856                                         do {
1857                                                 if ((hscroll.Value - document.ViewPortWidth / 3) >= hscroll.Minimum) {
1858                                                         hscroll.Value -= document.ViewPortWidth / 3;
1859                                                 } else {
1860                                                         hscroll.Value = hscroll.Minimum;
1861                                                 }
1862                                         } while (hscroll.Value > pos.X);
1863                                 }
1864
1865                                 // Check if we moved out of view to the right
1866                                 if ((pos.X >= (document.ViewPortWidth + document.ViewPortX)) && (hscroll.Value != hscroll.Maximum)) {
1867                                         if ((pos.X - document.ViewPortWidth + 1) <= hscroll.Maximum) {
1868                                                 if (pos.X - document.ViewPortWidth >= 0) {
1869                                                         hscroll.Value = pos.X - document.ViewPortWidth + 1;
1870                                                 } else {
1871                                                         hscroll.Value = 0;
1872                                                 }
1873                                         } else {
1874                                                 hscroll.Value = hscroll.Maximum;
1875                                         }
1876                                 }
1877                         } else if (document.CaretLine.alignment == HorizontalAlignment.Right) {
1878 //                              hscroll.Value = pos.X;
1879
1880 //                              if ((pos.X > (this.canvas_width + document.ViewPortX)) && (hscroll.Enabled && (hscroll.Value != hscroll.Maximum))) {
1881 //                                      hscroll.Value = hscroll.Maximum;
1882 //                              }
1883                         } else {
1884                                 // FIXME - implement center cursor alignment
1885                         }
1886
1887                         if (!document.multiline) {
1888                                 return;
1889                         }
1890
1891                         // Handle vertical scrolling
1892                         height = document.CaretLine.Height + 1;
1893
1894                         if (pos.Y < document.ViewPortY) {
1895                                 vscroll.Value = pos.Y;
1896                         }
1897
1898                         if ((pos.Y + height) > (document.ViewPortY + canvas_height)) {
1899                                 vscroll.Value = Math.Max (0, pos.Y - canvas_height + height);
1900                         }
1901                 }
1902
1903                 internal bool Paste(IDataObject clip, DataFormats.Format format, bool obey_length) {
1904                         string          s;
1905
1906                         if (clip == null)
1907                                 return false;
1908
1909                         if (format == null) {
1910                                 if ((this is RichTextBox) && clip.GetDataPresent(DataFormats.Rtf)) {
1911                                         format = DataFormats.GetFormat(DataFormats.Rtf);
1912                                 } else if ((this is RichTextBox) && clip.GetDataPresent (DataFormats.Bitmap)) {
1913                                         format = DataFormats.GetFormat (DataFormats.Bitmap);
1914                                 } else if (clip.GetDataPresent(DataFormats.UnicodeText)) {
1915                                         format = DataFormats.GetFormat(DataFormats.UnicodeText);
1916                                 } else if (clip.GetDataPresent(DataFormats.Text)) {
1917                                         format = DataFormats.GetFormat(DataFormats.Text);
1918                                 } else {
1919                                         return false;
1920                                 }
1921                         } else {
1922                                 if ((format.Name == DataFormats.Rtf) && !(this is RichTextBox)) {
1923                                         return false;
1924                                 }
1925
1926                                 if (!clip.GetDataPresent(format.Name)) {
1927                                         return false;
1928                                 }
1929                         }
1930
1931                         if (format.Name == DataFormats.Rtf) {
1932                                 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1933                                 ((RichTextBox)this).SelectedRtf = (string)clip.GetData(DataFormats.Rtf);
1934                                 document.undo.EndUserAction ();
1935                                 return true;
1936                         } else if (format.Name == DataFormats.Bitmap) {
1937                                 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1938                                 //      document.InsertImage (document.caret.line, document.caret.pos, (Image) clip.GetData (DataFormats.Bitmap));
1939                                 document.MoveCaret (CaretDirection.CharForward);
1940                                 document.undo.EndUserAction ();
1941                                 return true;
1942                         } else if (format.Name == DataFormats.UnicodeText) {
1943                                 s = (string)clip.GetData(DataFormats.UnicodeText);
1944                         } else if (format.Name == DataFormats.Text) {
1945                                 s = (string)clip.GetData(DataFormats.Text);
1946                         } else {
1947                                 return false;
1948                         }
1949
1950                         if (!obey_length) {
1951                                 document.undo.BeginUserAction (Locale.GetText ("Paste"));
1952                                 this.SelectedText = s;
1953                                 document.undo.EndUserAction ();
1954                         } else {
1955                                 if ((s.Length + document.Length) < max_length) {
1956                                         document.undo.BeginUserAction (Locale.GetText ("Paste"));
1957                                         this.SelectedText = s;
1958                                         document.undo.EndUserAction ();
1959                                 } else if (document.Length < max_length) {
1960                                         document.undo.BeginUserAction (Locale.GetText ("Paste"));
1961                                         this.SelectedText = s.Substring (0, max_length - document.Length);
1962                                         document.undo.EndUserAction ();
1963                                 }
1964                         }
1965
1966                         return true;
1967                 }
1968                 #endregion      // Private Methods
1969
1970 #if NET_2_0
1971                 // This is called just before OnTextChanged is called.
1972                 internal virtual void OnTextUpdate ()
1973                 {
1974                 }
1975                 
1976                 protected override void OnTextChanged (EventArgs e)
1977                 {
1978                         base.OnTextChanged (e);
1979                 }
1980
1981                 public virtual int GetLineFromCharIndex (int index)
1982                 {
1983                         Line line_out;
1984                         LineTag tag_out;
1985                         int pos;
1986                         
1987                         document.CharIndexToLineTag (index, out line_out, out tag_out, out pos);
1988
1989                         return line_out.LineNo;
1990                 }
1991
1992                 protected override void OnMouseUp (MouseEventArgs e)
1993                 {
1994                         base.OnMouseUp (e);
1995                 }
1996
1997 #endif
1998         }
1999 }