* Control.cs: ControlCollection.Count must be public. Fixed build of
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DomainUpDown.cs
index 43cd20725039c06f946228ab84ae514911166c43..0f29fdcc0bdf39bf9e24e90ff261faa9ad5590b2 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Copyright (c) 2004 Novell, Inc.
+// Copyright (c) 2005 Novell, Inc.
 //
 // Authors:
-//     Miguel de Icaza (miguel@novell.com).
+//     Jonathan Gilbert        <logic@deltaq.org>
 //
+// Integration into MWF:
+//     Peter Bartok            <pbartok@novell.com>
 //
 
 using System;
-using System.ComponentModel;
 using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
 
-namespace System.Windows.Forms {
-
-       [DefaultEvent ("SelectedItemChanged")]
-       [DefaultProperty ("Items")]
-       public class DomainUpDown : UpDownBase
+namespace System.Windows.Forms
+{
+       [DefaultProperty("Items")]
+       [DefaultEvent("SelectedItemChanged")]
 #if NET_2_0
-               , ISupportInitialize
+       [DefaultBindingProperty ("SelectedItem")]
+       [ClassInterface (ClassInterfaceType.AutoDispatch)]
+       [ComVisible (true)]
 #endif
-       {
-               public class DomainUpDownItemCollection : ArrayList {
-                       DomainUpDown owner;
-                       
-                       internal DomainUpDownItemCollection (DomainUpDown owner)
-                       {
-                               this.owner = owner;
+       public class DomainUpDown : UpDownBase {
+               #region Local Variables
+               private DomainUpDownItemCollection      items;
+               private int                             selected_index = -1;
+               private bool                            sorted;
+               private bool                            wrap;
+               private int                             typed_to_index = -1;
+               #endregion      // Local Variables
+
+               #region DomainUpDownAccessibleObject sub-class
+               [ComVisible(true)]
+               public class DomainItemAccessibleObject : AccessibleObject {
+                       #region DomainItemAccessibleObject Local Variables
+                       private AccessibleObject parent;
+                       #endregion      // DomainItemAccessibleObject Local Variables
+
+                       #region DomainItemAccessibleObject Constructors
+                       public DomainItemAccessibleObject(string name, AccessibleObject parent) {
+                               this.name = name;
+                               this.parent = parent;
                        }
+                       #endregion      // DomainItemAccessibleObject Constructors
 
-                       public override int Add (object item)
-                       {
-                               int ret = base.Add (item);
-                               if (owner.sorted){
-                                       Sort ();
+                       #region DomainItemAccessibleObject Properties
+                       public override string Name {
+                               get {
+                                       return base.Name;
+                               }
 
-                                       // Will trigger an update.
-                                       owner.SelectedIndex = owner.SelectedIndex;
+                               set {
+                                       base.Name = value;
                                }
+                       }
 
-                               return ret;
+                       public override AccessibleObject Parent {
+                               get {
+                                       return parent;
+                               }
                        }
 
-                       public override void Insert (int index, object item)
-                       {
-                               base.Insert (index, item);
-                               if (owner.sorted){
-                                       Sort ();
-                                       owner.SelectedIndex = owner.SelectedIndex;
-                               } else {
-                                       if (index == owner.SelectedIndex)
-                                               owner.UpdateEditText ();
+                       public override AccessibleRole Role {
+                               get {
+                                       return base.Role;
                                }
                        }
 
-                       public override void Remove (object item)
-                       {
-                               base.Remove (item);
-                               if (Count < owner.SelectedIndex)
-                                       owner.SelectedIndex -= 1;
-                               if (owner.sorted){
-                                       Sort ();
-                                       owner.UpdateEditText ();
+                       public override AccessibleStates State {
+                               get {
+                                       return base.State;
+                               }
+                       }
+
+                       public override string Value {
+                               get {
+                                       return base.Value;
                                }
                        }
+                       #endregion      // DomainItemAccessibleObject Properties
+               }
+               #endregion      // DomainItemAccessibleObject sub-class
 
-                       public override void RemoveAt (int item)
+               #region DomainUpDownAccessibleObject sub-class
+               [ComVisible(true)]
+               public class DomainUpDownAccessibleObject : ControlAccessibleObject {
+                       #region DomainUpDownAccessibleObject Local Variables
+                       //private Control       owner;
+                       #endregion      // DomainUpDownAccessibleObject Local Variables
+
+                       #region DomainUpDownAccessibleObject Constructors
+                       public DomainUpDownAccessibleObject(Control owner) : base(owner)
                        {
-                               base.RemoveAt (item);
+                               //this.owner = owner;
+                       }
+                       #endregion      // DomainUpDownAccessibleObject Constructors
 
-                               if (Count < owner.SelectedIndex)
-                                       owner.SelectedIndex -= 1;
-                               if (owner.SelectedIndex == item)
-                                       owner.UpdateEditText ();
-                               if (owner.sorted){
-                                       Sort ();
-                                       owner.UpdateEditText ();
+                       #region DomainUpDownAccessibleObject Properties
+                       public override AccessibleRole Role {
+                               get {
+                                       return base.Role;
                                }
                        }
+                       #endregion      // DomainUpDownAccessibleObject Properties
+
+                       #region DomainUpDownAccessibleObject Methods
+                       public override AccessibleObject GetChild(int index) {
+                               return base.GetChild (index);
+                       }
+
+                       public override int GetChildCount() {
+                               return base.GetChildCount ();
+                       }
+                       #endregion      // DomainUpDownAccessibleObject Methods
                }
+               #endregion      // DomainUpDownAccessibleObject sub-class
 
-               int selected_index = -1;
-               bool sorted = false;
-               bool wrap = false;
-               DomainUpDownItemCollection items;
+               #region DomainUpDownItemCollection sub-class
+               public class DomainUpDownItemCollection : ArrayList {
+                       internal ArrayList string_cache = new ArrayList();
 
-#if NET_2_0
-#region ISupportInitialize methods
-               
-               public void BeginInit ()
-               {
+                       #region Local Variables
+                       #endregion      // Local Variables
+
+                       #region Constructors
+                       internal DomainUpDownItemCollection() {}
+                       #endregion      // Constructors
+
+                       #region Public Instance Properties
+                       [Browsable(false)]
+                       [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+                       public override object this[int index] {
+                               get {
+                                       return base[index];
+                               }
+
+                               set {
+                                       if (value == null) {
+                                               throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
+                                       }
+
+                                       base[index] = value;
+                                       string_cache[index] = value.ToString();
+                                       OnCollectionChanged(index, 0);
+                               }
+                       }
+                       #endregion      // Public Instance Properties
+
+                       #region Public Instance Methods
+                       public override int Add(object value) {
+                               if (value == null)
+                                       throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
+
+                               int ret = base.Add(value);
+                               string_cache.Add(value.ToString());
+                               OnCollectionChanged(Count - 1, +1);
+                               return ret;
+                       }
+
+                       public override void Insert(int index, object value) {
+                               if (value == null)
+                                       throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
+
+                               base.Insert(index, value);
+                               string_cache.Insert(index, value.ToString());
+                               OnCollectionChanged(index, +1);
+                       }
+
+                       public override void Remove(object obj) {
+                               int index = IndexOf(obj);
+
+                               if (index >= 0)
+                                       RemoveAt(index);
+                       }
+
+                       public override void RemoveAt(int index) {
+                               base.RemoveAt(index);
+                               string_cache.RemoveAt(index);
+                               OnCollectionChanged(index, -1);
+                       }
+                       #endregion      // Public Instance Methods
+
+                       #region Internal Methods and Events
+                       internal void OnCollectionChanged(int index, int size_delta) {
+                               CollectionChangedEventHandler handler = CollectionChanged;
+
+                               if (handler != null) {
+                                       handler(index, size_delta);
+                               }
+                       }
+
+                       internal void PrivSort() {
+                               PrivSort(0, Count, Comparer.Default);
+                       }
+
+                       internal void PrivSort(int index, int count, IComparer comparer) {
+                               object[] base_items = null; // this will refer to the base ArrayList private _items member
+                               object[] string_cache_items = null; // and this will refer to that of the string_cache
+
+                               FieldInfo items_field = null;
+        
+                               try {
+                                       items_field = typeof(ArrayList).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
+                               }
+                               catch {} // security exceptions, perhaps...
+
+                               if (items_field != null) {
+                                       base_items = items_field.GetValue(this) as object[];
+                                       string_cache_items = items_field.GetValue(string_cache) as object[];
+                               }
+
+                               if ((base_items == null) || (string_cache_items == null)) {
+                                       // oh poop =/ guess we have to completely repopulate the string cache
+                                       base.Sort(index, count, comparer);
+
+                                       for (int i=0; i < count; i++)
+                                               string_cache[i + index] = base[i + index].ToString();
+                               }
+                               else {
+                                       // yay, this will be much faster than creating a whole bunch more items
+                                       Array.Sort(string_cache_items, base_items, index, count, comparer);
+
+                                       OnCollectionChanged(-1, 0);
+                               }
+                       }
+
+                       internal void PrivSort(IComparer comparer) {
+                               PrivSort(0, Count, comparer);
+                       }
+
+                       internal event CollectionChangedEventHandler CollectionChanged;
+                       #endregion      // Internal Methods and Events
                }
+               #endregion      // DomainUpDownItemCollection sub-class
+
+               #region Private Methods
+               // normally I'd use an EventArgs class, but I don't want to create spurious objects here
+               internal delegate void  CollectionChangedEventHandler(int index, int size_delta);
 
-               public void EndInit ()
-               {
+               internal void items_CollectionChanged(int index, int size_delta) {
+                       bool new_item = false;
+
+                       if ((index == selected_index) && (size_delta <= 0))
+                               new_item = true;
+                       else if (index <= selected_index)
+                               selected_index += size_delta;
+
+                       if (sorted && (index >= 0)) // index < 0 means it is already sorting
+                               items.PrivSort();
+
+                       UpdateEditText();
+
+                       if (new_item) {
+                               OnSelectedItemChanged(this, EventArgs.Empty);
+                       }
                }
-#endregion
-#endif
-               
-               public event EventHandler SelectedItemChanged;
-               
-               public DomainUpDown () : base () {
-                       items = new DomainUpDownItemCollection (this);
-               }
-               
-               public override void DownButton ()
-               {
-                       if (wrap)
-                               selected_index %= items.Count;
-                       else if (selected_index < items.Count)
-                               selected_index++;
-
-                       UpdateEditText ();
-               }
-
-               public override void UpButton ()
-               {
-                       if (wrap){
-                               selected_index--;
-                               if (selected_index == -1)
-                                       selected_index = items.Count-1;
-                       } else {
-                               if (selected_index > -1)
-                                       selected_index--;
-                       }
-                       UpdateEditText ();
-               }
-
-               public override void UpdateEditText ()
-               {
-                       ChangingText = true;
-                       if (selected_index == -1)
-                               Text = "";
-                       else
-                               Text = items [selected_index].ToString ();
+
+               void go_to_user_input() {
                        UserEdit = false;
+
+                       if (typed_to_index >= 0) {
+                               selected_index = typed_to_index;
+                               OnSelectedItemChanged(this, EventArgs.Empty);
+                       }
                }
 
-               protected override void OnChanged (object source, EventArgs e)
-               {
-                       OnSelectedItemChanged (source, e);
+               private void TextBoxLostFocus(object source, EventArgs e) {
+                       Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
                }
-               
-               protected void OnSelectedItemChanged (object source, EventArgs e)
-               {
-                       if (SelectedItemChanged != null)
-                               SelectedItemChanged (source, e);
+
+               private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
+                       if (!UserEdit) {
+                               base.txtView.SelectionLength = 0;
+                               typed_to_index = -1;
+                       }
+
+                       if (base.txtView.SelectionLength == 0) {
+                               base.txtView.SelectionStart = 0;
+                       }
+
+                       if (base.txtView.SelectionStart != 0) {
+                               return;
+                       }
+
+                       if (e.KeyChar == '\b') { // backspace
+                               if (base.txtView.SelectionLength > 0) {
+                                       string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
+
+                                       bool found = false;
+
+                                       if (typed_to_index < 0) {
+                                               typed_to_index = 0;
+                                       }
+
+                                       if (sorted) {
+                                               for (int i=typed_to_index; i >= 0; i--) {
+                                                       int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
+
+                                                       if (difference == 0) {
+                                                               found = true;
+                                                               typed_to_index = i;
+                                                       }
+
+                                                       if (difference > 0) { // since it is sorted, no strings after this point will match
+                                                               break;
+                                                       }
+                                               }
+                                       } else {
+                                               for (int i=0; i < items.Count; i++) {
+                                                       if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
+                                                               found = true;
+                                                               typed_to_index = i;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+
+                                       ChangingText = true;
+
+                                       if (found)
+                                               Text = items.string_cache[typed_to_index].ToString();
+                                       else
+                                               Text = prefix;
+
+                                       Select(0, prefix.Length);
+
+                                       UserEdit = true;
+
+                                       e.Handled = true;
+                               }
+                       }
+                       else {
+                               char key_char = e.KeyChar;
+
+                               if (char.IsLetterOrDigit(key_char)
+                                       || char.IsNumber(key_char)
+                                       || char.IsPunctuation(key_char)
+                                       || char.IsSymbol(key_char)
+                                       || char.IsWhiteSpace(key_char)) {
+                                       string prefix = base.txtView.SelectedText + key_char;
+
+                                       bool found = false;
+
+                                       if (typed_to_index < 0) {
+                                               typed_to_index = 0;
+                                       }
+
+                                       if (sorted) {
+                                               for (int i=typed_to_index; i < items.Count; i++) {
+                                                       int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
+
+                                                       if (difference == 0) {
+                                                               found = true;
+                                                               typed_to_index = i;
+                                                       }
+
+                                                       if (difference <= 0) { // since it is sorted, no strings after this point will match
+                                                               break;
+                                                       }
+                                               }
+                                       } else {
+                                               for (int i=0; i < items.Count; i++) {
+                                                       if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
+                                                               found = true;
+                                                               typed_to_index = i;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+
+                                       ChangingText = true;
+
+                                       if (found) {
+                                               Text = items.string_cache[typed_to_index].ToString();
+                                       } else {
+                                               Text = prefix;
+                                       }
+
+                                       Select(0, prefix.Length);
+
+                                       UserEdit = true;
+
+                                       e.Handled = true;
+                               }
+                       }
                }
-               
-               protected override AccessibleObject CreateAccessibilityInstance ()
-               {
-                       AccessibleObject ao;
-                       
-                       ao = base.CreateAccessibilityInstance ();
-                       ao.description = "DomainUpDown";
+               #endregion      // Private Methods
 
-                       return ao;
+               #region Public Constructors
+               public DomainUpDown() {
+                       selected_index = -1;
+                       sorted = false;
+                       wrap = false;
+                       typed_to_index = -1;
+
+                       items = new DomainUpDownItemCollection();
+                       items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
+
+                       this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
+                       this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
                }
+               #endregion      // Public Constructors
 
+               #region Public Instance Properties
                [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+               [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
                [Localizable(true)]
                public DomainUpDownItemCollection Items {
                        get {
@@ -182,38 +434,39 @@ namespace System.Windows.Forms {
                        }
                }
 
-               [DefaultValue(-1)]
                [Browsable(false)]
+               [DefaultValue(-1)]
                public int SelectedIndex {
                        get {
                                return selected_index;
                        }
-
                        set {
-                               if (value < -1 || value > items.Count)
-                                       throw new ArgumentException (String.Format ("Boundaries are -1 to {0}", items.Count-1));
+                               object before = (selected_index >= 0) ? items[selected_index] : null;
 
                                selected_index = value;
-                               UpdateEditText ();
+                               UpdateEditText();
+
+                               object after = (selected_index >= 0) ? items[selected_index] : null;
+
+                               if (!ReferenceEquals(before, after)) {
+                                       OnSelectedItemChanged(this, EventArgs.Empty);
+                               }
                        }
                }
 
-               [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
                [Browsable(false)]
+               [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
                public object SelectedItem {
                        get {
-                               if (selected_index == -1)
+                               if (selected_index >= 0) {
+                                       return items[selected_index];
+                               } else {
                                        return null;
-
-                               return items [selected_index];
+                               }
                        }
 
                        set {
-                               for (int i = 0; i < items.Count; i++)
-                                       if (items [i] == value){
-                                               SelectedIndex = i;
-                                               break;
-                                       }
+                               SelectedIndex = items.IndexOf(value);
                        }
                }
 
@@ -222,33 +475,104 @@ namespace System.Windows.Forms {
                        get {
                                return sorted;
                        }
-
                        set {
-                               //
-                               // It never returns to unsorted state
-                               //
-
                                sorted = value;
-                               
+
                                if (sorted)
-                                       items.Sort ();
+                                       items.PrivSort();
                        }
                }
 
-               [DefaultValue (false)]
+               [DefaultValue(false)]
+               [Localizable(true)]
                public bool Wrap {
                        get {
                                return wrap;
                        }
-
                        set {
                                wrap = value;
                        }
                }
+               #endregion      // Public Instance Properties
+
+               #region Public Instance Methods
+               public override void DownButton() {
+                       if (UserEdit)
+                               go_to_user_input();
+
+                       int new_index = selected_index + 1;
+
+                       if (new_index >= items.Count) {
+                               if (!wrap)
+                                       return;
+
+                               new_index = 0;
+                       }
+
+                       SelectedIndex = new_index;
+               }
+
+               public override string ToString() {
+                       return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
+               }
+
+               public override void UpButton() {
+                       if (UserEdit)
+                               go_to_user_input();
+
+                       int new_index = selected_index - 1;
+
+                       if (new_index < 0) {
+                               if (!wrap) {
+                                       return;
+                               }
+
+                               new_index = items.Count - 1;
+                       }
+
+                       SelectedIndex = new_index;
+               }
+               #endregion      // Public Instance Methods
+
+               #region Protected Instance Methods
+               protected override AccessibleObject CreateAccessibilityInstance() {
+                       AccessibleObject        acc;
+
+                       acc = new AccessibleObject(this);
+                       acc.role = AccessibleRole.SpinButton;
+
+                       return acc;
+               }
+
+               protected override void OnChanged(object source, EventArgs e) {
+                       base.OnChanged (source, e);
+               }
+
+               protected void OnSelectedItemChanged(object source, EventArgs e) {
+                       EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
+                       if (eh != null)
+                               eh (this, e);
+               }
+
+               protected override void UpdateEditText() {
+                       if ((selected_index >= 0) && (selected_index < items.Count)) {
+                               ChangingText = true;
+                               Text = items.string_cache[selected_index].ToString();
+                       }
+               }
+
+               protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
+                       base.OnTextBoxKeyDown (source, e);
+               }
+
+               #endregion      // Protected Instance Methods
 
-               public override string ToString ()
-               {
-                       return String.Format ("{0} Count={1} Selected={2}", base.ToString (), items.Count, selected_index);
+               #region Events
+               static object SelectedItemChangedEvent = new object ();
+               public event EventHandler SelectedItemChanged {
+                       add { Events.AddHandler (SelectedItemChangedEvent, value); }
+                       remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }
                }
+               #endregion      // Events
        }
 }