2008-12-10 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TextBox.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 //     Daniel Nauck    (dna(at)mono-project(dot)de)
25 //
26
27 // NOT COMPLETE
28
29 using System;
30 using System.Collections;
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
33 using System.Drawing;
34 #if NET_2_0
35 using System.Collections.Generic;
36 using System.Runtime.InteropServices;
37 #endif
38
39 namespace System.Windows.Forms {
40
41 #if NET_2_0
42         [ComVisible(true)]
43         [ClassInterface (ClassInterfaceType.AutoDispatch)]
44         [Designer ("System.Windows.Forms.Design.TextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
45 #endif
46         public class TextBox : TextBoxBase {
47                 #region Variables
48                 private ContextMenu     menu;
49                 private MenuItem        undo;
50                 private MenuItem        cut;
51                 private MenuItem        copy;
52                 private MenuItem        paste;
53                 private MenuItem        delete;
54                 private MenuItem        select_all;
55
56 #if NET_2_0
57                 private bool use_system_password_char;
58                 private AutoCompleteStringCollection auto_complete_custom_source;
59                 private AutoCompleteMode auto_complete_mode = AutoCompleteMode.None;
60                 private AutoCompleteSource auto_complete_source = AutoCompleteSource.None;
61                 private AutoCompleteListBox auto_complete_listbox;
62                 private string auto_complete_original_text;
63                 private int auto_complete_selected_index = -1;
64                 private List<string> auto_complete_matches;
65                 private ComboBox auto_complete_cb_source;
66 #endif
67                 #endregion      // Variables
68
69                 #region Public Constructors
70                 public TextBox() {
71
72                         scrollbars = RichTextBoxScrollBars.None;
73                         alignment = HorizontalAlignment.Left;
74                         this.LostFocus +=new EventHandler(TextBox_LostFocus);
75                         this.RightToLeftChanged += new EventHandler (TextBox_RightToLeftChanged);
76 #if NET_2_0
77                         MouseWheel += new MouseEventHandler (TextBox_MouseWheel);
78 #endif
79
80                         BackColor = SystemColors.Window;
81                         ForeColor = SystemColors.WindowText;
82                         backcolor_set = false;
83
84                         SetStyle (ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, false);
85                         SetStyle (ControlStyles.FixedHeight, true);
86
87                         undo = new MenuItem(Locale.GetText("&Undo"));
88                         cut = new MenuItem(Locale.GetText("Cu&t"));
89                         copy = new MenuItem(Locale.GetText("&Copy"));
90                         paste = new MenuItem(Locale.GetText("&Paste"));
91                         delete = new MenuItem(Locale.GetText("&Delete"));
92                         select_all = new MenuItem(Locale.GetText("Select &All"));
93
94                         menu = new ContextMenu(new MenuItem[] { undo, new MenuItem("-"), cut, copy, paste, delete, new MenuItem("-"), select_all});
95                         ContextMenu = menu;
96
97                         menu.Popup += new EventHandler(menu_Popup);
98                         undo.Click += new EventHandler(undo_Click);
99                         cut.Click += new EventHandler(cut_Click);
100                         copy.Click += new EventHandler(copy_Click);
101                         paste.Click += new EventHandler(paste_Click);
102                         delete.Click += new EventHandler(delete_Click);
103                         select_all.Click += new EventHandler(select_all_Click);
104
105                         document.multiline = false;
106                 }
107
108                 #endregion      // Public Constructors
109
110                 #region Private & Internal Methods
111
112                 void TextBox_RightToLeftChanged (object sender, EventArgs e)
113                 {
114                         UpdateAlignment ();
115                 }
116
117                 private void TextBox_LostFocus (object sender, EventArgs e) {
118                         if (hide_selection)
119                                 document.InvalidateSelectionArea ();
120 #if NET_2_0
121                         if (auto_complete_listbox != null && auto_complete_listbox.Visible)
122                                 auto_complete_listbox.HideListBox (false);
123 #endif
124                 }
125
126 #if NET_2_0
127                 private void TextBox_MouseWheel (object o, MouseEventArgs args)
128                 {
129                         if (auto_complete_listbox == null || !auto_complete_listbox.Visible)
130                                 return;
131
132                         int lines = args.Delta / 120;
133                         auto_complete_listbox.Scroll (-lines);
134                 }
135
136                 private void ShowAutoCompleteListBox (bool is_backspace)
137                 {
138                         // 
139                         // We only support CustomSource by now
140                         //
141
142                         IList source;
143                         if (auto_complete_cb_source == null)
144                                 source = auto_complete_custom_source;
145                         else
146                                 source = auto_complete_cb_source.Items;
147
148                         if (source == null || source.Count == 0)
149                                 return;
150
151                         bool append = auto_complete_mode == AutoCompleteMode.Append || auto_complete_mode == AutoCompleteMode.SuggestAppend;
152                         bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend;
153
154                         if (Text.Length == 0) {
155                                 if (auto_complete_listbox != null)
156                                         auto_complete_listbox.HideListBox (false);
157                                 return;
158                         }
159
160                         if (auto_complete_matches == null)
161                                 auto_complete_matches = new List<string> ();
162
163                         string text = Text;
164                         auto_complete_matches.Clear ();
165
166                         for (int i = 0; i < source.Count; i++) {
167                                 string item_text = auto_complete_cb_source == null ? auto_complete_custom_source [i] :
168                                         auto_complete_cb_source.GetItemText (auto_complete_cb_source.Items [i]);
169                                 if (item_text.StartsWith (text, StringComparison.CurrentCultureIgnoreCase))
170                                         auto_complete_matches.Add (item_text);
171                         }
172
173                         auto_complete_matches.Sort ();
174
175                         // Return if we have a single exact match
176                         if ((auto_complete_matches.Count == 0) || (auto_complete_matches.Count == 1 && 
177                                                 auto_complete_matches [0].Equals (text, StringComparison.CurrentCultureIgnoreCase))) {
178
179                                 if (auto_complete_listbox != null && auto_complete_listbox.Visible)
180                                         auto_complete_listbox.HideListBox (false);
181                                 return;
182                         }
183
184                         auto_complete_selected_index = suggest ? -1 : 0;
185
186                         if (suggest) {
187                                 if (auto_complete_listbox == null)
188                                         auto_complete_listbox = new AutoCompleteListBox (this);
189
190                                 // Show or update auto complete listbox contents
191                                 auto_complete_listbox.Location = PointToScreen (new Point (0, Height));
192                                 auto_complete_listbox.ShowListBox ();
193                         }
194
195                         if (append && !is_backspace)
196                                 AppendAutoCompleteMatch (0);
197
198                         document.MoveCaret (CaretDirection.End);
199                 }
200
201                 internal void HideAutoCompleteList ()
202                 {
203                         if (auto_complete_listbox != null)
204                                 auto_complete_listbox.HideListBox (false);
205                 }
206
207                 bool IsAutoCompleteAvailable {
208                         get {
209                                 if (auto_complete_source == AutoCompleteSource.None || auto_complete_mode == AutoCompleteMode.None)
210                                         return false;
211
212                                 // We only support CustomSource by now
213                                 if (auto_complete_source != AutoCompleteSource.CustomSource || auto_complete_custom_source == null ||
214                                                 auto_complete_custom_source.Count == 0)
215                                         return false;
216
217                                 return true;
218                         }
219                 }
220
221                 internal ComboBox AutoCompleteInternalSource {
222                         get {
223                                 return auto_complete_cb_source;
224                         }
225                         set {
226                                 auto_complete_cb_source = value;
227                         }
228                 }
229
230                 internal bool CanNavigateAutoCompleteList {
231                         get {
232                                 if (auto_complete_mode == AutoCompleteMode.None)
233                                         return false;
234                                 if (auto_complete_matches == null || auto_complete_matches.Count == 0)
235                                         return false;
236
237                                 bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible;
238                                 if (auto_complete_mode == AutoCompleteMode.Suggest && !suggest_window_visible)
239                                         return false;
240
241                                 return true;
242                         }
243                 }
244
245                 bool NavigateAutoCompleteList (Keys key)
246                 {
247                         if (auto_complete_matches == null || auto_complete_matches.Count == 0)
248                                 return false;
249
250                         bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible;
251                         if (!suggest_window_visible && auto_complete_mode == AutoCompleteMode.Suggest)
252                                 return false;
253
254                         int index = auto_complete_selected_index;
255
256                         switch (key) {
257                                 case Keys.Up:
258                                         index -= 1;
259                                         if (index < -1)
260                                                 index = auto_complete_matches.Count - 1;
261                                         break;
262                                 case Keys.Down:
263                                         index += 1;
264                                         if (index >= auto_complete_matches.Count)
265                                                 index = -1;
266                                         break;
267                                 case Keys.PageUp:
268                                         if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible)
269                                                 goto case Keys.Up;
270
271                                         if (index == -1)
272                                                 index = auto_complete_matches.Count - 1;
273                                         else if (index == 0)
274                                                 index = -1;
275                                         else {
276                                                 index -= auto_complete_listbox.page_size - 1;
277                                                 if (index < 0)
278                                                         index = 0;
279                                         }
280                                         break;
281                                 case Keys.PageDown:
282                                         if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible)
283                                                 goto case Keys.Down;
284
285                                         if (index == -1)
286                                                 index = 0;
287                                         else if (index == auto_complete_matches.Count - 1)
288                                                 index = -1;
289                                         else {
290                                                 index += auto_complete_listbox.page_size - 1;
291                                                 if (index >= auto_complete_matches.Count)
292                                                         index = auto_complete_matches.Count - 1;
293                                         }
294                                         break;
295                                 default:
296                                         break;
297                         }
298
299                         // In SuggestAppend mode the navigation mode depends on the visibility of the suggest lb.
300                         bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend;
301                         if (suggest && suggest_window_visible) {
302                                 Text = index == -1 ? auto_complete_original_text : auto_complete_matches [index];
303                                 auto_complete_listbox.HighlightedIndex = index;
304                         } else
305                                 // Append only, not suggest at all
306                                 AppendAutoCompleteMatch (index < 0 ? 0 : index);
307                                 
308                         auto_complete_selected_index = index;
309                         document.MoveCaret (CaretDirection.End);
310
311                         return true;
312                 }
313
314                 void AppendAutoCompleteMatch (int index)
315                 {
316                         Text = auto_complete_original_text + auto_complete_matches [index].Substring (auto_complete_original_text.Length);
317                         SelectionStart = auto_complete_original_text.Length;
318                         SelectionLength = auto_complete_matches [index].Length - auto_complete_original_text.Length;
319                 }
320
321 #endif
322
323                 private void UpdateAlignment ()
324                 {
325                         HorizontalAlignment new_alignment = alignment;
326                         RightToLeft rtol = GetInheritedRtoL ();
327
328                         if (rtol == RightToLeft.Yes) {
329                                 if (new_alignment == HorizontalAlignment.Left)
330                                         new_alignment = HorizontalAlignment.Right;
331                                 else if (new_alignment == HorizontalAlignment.Right)
332                                         new_alignment = HorizontalAlignment.Left;
333                         }
334
335                         document.alignment = new_alignment;
336
337                         // MS word-wraps if alignment isn't left
338                         if (Multiline) {
339                                 if (alignment != HorizontalAlignment.Left) {
340                                         document.Wrap = true;
341                                 } else {
342                                         document.Wrap = word_wrap;
343                                 }
344                         }
345
346                         for (int i = 1; i <= document.Lines; i++) {
347                                 document.GetLine (i).Alignment = new_alignment;
348                         }
349
350                         document.RecalculateDocument (CreateGraphicsInternal ());
351
352                         Invalidate ();  // Make sure we refresh
353                 }
354
355                 internal override Color ChangeBackColor (Color backColor)
356                 {
357                         if (backColor == Color.Empty) {
358 #if NET_2_0
359                                 if (!ReadOnly)
360                                         backColor = SystemColors.Window;
361 #else
362                                 backColor = SystemColors.Window;
363 #endif
364                                 backcolor_set = false;
365                         }
366                         return backColor;
367                 }
368
369 #if NET_2_0
370                 void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) {
371                         if(auto_complete_source == AutoCompleteSource.CustomSource) {
372                                 //FIXME: handle add, remove and refresh events in AutoComplete algorithm.
373                         }
374                 }
375 #endif
376                 #endregion      // Private & Internal Methods
377
378                 #region Public Instance Properties
379 #if NET_2_0
380                 [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
381                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
382                 [Browsable (true)]
383                 [EditorBrowsable (EditorBrowsableState.Always)]
384                 [Localizable (true)]
385                 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design,
386                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
387                 public AutoCompleteStringCollection AutoCompleteCustomSource { 
388                         get {
389                                 if(auto_complete_custom_source == null) {
390                                         auto_complete_custom_source = new AutoCompleteStringCollection ();
391                                         auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
392                                 }
393                                 return auto_complete_custom_source;
394                         }
395                         set {
396                                 if(auto_complete_custom_source == value)
397                                         return;
398
399                                 if(auto_complete_custom_source != null) //remove eventhandler from old collection
400                                         auto_complete_custom_source.CollectionChanged -= new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
401
402                                 auto_complete_custom_source = value;
403
404                                 if(auto_complete_custom_source != null)
405                                         auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged);
406                         }
407                 }
408
409                 [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
410                 [Browsable (true)]
411                 [EditorBrowsable (EditorBrowsableState.Always)]
412                 [DefaultValue (AutoCompleteMode.None)]
413                 public AutoCompleteMode AutoCompleteMode {
414                         get { return auto_complete_mode; }
415                         set {
416                                 if(auto_complete_mode == value)
417                                         return;
418
419                                 if((value < AutoCompleteMode.None) || (value > AutoCompleteMode.SuggestAppend))
420                                         throw new InvalidEnumArgumentException (Locale.GetText ("Enum argument value '{0}' is not valid for AutoCompleteMode", value));
421
422                                 auto_complete_mode = value;
423                         }
424                 }
425
426                 [MonoTODO("AutoCompletion algorithm is currently not implemented.")]
427                 [Browsable (true)]
428                 [EditorBrowsable (EditorBrowsableState.Always)]
429                 [DefaultValue (AutoCompleteSource.None)]
430                 [TypeConverter (typeof (TextBoxAutoCompleteSourceConverter))]
431                 public AutoCompleteSource AutoCompleteSource {
432                         get { return auto_complete_source; }
433                         set {
434                                 if(auto_complete_source == value)
435                                         return;
436
437                                 if(!Enum.IsDefined (typeof (AutoCompleteSource), value))
438                                         throw new InvalidEnumArgumentException (Locale.GetText ("Enum argument value '{0}' is not valid for AutoCompleteSource", value));
439
440                                 auto_complete_source = value;
441                         }
442                 }
443
444                 [DefaultValue(false)]
445                 [RefreshProperties (RefreshProperties.Repaint)]
446                 public bool UseSystemPasswordChar {
447                         get {
448                                 return use_system_password_char;
449                         }
450
451                         set {
452                                 if (use_system_password_char != value) {
453                                         use_system_password_char = value;
454                                         
455                                         if (!Multiline)
456                                                 document.PasswordChar = PasswordChar.ToString ();
457                                         else
458                                                 document.PasswordChar = string.Empty;
459                                         Invalidate ();
460                                 }
461                         }
462                 }
463 #endif
464
465                 [DefaultValue(false)]
466                 [MWFCategory("Behavior")]
467                 public bool AcceptsReturn {
468                         get {
469                                 return accepts_return;
470                         }
471
472                         set {
473                                 if (value != accepts_return) {
474                                         accepts_return = value;
475                                 }
476                         }
477                 }
478
479                 [DefaultValue(CharacterCasing.Normal)]
480                 [MWFCategory("Behavior")]
481                 public CharacterCasing CharacterCasing {
482                         get {
483                                 return character_casing;
484                         }
485
486                         set {
487                                 if (value != character_casing) {
488                                         character_casing = value;
489                                 }
490                         }
491                 }
492
493                 [Localizable(true)]
494                 [DefaultValue('\0')]
495                 [MWFCategory("Behavior")]
496 #if NET_2_0
497                 [RefreshProperties (RefreshProperties.Repaint)]
498 #endif
499                 public char PasswordChar {
500                         get {
501 #if NET_2_0
502                                 if (use_system_password_char) {
503                                         return '*';
504                                 }
505 #endif
506                                 return password_char;
507                         }
508
509                         set {
510                                 if (value != password_char) {
511                                         password_char = value;
512                                         if (!Multiline) {
513                                                 document.PasswordChar = PasswordChar.ToString ();
514                                         } else {
515                                                 document.PasswordChar = string.Empty;
516                                         }
517                                         this.CalculateDocument();
518                                 }
519                         }
520                 }
521
522                 [DefaultValue(ScrollBars.None)]
523                 [Localizable(true)]
524                 [MWFCategory("Appearance")]
525                 public ScrollBars ScrollBars {
526                         get {
527                                 return (ScrollBars)scrollbars;
528                         }
529
530                         set {
531                                 if (!Enum.IsDefined (typeof (ScrollBars), value))
532                                         throw new InvalidEnumArgumentException ("value", (int) value,
533                                                 typeof (ScrollBars));
534
535                                 if (value != (ScrollBars)scrollbars) {
536                                         scrollbars = (RichTextBoxScrollBars)value;
537                                         base.CalculateScrollBars();
538                                 }
539                         }
540                 }
541
542 #if ONLY_1_1
543                 [Browsable(false)]
544                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
545                 public override int SelectionLength {
546                         get {
547                                 return base.SelectionLength;
548                         }
549                         set {
550                                 base.SelectionLength = value;
551                         }
552                 }
553 #endif
554
555                 public override string Text {
556                         get {
557                                 return base.Text;
558                         }
559
560                         set {
561                                 base.Text = value;
562                         }
563                 }
564
565                 [DefaultValue(HorizontalAlignment.Left)]
566                 [Localizable(true)]
567                 [MWFCategory("Appearance")]
568                 public HorizontalAlignment TextAlign {
569                         get {
570                                 return alignment;
571                         }
572
573                         set {
574                                 if (value != alignment) {
575                                         alignment = value;
576
577                                         UpdateAlignment ();
578
579                                         OnTextAlignChanged(EventArgs.Empty);
580                                 }
581                         }
582                 }
583                 #endregion      // Public Instance Properties
584
585 #if NET_2_0
586                 public void Paste (string text)
587                 {
588                         document.ReplaceSelection (CaseAdjust (text), false);
589
590                         ScrollToCaret();
591                         OnTextChanged(EventArgs.Empty);
592                 }
593 #endif
594                 #region Protected Instance Methods
595                 protected override CreateParams CreateParams {
596                         get {
597                                 return base.CreateParams;
598                         }
599                 }
600
601 #if ONLY_1_1
602                 protected override ImeMode DefaultImeMode {
603                         get {
604                                 return base.DefaultImeMode;
605                         }
606                 }
607 #endif
608 #if NET_2_0
609                 protected override void Dispose (bool disposing)
610                 {
611                         base.Dispose (disposing);
612                 }
613 #endif
614
615                 protected override bool IsInputKey (Keys keyData)
616                 {
617                         return base.IsInputKey (keyData);
618                 }
619
620                 protected override void OnGotFocus (EventArgs e)
621                 {
622                         base.OnGotFocus (e);
623                         if (selection_length == -1 && !has_been_focused)
624                                 SelectAllNoScroll ();
625                         has_been_focused = true;
626                 }
627
628                 protected override void OnHandleCreated (EventArgs e)
629                 {
630                         base.OnHandleCreated (e);
631                 }
632
633 #if ONLY_1_1
634                 protected override void OnMouseUp(MouseEventArgs mevent)
635                 {
636                         base.OnMouseUp (mevent);
637                 }
638 #endif
639
640                 protected virtual void OnTextAlignChanged (EventArgs e)
641                 {
642                         EventHandler eh = (EventHandler)(Events [TextAlignChangedEvent]);
643                         if (eh != null)
644                                 eh (this, e);
645                 }
646
647                 protected override void WndProc (ref Message m)
648                 {
649                         switch ((Msg)m.Msg) {
650 #if NET_2_0
651                                 case Msg.WM_KEYDOWN:
652                                         if (!IsAutoCompleteAvailable)
653                                                 break;
654
655                                         Keys key_data = (Keys)m.WParam.ToInt32 ();
656                                         switch (key_data) {
657                                                 case Keys.Down:
658                                                 case Keys.Up:
659                                                 case Keys.PageDown:
660                                                 case Keys.PageUp:
661                                                         if (NavigateAutoCompleteList (key_data)) {
662                                                                 m.Result = IntPtr.Zero;
663                                                                 return;
664                                                         }
665                                                         break;
666                                                 case Keys.Enter:
667                                                         if (auto_complete_listbox != null && auto_complete_listbox.Visible)
668                                                                 auto_complete_listbox.HideListBox (false);
669                                                         SelectAll ();
670                                                         break;
671                                                 case Keys.Escape:
672                                                         if (auto_complete_listbox != null && auto_complete_listbox.Visible)
673                                                                 auto_complete_listbox.HideListBox (false);
674                                                         break;
675                                                 default:
676                                                         break;
677                                         }
678                                         break;
679                                 case Msg.WM_CHAR:
680                                         if (!IsAutoCompleteAvailable)
681                                                 break;
682
683                                         bool is_backspace = m.WParam.ToInt32 () == 8;
684                                         if (!Char.IsLetterOrDigit ((char)m.WParam) && !is_backspace)
685                                                 break;
686                                         
687                                         if (!is_backspace)
688                                                 Text = auto_complete_original_text;
689
690                                         document.MoveCaret (CaretDirection.End);
691
692                                         // Need to call base.WndProc before to have access to
693                                         // the updated Text property value
694                                         base.WndProc (ref m);
695                                         auto_complete_original_text = Text;
696                                         ShowAutoCompleteListBox (is_backspace);
697                                         return;
698 #endif
699                                 case Msg.WM_LBUTTONDOWN:
700                                         // When the textbox gets focus by LBUTTON (but not by middle or right)
701                                         // it does not do the select all / scroll thing.
702                                         has_been_focused = true;
703                                         FocusInternal (true);
704                                         break;
705                         }
706
707                         base.WndProc(ref m);
708                 }
709                 #endregion      // Protected Instance Methods
710
711                 #region Events
712                 static object TextAlignChangedEvent = new object ();
713
714                 public event EventHandler TextAlignChanged {
715                         add { Events.AddHandler (TextAlignChangedEvent, value); }
716                         remove { Events.RemoveHandler (TextAlignChangedEvent, value); }
717                 }
718                 #endregion      // Events
719
720                 #region Private Methods
721
722                 internal override ContextMenu ContextMenuInternal {
723                         get {
724                                 ContextMenu res = base.ContextMenuInternal;
725                                 if (res == menu)
726                                         return null;
727                                 return res;
728                         }
729                         set {
730                                 base.ContextMenuInternal = value;
731                         }
732                 }
733
734                 internal void RestoreContextMenu ()
735                 {
736                         ContextMenuInternal = menu;
737                 }
738
739                 private void menu_Popup(object sender, EventArgs e) {
740                         if (SelectionLength == 0) {
741                                 cut.Enabled = false;
742                                 copy.Enabled = false;
743                         } else {
744                                 cut.Enabled = true;
745                                 copy.Enabled = true;
746                         }
747
748                         if (SelectionLength == TextLength) {
749                                 select_all.Enabled = false;
750                         } else {
751                                 select_all.Enabled = true;
752                         }
753
754                         if (!CanUndo) {
755                                 undo.Enabled = false;
756                         } else {
757                                 undo.Enabled = true;
758                         }
759
760                         if (ReadOnly) {
761                                 undo.Enabled = cut.Enabled = paste.Enabled = delete.Enabled = false;
762                         }
763                 }
764
765                 private void undo_Click(object sender, EventArgs e) {
766                         Undo();
767                 }
768
769                 private void cut_Click(object sender, EventArgs e) {
770                         Cut();
771                 }
772
773                 private void copy_Click(object sender, EventArgs e) {
774                         Copy();
775                 }
776
777                 private void paste_Click(object sender, EventArgs e) {
778                         Paste();
779                 }
780
781                 private void delete_Click(object sender, EventArgs e) {
782                         SelectedText = string.Empty;
783                 }
784
785                 private void select_all_Click(object sender, EventArgs e) {
786                         SelectAll();
787                 }
788                 #endregion      // Private Methods
789
790 #if NET_2_0
791                 public override bool Multiline {
792                         get {
793                                 return base.Multiline;
794                         }
795
796                         set {
797                                 base.Multiline = value;
798                         }
799                 }
800
801                 protected override void OnBackColorChanged (EventArgs e)
802                 {
803                         base.OnBackColorChanged (e);
804                 }
805                 
806                 protected override void OnFontChanged (EventArgs e)
807                 {
808                         base.OnFontChanged (e);
809                 }
810
811                 protected override void OnHandleDestroyed (EventArgs e)
812                 {
813                         base.OnHandleDestroyed (e);
814                 }
815
816                 class AutoCompleteListBox : Control
817                 {
818                         TextBox owner;
819                         VScrollBar vscroll;
820                         int top_item;
821                         int last_item;
822                         internal int page_size;
823                         int item_height;
824                         int highlighted_index = -1;
825                         bool user_defined_size;
826                         bool resizing;
827                         Rectangle resizer_bounds;
828
829                         const int DefaultDropDownItems = 7;
830
831                         public AutoCompleteListBox (TextBox tb)
832                         {
833                                 owner = tb;
834                                 item_height = FontHeight + 2;
835
836                                 vscroll = new VScrollBar ();
837                                 vscroll.ValueChanged += VScrollValueChanged;
838                                 Controls.Add (vscroll);
839
840                                 is_visible = false;
841                                 InternalBorderStyle = BorderStyle.FixedSingle;
842                         }
843
844                         protected override CreateParams CreateParams {
845                                 get {
846                                         CreateParams cp = base.CreateParams;
847
848                                         cp.Style ^= (int)WindowStyles.WS_CHILD;
849                                         cp.Style ^= (int)WindowStyles.WS_VISIBLE;
850                                         cp.Style |= (int)WindowStyles.WS_POPUP;
851                                         cp.ExStyle |= (int)WindowExStyles.WS_EX_TOPMOST | (int)WindowExStyles.WS_EX_TOOLWINDOW;
852                                         return cp;
853                                 }
854                         }
855
856                         public int HighlightedIndex {
857                                 get {
858                                         return highlighted_index;
859                                 }
860                                 set {
861                                         if (value == highlighted_index)
862                                                 return;
863
864                                         if (highlighted_index != -1)
865                                                 Invalidate (GetItemBounds (highlighted_index));
866                                         highlighted_index = value;
867                                         if (highlighted_index != -1)
868                                                 Invalidate (GetItemBounds (highlighted_index));
869
870                                         if (highlighted_index != -1)
871                                                 EnsureVisible (highlighted_index);
872                                 }
873                         }
874
875                         public void Scroll (int lines)
876                         {
877                                 int max = vscroll.Maximum - page_size + 1;
878                                 int val = vscroll.Value + lines;
879                                 if (val > max)
880                                         val = max;
881                                 else if (val < vscroll.Minimum)
882                                         val = vscroll.Minimum;
883
884                                 vscroll.Value = val;
885                         }
886
887                         public void EnsureVisible (int index)
888                         {
889                                 if (index < top_item) {
890                                         vscroll.Value = index;
891                                 } else {
892                                         int max = vscroll.Maximum - page_size + 1;
893                                         int rows = Height / item_height;
894                                         if (index > top_item + rows - 1) {
895                                                 index = index - rows + 1;
896                                                 vscroll.Value = index > max ? max : index;
897                                         }
898                                 }
899                         }
900
901                         internal override bool ActivateOnShow {
902                                 get {
903                                         return false;
904                                 }
905                         }
906
907                         void VScrollValueChanged (object o, EventArgs args)
908                         {
909                                 if (top_item == vscroll.Value)
910                                         return;
911
912                                 top_item = vscroll.Value;
913                                 last_item = GetLastVisibleItem ();
914                                 Invalidate ();
915                         }
916
917                         int GetLastVisibleItem ()
918                         {
919                                 int top_y = Height;
920
921                                 for (int i = top_item; i < owner.auto_complete_matches.Count; i++) {
922                                         int pos = i - top_item; // relative to visible area
923                                         if ((pos * item_height) + item_height >= top_y)
924                                                 return i;
925                                 }
926
927                                 return owner.auto_complete_matches.Count - 1;
928                         }
929
930                         Rectangle GetItemBounds (int index)
931                         {
932                                 int pos = index - top_item;
933                                 Rectangle bounds = new Rectangle (0, pos * item_height, Width, item_height);
934                                 if (vscroll.Visible)
935                                         bounds.Width -= vscroll.Width;
936
937                                 return bounds;
938                         }
939
940                         int GetItemAt (Point loc)
941                         {
942                                 if (loc.Y > (last_item - top_item) * item_height + item_height)
943                                         return -1;
944
945                                 int retval = loc.Y / item_height;
946                                 retval += top_item;
947
948                                 return retval;
949                         }
950
951                         void LayoutListBox ()
952                         {
953                                 int total_height = owner.auto_complete_matches.Count * item_height;
954                                 page_size = Math.Max (Height / item_height, 1);
955                                 last_item = GetLastVisibleItem ();
956
957                                 if (Height < total_height) {
958                                         vscroll.Visible = true;
959                                         vscroll.Maximum = owner.auto_complete_matches.Count - 1;
960                                         vscroll.LargeChange = page_size;
961                                         vscroll.Location = new Point (Width - vscroll.Width, 0);
962                                         vscroll.Height = Height - item_height;
963                                 } else
964                                         vscroll.Visible = false;
965
966                                 resizer_bounds = new Rectangle (Width - item_height, Height - item_height,
967                                                 item_height, item_height);
968                         }
969
970                         public void HideListBox (bool set_text)
971                         {
972                                 if (set_text)
973                                         owner.Text = owner.auto_complete_matches [HighlightedIndex];
974
975                                 Capture = false;
976                                 Hide ();
977                         }
978
979                         public void ShowListBox ()
980                         {
981                                 if (!user_defined_size) {
982                                         // This should call the Layout routine for us
983                                         int height = owner.auto_complete_matches.Count > DefaultDropDownItems ? 
984                                                 DefaultDropDownItems * item_height : (owner.auto_complete_matches.Count + 1) * item_height;
985                                         Size = new Size (owner.Width, height);
986                                 } else
987                                         LayoutListBox ();
988
989                                 vscroll.Value = 0;
990                                 HighlightedIndex = -1;
991
992                                 Show ();
993                                 Invalidate ();
994                         }
995
996                         protected override void OnResize (EventArgs args)
997                         {
998                                 base.OnResize (args);
999
1000                                 LayoutListBox ();
1001                                 Refresh ();
1002                         }
1003
1004                         protected override void OnMouseDown (MouseEventArgs args)
1005                         {
1006                                 base.OnMouseDown (args);
1007
1008                                 if (!resizer_bounds.Contains (args.Location))
1009                                         return;
1010
1011                                 user_defined_size = true;
1012                                 resizing = true;
1013                                 Capture = true;
1014                         }
1015
1016                         protected override void OnMouseMove (MouseEventArgs args)
1017                         {
1018                                 base.OnMouseMove (args);
1019
1020                                 if (resizing) {
1021                                         Point mouse_loc = Control.MousePosition;
1022                                         Point ctrl_loc = PointToScreen (Point.Empty);
1023
1024                                         Size new_size = new Size (mouse_loc.X - ctrl_loc.X, mouse_loc.Y - ctrl_loc.Y);
1025                                         if (new_size.Height < item_height)
1026                                                 new_size.Height = item_height;
1027                                         if (new_size.Width < item_height)
1028                                                 new_size.Width = item_height;
1029
1030                                         Size = new_size;
1031                                         return;
1032                                 }
1033
1034                                 Cursor = resizer_bounds.Contains (args.Location) ? Cursors.SizeNWSE : Cursors.Default;
1035
1036                                 int item_idx = GetItemAt (args.Location);
1037                                 if (item_idx != -1)
1038                                         HighlightedIndex = item_idx;
1039                         }
1040
1041                         protected override void OnMouseUp (MouseEventArgs args)
1042                         {
1043                                 base.OnMouseUp (args);
1044
1045                                 int item_idx = GetItemAt (args.Location);
1046                                 if (item_idx != -1 && !resizing)
1047                                         HideListBox (true);
1048
1049                                 resizing = false;
1050                                 Capture = false;
1051                         }
1052
1053                         internal override void OnPaintInternal (PaintEventArgs args)
1054                         {
1055                                 Graphics g = args.Graphics;
1056                                 Brush brush = ThemeEngine.Current.ResPool.GetSolidBrush (ForeColor);
1057
1058                                 int highlighted_idx = HighlightedIndex;
1059
1060                                 int y = 0;
1061                                 int last = GetLastVisibleItem ();
1062                                 for (int i = top_item; i <= last; i++) {
1063                                         Rectangle item_bounds = GetItemBounds (i);
1064                                         if (!item_bounds.IntersectsWith (args.ClipRectangle))
1065                                                 continue;
1066
1067                                         if (i == highlighted_idx) {
1068                                                 g.FillRectangle (SystemBrushes.Highlight, item_bounds);
1069                                                 g.DrawString (owner.auto_complete_matches [i], Font, SystemBrushes.HighlightText, item_bounds);
1070                                         } else 
1071                                                 g.DrawString (owner.auto_complete_matches [i], Font, brush, item_bounds);
1072
1073                                         y += item_height;
1074                                 }
1075
1076                                 ThemeEngine.Current.CPDrawSizeGrip (g, SystemColors.Control, resizer_bounds);
1077                         }
1078                 }
1079 #endif
1080         }
1081         
1082 #if NET_2_0
1083         internal class TextBoxAutoCompleteSourceConverter : EnumConverter
1084         {
1085                 public TextBoxAutoCompleteSourceConverter(Type type)
1086                         : base(type)
1087                 { }
1088
1089                 public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
1090                 {
1091                         StandardValuesCollection stdv = base.GetStandardValues(context);
1092                         AutoCompleteSource[] arr = new AutoCompleteSource[stdv.Count];
1093                         stdv.CopyTo(arr, 0);
1094                         AutoCompleteSource[] arr2 = Array.FindAll(arr, delegate (AutoCompleteSource value) {
1095                                 // No "ListItems" in a TextBox.
1096                                 return value != AutoCompleteSource.ListItems;
1097                         });
1098                         return new StandardValuesCollection(arr2);
1099                 }
1100         }
1101 #endif
1102 }