// 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 {
}
}
- [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);
}
}
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
}
}