// NOT COMPLETE
using System;
+using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
-#if NET_2_0
+using System.Collections.Generic;
using System.Runtime.InteropServices;
-#endif
namespace System.Windows.Forms {
-#if NET_2_0
[ComVisible(true)]
[ClassInterface (ClassInterfaceType.AutoDispatch)]
[Designer ("System.Windows.Forms.Design.TextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
-#endif
public class TextBox : TextBoxBase {
#region Variables
private ContextMenu menu;
private MenuItem delete;
private MenuItem select_all;
-#if NET_2_0
- private bool use_system_password_char = false;
- private AutoCompleteStringCollection auto_complete_custom_source = null;
+ private bool use_system_password_char;
+ private AutoCompleteStringCollection auto_complete_custom_source;
private AutoCompleteMode auto_complete_mode = AutoCompleteMode.None;
private AutoCompleteSource auto_complete_source = AutoCompleteSource.None;
-#endif
+ private AutoCompleteListBox auto_complete_listbox;
+ private string auto_complete_original_text;
+ private int auto_complete_selected_index = -1;
+ private List<string> auto_complete_matches;
+ private ComboBox auto_complete_cb_source;
#endregion // Variables
#region Public Constructors
alignment = HorizontalAlignment.Left;
this.LostFocus +=new EventHandler(TextBox_LostFocus);
this.RightToLeftChanged += new EventHandler (TextBox_RightToLeftChanged);
+ MouseWheel += new MouseEventHandler (TextBox_MouseWheel);
BackColor = SystemColors.Window;
ForeColor = SystemColors.WindowText;
UpdateAlignment ();
}
- private void TextBox_LostFocus(object sender, EventArgs e) {
+ private void TextBox_LostFocus (object sender, EventArgs e) {
if (hide_selection)
document.InvalidateSelectionArea ();
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ }
+
+ private void TextBox_MouseWheel (object o, MouseEventArgs args)
+ {
+ if (auto_complete_listbox == null || !auto_complete_listbox.Visible)
+ return;
+
+ int lines = args.Delta / 120;
+ auto_complete_listbox.Scroll (-lines);
+ }
+
+ // Receives either WM_KEYDOWN or WM_CHAR that will likely need the generation/lookup
+ // of new matches
+ private void ProcessAutoCompleteInput (ref Message m, bool deleting_chars)
+ {
+ // Need to call base.WndProc before to have access to
+ // the updated Text property value
+ base.WndProc (ref m);
+ auto_complete_original_text = Text;
+ ShowAutoCompleteListBox (deleting_chars);
+ }
+
+ private void ShowAutoCompleteListBox (bool deleting_chars)
+ {
+ //
+ // We only support CustomSource by now
+ //
+
+ IList source = auto_complete_cb_source == null ? auto_complete_custom_source : (IList)auto_complete_cb_source.Items;
+
+ bool append = auto_complete_mode == AutoCompleteMode.Append || auto_complete_mode == AutoCompleteMode.SuggestAppend;
+ bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend;
+
+ if (Text.Length == 0) {
+ if (auto_complete_listbox != null)
+ auto_complete_listbox.HideListBox (false);
+ return;
+ }
+
+ if (auto_complete_matches == null)
+ auto_complete_matches = new List<string> ();
+
+ string text = Text;
+ auto_complete_matches.Clear ();
+
+ for (int i = 0; i < source.Count; i++) {
+ string item_text = auto_complete_cb_source == null ? auto_complete_custom_source [i] :
+ auto_complete_cb_source.GetItemText (auto_complete_cb_source.Items [i]);
+ if (item_text.StartsWith (text, StringComparison.CurrentCultureIgnoreCase))
+ auto_complete_matches.Add (item_text);
+ }
+
+ auto_complete_matches.Sort ();
+
+ // Return if we have a single exact match
+ if ((auto_complete_matches.Count == 0) || (auto_complete_matches.Count == 1 &&
+ auto_complete_matches [0].Equals (text, StringComparison.CurrentCultureIgnoreCase))) {
+
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ return;
+ }
+
+ auto_complete_selected_index = suggest ? -1 : 0;
+
+ if (suggest) {
+ if (auto_complete_listbox == null)
+ auto_complete_listbox = new AutoCompleteListBox (this);
+
+ // Show or update auto complete listbox contents
+ auto_complete_listbox.Location = PointToScreen (new Point (0, Height));
+ auto_complete_listbox.ShowListBox ();
+ }
+
+ if (append && !deleting_chars)
+ AppendAutoCompleteMatch (0);
+
+ document.MoveCaret (CaretDirection.End);
+ }
+
+ internal void HideAutoCompleteList ()
+ {
+ if (auto_complete_listbox != null)
+ auto_complete_listbox.HideListBox (false);
+ }
+
+ internal bool IsAutoCompleteAvailable {
+ get {
+ if (auto_complete_source == AutoCompleteSource.None || auto_complete_mode == AutoCompleteMode.None)
+ return false;
+
+ // We only support CustomSource by now, as well as an internal custom source used by ComboBox
+ if (auto_complete_source != AutoCompleteSource.CustomSource)
+ return false;
+ IList custom_source = auto_complete_cb_source == null ? auto_complete_custom_source : (IList)auto_complete_cb_source.Items;
+ if (custom_source == null || custom_source.Count == 0)
+ return false;
+
+ return true;
+ }
+ }
+
+ internal ComboBox AutoCompleteInternalSource {
+ get {
+ return auto_complete_cb_source;
+ }
+ set {
+ auto_complete_cb_source = value;
+ }
+ }
+
+ internal bool CanNavigateAutoCompleteList {
+ get {
+ if (auto_complete_mode == AutoCompleteMode.None)
+ return false;
+ if (auto_complete_matches == null || auto_complete_matches.Count == 0)
+ return false;
+
+ bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible;
+ if (auto_complete_mode == AutoCompleteMode.Suggest && !suggest_window_visible)
+ return false;
+
+ return true;
+ }
+ }
+
+ bool NavigateAutoCompleteList (Keys key)
+ {
+ if (auto_complete_matches == null || auto_complete_matches.Count == 0)
+ return false;
+
+ bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible;
+ if (!suggest_window_visible && auto_complete_mode == AutoCompleteMode.Suggest)
+ return false;
+
+ int index = auto_complete_selected_index;
+
+ switch (key) {
+ case Keys.Up:
+ index -= 1;
+ if (index < -1)
+ index = auto_complete_matches.Count - 1;
+ break;
+ case Keys.Down:
+ index += 1;
+ if (index >= auto_complete_matches.Count)
+ index = -1;
+ break;
+ case Keys.PageUp:
+ if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible)
+ goto case Keys.Up;
+
+ if (index == -1)
+ index = auto_complete_matches.Count - 1;
+ else if (index == 0)
+ index = -1;
+ else {
+ index -= auto_complete_listbox.page_size - 1;
+ if (index < 0)
+ index = 0;
+ }
+ break;
+ case Keys.PageDown:
+ if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible)
+ goto case Keys.Down;
+
+ if (index == -1)
+ index = 0;
+ else if (index == auto_complete_matches.Count - 1)
+ index = -1;
+ else {
+ index += auto_complete_listbox.page_size - 1;
+ if (index >= auto_complete_matches.Count)
+ index = auto_complete_matches.Count - 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // In SuggestAppend mode the navigation mode depends on the visibility of the suggest lb.
+ bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend;
+ if (suggest && suggest_window_visible) {
+ Text = index == -1 ? auto_complete_original_text : auto_complete_matches [index];
+ auto_complete_listbox.HighlightedIndex = index;
+ } else
+ // Append only, not suggest at all
+ AppendAutoCompleteMatch (index < 0 ? 0 : index);
+
+ auto_complete_selected_index = index;
+ document.MoveCaret (CaretDirection.End);
+
+ return true;
+ }
+
+ void AppendAutoCompleteMatch (int index)
+ {
+ Text = auto_complete_original_text + auto_complete_matches [index].Substring (auto_complete_original_text.Length);
+ SelectionStart = auto_complete_original_text.Length;
+ SelectionLength = auto_complete_matches [index].Length - auto_complete_original_text.Length;
+ }
+
+ // this is called when the user selects a value from the autocomplete list
+ // *with* the mouse
+ internal virtual void OnAutoCompleteValueSelected (EventArgs args)
+ {
}
private void UpdateAlignment ()
internal override Color ChangeBackColor (Color backColor)
{
if (backColor == Color.Empty) {
-#if NET_2_0
if (!ReadOnly)
backColor = SystemColors.Window;
-#else
- backColor = SystemColors.Window;
-#endif
+
backcolor_set = false;
}
+
return backColor;
}
-#if NET_2_0
void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) {
if(auto_complete_source == AutoCompleteSource.CustomSource) {
//FIXME: handle add, remove and refresh events in AutoComplete algorithm.
}
}
-#endif
#endregion // Private & Internal Methods
#region Public Instance Properties
-#if NET_2_0
[MonoTODO("AutoCompletion algorithm is currently not implemented.")]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
[Browsable (true)]
if (!Multiline)
document.PasswordChar = PasswordChar.ToString ();
else
- document.PasswordChar = "";
+ document.PasswordChar = string.Empty;
+ Invalidate ();
}
}
}
-#endif
[DefaultValue(false)]
[MWFCategory("Behavior")]
set {
if (value != accepts_return) {
accepts_return = value;
- }
+ }
}
}
[Localizable(true)]
[DefaultValue('\0')]
[MWFCategory("Behavior")]
-#if NET_2_0
[RefreshProperties (RefreshProperties.Repaint)]
-#endif
public char PasswordChar {
get {
-#if NET_2_0
if (use_system_password_char) {
return '*';
}
-#endif
return password_char;
}
if (!Multiline) {
document.PasswordChar = PasswordChar.ToString ();
} else {
- document.PasswordChar = "";
+ document.PasswordChar = string.Empty;
}
this.CalculateDocument();
}
}
}
-#if ONLY_1_1
- [Browsable(false)]
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public override int SelectionLength {
- get {
- return base.SelectionLength;
- }
- set {
- base.SelectionLength = value;
- }
- }
-#endif
-
public override string Text {
get {
return base.Text;
}
#endregion // Public Instance Properties
-#if NET_2_0
public void Paste (string text)
{
document.ReplaceSelection (CaseAdjust (text), false);
ScrollToCaret();
OnTextChanged(EventArgs.Empty);
}
-#endif
#region Protected Instance Methods
protected override CreateParams CreateParams {
get {
}
}
-#if ONLY_1_1
- protected override ImeMode DefaultImeMode {
- get {
- return base.DefaultImeMode;
- }
- }
-#endif
-#if NET_2_0
protected override void Dispose (bool disposing)
{
base.Dispose (disposing);
}
-#endif
- protected override bool IsInputKey(Keys keyData) {
+ protected override bool IsInputKey (Keys keyData)
+ {
return base.IsInputKey (keyData);
}
- protected override void OnGotFocus(EventArgs e) {
+ protected override void OnGotFocus (EventArgs e)
+ {
base.OnGotFocus (e);
if (selection_length == -1 && !has_been_focused)
SelectAllNoScroll ();
has_been_focused = true;
}
- protected override void OnHandleCreated(EventArgs e) {
+ protected override void OnHandleCreated (EventArgs e)
+ {
base.OnHandleCreated (e);
}
-#if ONLY_1_1
- protected override void OnMouseUp(MouseEventArgs e) {
- base.OnMouseUp (e);
- }
-#endif
-
- protected virtual void OnTextAlignChanged(EventArgs e) {
+ protected virtual void OnTextAlignChanged (EventArgs e)
+ {
EventHandler eh = (EventHandler)(Events [TextAlignChangedEvent]);
if (eh != null)
eh (this, e);
}
- protected override void WndProc(ref Message m) {
+ protected override void WndProc (ref Message m)
+ {
switch ((Msg)m.Msg) {
+ case Msg.WM_KEYDOWN:
+ if (!IsAutoCompleteAvailable)
+ break;
+
+ Keys key_data = (Keys)m.WParam.ToInt32 ();
+ switch (key_data) {
+ case Keys.Down:
+ case Keys.Up:
+ case Keys.PageDown:
+ case Keys.PageUp:
+ if (NavigateAutoCompleteList (key_data)) {
+ m.Result = IntPtr.Zero;
+ return;
+ }
+ break;
+ case Keys.Enter:
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ SelectAll ();
+ break;
+ case Keys.Escape:
+ if (auto_complete_listbox != null && auto_complete_listbox.Visible)
+ auto_complete_listbox.HideListBox (false);
+ break;
+ case Keys.Delete:
+ ProcessAutoCompleteInput (ref m, true);
+ return;
+ default:
+ break;
+ }
+ break;
+ case Msg.WM_CHAR:
+ if (!IsAutoCompleteAvailable)
+ break;
+
+ // Don't handle either Enter or Esc - they are handled in the WM_KEYDOWN case
+ int char_value = m.WParam.ToInt32 ();
+ if (char_value == 13 || char_value == 27)
+ break;
+
+ ProcessAutoCompleteInput (ref m, char_value == 8);
+ return;
case Msg.WM_LBUTTONDOWN:
// When the textbox gets focus by LBUTTON (but not by middle or right)
// it does not do the select all / scroll thing.
}
}
+ internal void RestoreContextMenu ()
+ {
+ ContextMenuInternal = menu;
+ }
+
private void menu_Popup(object sender, EventArgs e) {
if (SelectionLength == 0) {
cut.Enabled = false;
}
private void delete_Click(object sender, EventArgs e) {
- SelectedText = "";
+ SelectedText = string.Empty;
}
private void select_all_Click(object sender, EventArgs e) {
}
#endregion // Private Methods
-#if NET_2_0
public override bool Multiline {
get {
return base.Multiline;
{
base.OnHandleDestroyed (e);
}
-#endif
+
+ class AutoCompleteListBox : Control
+ {
+ TextBox owner;
+ VScrollBar vscroll;
+ int top_item;
+ int last_item;
+ internal int page_size;
+ int item_height;
+ int highlighted_index = -1;
+ bool user_defined_size;
+ bool resizing;
+ Rectangle resizer_bounds;
+
+ const int DefaultDropDownItems = 7;
+
+ public AutoCompleteListBox (TextBox tb)
+ {
+ owner = tb;
+ item_height = FontHeight + 2;
+
+ vscroll = new VScrollBar ();
+ vscroll.ValueChanged += VScrollValueChanged;
+ Controls.Add (vscroll);
+
+ is_visible = false;
+ InternalBorderStyle = BorderStyle.FixedSingle;
+ }
+
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp = base.CreateParams;
+
+ cp.Style ^= (int)WindowStyles.WS_CHILD;
+ cp.Style ^= (int)WindowStyles.WS_VISIBLE;
+ cp.Style |= (int)WindowStyles.WS_POPUP;
+ cp.ExStyle |= (int)WindowExStyles.WS_EX_TOPMOST | (int)WindowExStyles.WS_EX_TOOLWINDOW;
+ return cp;
+ }
+ }
+
+ public int HighlightedIndex {
+ get {
+ return highlighted_index;
+ }
+ set {
+ if (value == highlighted_index)
+ return;
+
+ if (highlighted_index != -1)
+ Invalidate (GetItemBounds (highlighted_index));
+ highlighted_index = value;
+ if (highlighted_index != -1)
+ Invalidate (GetItemBounds (highlighted_index));
+
+ if (highlighted_index != -1)
+ EnsureVisible (highlighted_index);
+ }
+ }
+
+ public void Scroll (int lines)
+ {
+ int max = vscroll.Maximum - page_size + 1;
+ int val = vscroll.Value + lines;
+ if (val > max)
+ val = max;
+ else if (val < vscroll.Minimum)
+ val = vscroll.Minimum;
+
+ vscroll.Value = val;
+ }
+
+ public void EnsureVisible (int index)
+ {
+ if (index < top_item) {
+ vscroll.Value = index;
+ } else {
+ int max = vscroll.Maximum - page_size + 1;
+ int rows = Height / item_height;
+ if (index > top_item + rows - 1) {
+ index = index - rows + 1;
+ vscroll.Value = index > max ? max : index;
+ }
+ }
+ }
+
+ internal override bool ActivateOnShow {
+ get {
+ return false;
+ }
+ }
+
+ void VScrollValueChanged (object o, EventArgs args)
+ {
+ if (top_item == vscroll.Value)
+ return;
+
+ top_item = vscroll.Value;
+ last_item = GetLastVisibleItem ();
+ Invalidate ();
+ }
+
+ int GetLastVisibleItem ()
+ {
+ int top_y = Height;
+
+ for (int i = top_item; i < owner.auto_complete_matches.Count; i++) {
+ int pos = i - top_item; // relative to visible area
+ if ((pos * item_height) + item_height >= top_y)
+ return i;
+ }
+
+ return owner.auto_complete_matches.Count - 1;
+ }
+
+ Rectangle GetItemBounds (int index)
+ {
+ int pos = index - top_item;
+ Rectangle bounds = new Rectangle (0, pos * item_height, Width, item_height);
+ if (vscroll.Visible)
+ bounds.Width -= vscroll.Width;
+
+ return bounds;
+ }
+
+ int GetItemAt (Point loc)
+ {
+ if (loc.Y > (last_item - top_item) * item_height + item_height)
+ return -1;
+
+ int retval = loc.Y / item_height;
+ retval += top_item;
+
+ return retval;
+ }
+
+ void LayoutListBox ()
+ {
+ int total_height = owner.auto_complete_matches.Count * item_height;
+ page_size = Math.Max (Height / item_height, 1);
+ last_item = GetLastVisibleItem ();
+
+ if (Height < total_height) {
+ vscroll.Visible = true;
+ vscroll.Maximum = owner.auto_complete_matches.Count - 1;
+ vscroll.LargeChange = page_size;
+ vscroll.Location = new Point (Width - vscroll.Width, 0);
+ vscroll.Height = Height - item_height;
+ } else
+ vscroll.Visible = false;
+
+ resizer_bounds = new Rectangle (Width - item_height, Height - item_height,
+ item_height, item_height);
+ }
+
+ public void HideListBox (bool set_text)
+ {
+ if (set_text)
+ owner.Text = owner.auto_complete_matches [HighlightedIndex];
+
+ Capture = false;
+ Hide ();
+ }
+
+ public void ShowListBox ()
+ {
+ if (!user_defined_size) {
+ // This should call the Layout routine for us
+ int height = owner.auto_complete_matches.Count > DefaultDropDownItems ?
+ DefaultDropDownItems * item_height : (owner.auto_complete_matches.Count + 1) * item_height;
+ Size = new Size (owner.Width, height);
+ } else
+ LayoutListBox ();
+
+ vscroll.Value = 0;
+ HighlightedIndex = -1;
+
+ Show ();
+ // make sure we are on top - call the raw routine, since we are parentless
+ XplatUI.SetZOrder (Handle, IntPtr.Zero, true, false);
+ Invalidate ();
+ }
+
+ protected override void OnResize (EventArgs args)
+ {
+ base.OnResize (args);
+
+ LayoutListBox ();
+ Refresh ();
+ }
+
+ protected override void OnMouseDown (MouseEventArgs args)
+ {
+ base.OnMouseDown (args);
+
+ if (!resizer_bounds.Contains (args.Location))
+ return;
+
+ user_defined_size = true;
+ resizing = true;
+ Capture = true;
+ }
+
+ protected override void OnMouseMove (MouseEventArgs args)
+ {
+ base.OnMouseMove (args);
+
+ if (resizing) {
+ Point mouse_loc = Control.MousePosition;
+ Point ctrl_loc = PointToScreen (Point.Empty);
+
+ Size new_size = new Size (mouse_loc.X - ctrl_loc.X, mouse_loc.Y - ctrl_loc.Y);
+ if (new_size.Height < item_height)
+ new_size.Height = item_height;
+ if (new_size.Width < item_height)
+ new_size.Width = item_height;
+
+ Size = new_size;
+ return;
+ }
+
+ Cursor = resizer_bounds.Contains (args.Location) ? Cursors.SizeNWSE : Cursors.Default;
+
+ int item_idx = GetItemAt (args.Location);
+ if (item_idx != -1)
+ HighlightedIndex = item_idx;
+ }
+
+ protected override void OnMouseUp (MouseEventArgs args)
+ {
+ base.OnMouseUp (args);
+
+ int item_idx = GetItemAt (args.Location);
+ if (item_idx != -1 && !resizing)
+ HideListBox (true);
+
+ owner.OnAutoCompleteValueSelected (EventArgs.Empty); // internal
+ resizing = false;
+ Capture = false;
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs args)
+ {
+ Graphics g = args.Graphics;
+ Brush brush = ThemeEngine.Current.ResPool.GetSolidBrush (ForeColor);
+
+ int highlighted_idx = HighlightedIndex;
+
+ int y = 0;
+ int last = GetLastVisibleItem ();
+ for (int i = top_item; i <= last; i++) {
+ Rectangle item_bounds = GetItemBounds (i);
+ if (!item_bounds.IntersectsWith (args.ClipRectangle))
+ continue;
+
+ if (i == highlighted_idx) {
+ g.FillRectangle (SystemBrushes.Highlight, item_bounds);
+ g.DrawString (owner.auto_complete_matches [i], Font, SystemBrushes.HighlightText, item_bounds);
+ } else
+ g.DrawString (owner.auto_complete_matches [i], Font, brush, item_bounds);
+
+ y += item_height;
+ }
+
+ ThemeEngine.Current.CPDrawSizeGrip (g, SystemColors.Control, resizer_bounds);
+ }
+ }
}
-#if NET_2_0
- internal class TextBoxAutoCompleteSourceConverter : TypeConverter
+ internal class TextBoxAutoCompleteSourceConverter : EnumConverter
{
+ public TextBoxAutoCompleteSourceConverter(Type type)
+ : base(type)
+ { }
+
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
+ {
+ StandardValuesCollection stdv = base.GetStandardValues(context);
+ AutoCompleteSource[] arr = new AutoCompleteSource[stdv.Count];
+ stdv.CopyTo(arr, 0);
+ AutoCompleteSource[] arr2 = Array.FindAll(arr, delegate (AutoCompleteSource value) {
+ // No "ListItems" in a TextBox.
+ return value != AutoCompleteSource.ListItems;
+ });
+ return new StandardValuesCollection(arr2);
+ }
}
-#endif
}