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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc.
23 // Jonathan Gilbert <logic@deltaq.org>
25 // Integration into MWF:
26 // Peter Bartok <pbartok@novell.com>
30 using System.Collections;
31 using System.ComponentModel;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
35 using System.Windows.Forms;
37 namespace System.Windows.Forms
39 [DefaultProperty("Items")]
40 [DefaultEvent("SelectedItemChanged")]
42 [DefaultBindingProperty ("SelectedItem")]
43 [ClassInterface (ClassInterfaceType.AutoDispatch)]
46 public class DomainUpDown : UpDownBase {
47 #region Local Variables
48 private DomainUpDownItemCollection items;
49 private int selected_index = -1;
52 private int typed_to_index = -1;
53 #endregion // Local Variables
55 #region DomainUpDownAccessibleObject sub-class
57 public class DomainItemAccessibleObject : AccessibleObject {
58 #region DomainItemAccessibleObject Local Variables
59 private AccessibleObject parent;
60 #endregion // DomainItemAccessibleObject Local Variables
62 #region DomainItemAccessibleObject Constructors
63 public DomainItemAccessibleObject(string name, AccessibleObject parent) {
67 #endregion // DomainItemAccessibleObject Constructors
69 #region DomainItemAccessibleObject Properties
70 public override string Name {
80 public override AccessibleObject Parent {
86 public override AccessibleRole Role {
92 public override AccessibleStates State {
98 public override string Value {
103 #endregion // DomainItemAccessibleObject Properties
105 #endregion // DomainItemAccessibleObject sub-class
107 #region DomainUpDownAccessibleObject sub-class
109 public class DomainUpDownAccessibleObject : ControlAccessibleObject {
110 #region DomainUpDownAccessibleObject Local Variables
111 //private Control owner;
112 #endregion // DomainUpDownAccessibleObject Local Variables
114 #region DomainUpDownAccessibleObject Constructors
115 public DomainUpDownAccessibleObject(Control owner) : base(owner)
117 //this.owner = owner;
119 #endregion // DomainUpDownAccessibleObject Constructors
121 #region DomainUpDownAccessibleObject Properties
122 public override AccessibleRole Role {
127 #endregion // DomainUpDownAccessibleObject Properties
129 #region DomainUpDownAccessibleObject Methods
130 public override AccessibleObject GetChild(int index) {
131 return base.GetChild (index);
134 public override int GetChildCount() {
135 return base.GetChildCount ();
137 #endregion // DomainUpDownAccessibleObject Methods
139 #endregion // DomainUpDownAccessibleObject sub-class
141 #region DomainUpDownItemCollection sub-class
142 public class DomainUpDownItemCollection : ArrayList {
143 internal ArrayList string_cache = new ArrayList();
145 #region Local Variables
146 #endregion // Local Variables
149 internal DomainUpDownItemCollection() {}
150 #endregion // Constructors
152 #region Public Instance Properties
154 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
155 public override object this[int index] {
162 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
166 string_cache[index] = value.ToString();
167 OnCollectionChanged(index, 0);
170 #endregion // Public Instance Properties
172 #region Public Instance Methods
173 public override int Add(object value) {
175 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
177 int ret = base.Add(value);
178 string_cache.Add(value.ToString());
179 OnCollectionChanged(Count - 1, +1);
183 public override void Insert(int index, object value) {
185 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
187 base.Insert(index, value);
188 string_cache.Insert(index, value.ToString());
189 OnCollectionChanged(index, +1);
192 public override void Remove(object obj) {
193 int index = IndexOf(obj);
199 public override void RemoveAt(int index) {
200 base.RemoveAt(index);
201 string_cache.RemoveAt(index);
202 OnCollectionChanged(index, -1);
204 #endregion // Public Instance Methods
206 #region Internal Methods and Events
207 internal void OnCollectionChanged(int index, int size_delta) {
208 CollectionChangedEventHandler handler = CollectionChanged;
210 if (handler != null) {
211 handler(index, size_delta);
215 internal void PrivSort() {
216 PrivSort(0, Count, Comparer.Default);
219 internal void PrivSort(int index, int count, IComparer comparer) {
220 object[] base_items = null; // this will refer to the base ArrayList private _items member
221 object[] string_cache_items = null; // and this will refer to that of the string_cache
223 FieldInfo items_field = null;
226 items_field = typeof(ArrayList).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
228 catch {} // security exceptions, perhaps...
230 if (items_field != null) {
231 base_items = items_field.GetValue(this) as object[];
232 string_cache_items = items_field.GetValue(string_cache) as object[];
235 if ((base_items == null) || (string_cache_items == null)) {
236 // oh poop =/ guess we have to completely repopulate the string cache
237 base.Sort(index, count, comparer);
239 for (int i=0; i < count; i++)
240 string_cache[i + index] = base[i + index].ToString();
243 // yay, this will be much faster than creating a whole bunch more items
244 Array.Sort(string_cache_items, base_items, index, count, comparer);
246 OnCollectionChanged(-1, 0);
250 internal void PrivSort(IComparer comparer) {
251 PrivSort(0, Count, comparer);
254 internal event CollectionChangedEventHandler CollectionChanged;
255 #endregion // Internal Methods and Events
257 #endregion // DomainUpDownItemCollection sub-class
259 #region Private Methods
260 // normally I'd use an EventArgs class, but I don't want to create spurious objects here
261 internal delegate void CollectionChangedEventHandler(int index, int size_delta);
263 internal void items_CollectionChanged(int index, int size_delta) {
264 bool new_item = false;
266 if ((index == selected_index) && (size_delta <= 0))
268 else if (index <= selected_index)
269 selected_index += size_delta;
271 if (sorted && (index >= 0)) // index < 0 means it is already sorting
274 // XXX this might be wrong - it might be an explict 'Text = ...' assignment.
278 OnSelectedItemChanged(this, EventArgs.Empty);
282 void go_to_user_input() {
285 if (typed_to_index >= 0) {
286 selected_index = typed_to_index;
287 OnSelectedItemChanged(this, EventArgs.Empty);
291 private void TextBoxLostFocus(object source, EventArgs e) {
292 Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
295 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
297 base.txtView.SelectionLength = 0;
301 if (base.txtView.SelectionLength == 0) {
302 base.txtView.SelectionStart = 0;
305 if (base.txtView.SelectionStart != 0) {
309 if (e.KeyChar == '\b') { // backspace
310 if (base.txtView.SelectionLength > 0) {
311 string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
315 if (typed_to_index < 0) {
320 for (int i=typed_to_index; i >= 0; i--) {
321 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
323 if (difference == 0) {
328 if (difference > 0) { // since it is sorted, no strings after this point will match
333 for (int i=0; i < items.Count; i++) {
334 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
345 Text = items.string_cache[typed_to_index].ToString();
349 Select(0, prefix.Length);
357 char key_char = e.KeyChar;
359 if (char.IsLetterOrDigit(key_char)
360 || char.IsNumber(key_char)
361 || char.IsPunctuation(key_char)
362 || char.IsSymbol(key_char)
363 || char.IsWhiteSpace(key_char)) {
364 string prefix = base.txtView.SelectedText + key_char;
368 if (typed_to_index < 0) {
373 for (int i=typed_to_index; i < items.Count; i++) {
374 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
376 if (difference == 0) {
381 if (difference <= 0) { // since it is sorted, no strings after this point will match
386 for (int i=0; i < items.Count; i++) {
387 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
398 Text = items.string_cache[typed_to_index].ToString();
403 Select(0, prefix.Length);
411 #endregion // Private Methods
413 #region Public Constructors
414 public DomainUpDown() {
420 items = new DomainUpDownItemCollection();
421 items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
423 this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
424 this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
428 #endregion // Public Constructors
430 #region Public Instance Properties
431 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
432 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
434 public DomainUpDownItemCollection Items {
442 public int SelectedIndex {
444 return selected_index;
447 object before = (selected_index >= 0) ? items[selected_index] : null;
449 selected_index = value;
452 object after = (selected_index >= 0) ? items[selected_index] : null;
454 if (!ReferenceEquals(before, after)) {
455 OnSelectedItemChanged(this, EventArgs.Empty);
461 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
462 public object SelectedItem {
464 if (selected_index >= 0) {
465 return items[selected_index];
472 SelectedIndex = items.IndexOf(value);
476 [DefaultValue(false)]
489 [DefaultValue(false)]
499 #endregion // Public Instance Properties
501 #region Public Instance Methods
502 public override void DownButton() {
506 int new_index = selected_index + 1;
508 if (new_index >= items.Count) {
515 SelectedIndex = new_index;
518 public override string ToString() {
519 return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
522 public override void UpButton() {
526 int new_index = selected_index - 1;
533 new_index = items.Count - 1;
536 SelectedIndex = new_index;
538 #endregion // Public Instance Methods
540 #region Protected Instance Methods
541 protected override AccessibleObject CreateAccessibilityInstance() {
542 AccessibleObject acc;
544 acc = new AccessibleObject(this);
545 acc.role = AccessibleRole.SpinButton;
550 protected override void OnChanged(object source, EventArgs e) {
551 base.OnChanged (source, e);
554 protected void OnSelectedItemChanged(object source, EventArgs e) {
555 EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
560 protected override void UpdateEditText() {
561 if ((selected_index >= 0) && (selected_index < items.Count)) {
563 Text = items.string_cache[selected_index].ToString();
567 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
568 base.OnTextBoxKeyDown (source, e);
571 #endregion // Protected Instance Methods
574 static object SelectedItemChangedEvent = new object ();
575 public event EventHandler SelectedItemChanged {
576 add { Events.AddHandler (SelectedItemChangedEvent, value); }
577 remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }