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