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>
32 using System.Collections;
33 using System.ComponentModel;
35 using System.Reflection;
36 using System.Runtime.InteropServices;
37 using System.Windows.Forms;
39 namespace System.Windows.Forms
41 [DefaultProperty("Items")]
42 [DefaultEvent("SelectedItemChanged")]
43 public class DomainUpDown : UpDownBase {
44 #region Local Variables
45 private DomainUpDownItemCollection items;
46 private int selected_index = -1;
49 private int typed_to_index = -1;
50 #endregion // Local Variables
52 #region DomainUpDownAccessibleObject sub-class
54 public class DomainItemAccessibleObject : AccessibleObject {
55 #region DomainItemAccessibleObject Local Variables
56 private AccessibleObject parent;
57 #endregion // DomainItemAccessibleObject Local Variables
59 #region DomainItemAccessibleObject Constructors
60 public DomainItemAccessibleObject(string name, AccessibleObject parent) {
64 #endregion // DomainItemAccessibleObject Constructors
66 #region DomainItemAccessibleObject Properties
67 public override string Name {
77 public override AccessibleObject Parent {
83 public override AccessibleRole Role {
89 public override AccessibleStates State {
95 public override string Value {
100 #endregion // DomainItemAccessibleObject Properties
102 #endregion // DomainItemAccessibleObject sub-class
104 #region DomainUpDownAccessibleObject sub-class
106 public class DomainUpDownAccessibleObject : ControlAccessibleObject {
107 #region DomainUpDownAccessibleObject Local Variables
108 private Control owner;
109 #endregion // DomainUpDownAccessibleObject Local Variables
111 #region DomainUpDownAccessibleObject Constructors
112 public DomainUpDownAccessibleObject(Control owner) : base(owner) {
115 #endregion // DomainUpDownAccessibleObject Constructors
117 #region DomainUpDownAccessibleObject Properties
118 public override AccessibleRole Role {
123 #endregion // DomainUpDownAccessibleObject Properties
125 #region DomainUpDownAccessibleObject Methods
126 public override AccessibleObject GetChild(int index) {
127 return base.GetChild (index);
130 public override int GetChildCount() {
131 return base.GetChildCount ();
133 #endregion // DomainUpDownAccessibleObject Methods
135 #endregion // DomainUpDownAccessibleObject sub-class
137 #region DomainUpDownItemCollection sub-class
138 public class DomainUpDownItemCollection : ArrayList {
139 internal ArrayList string_cache = new ArrayList();
141 #region Local Variables
142 #endregion // Local Variables
145 internal DomainUpDownItemCollection() {}
146 #endregion // Constructors
148 #region Public Instance Properties
150 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
151 public override object this[int index] {
158 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
162 string_cache[index] = value.ToString();
163 OnCollectionChanged(index, 0);
166 #endregion // Public Instance Properties
168 #region Public Instance Methods
169 public override int Add(object value) {
171 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
173 int ret = base.Add(value);
174 string_cache.Add(value.ToString());
175 OnCollectionChanged(Count - 1, +1);
179 public override void Insert(int index, object value) {
181 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
183 base.Insert(index, value);
184 string_cache.Insert(index, value.ToString());
185 OnCollectionChanged(index, +1);
188 public override void Remove(object obj) {
189 int index = IndexOf(obj);
195 public override void RemoveAt(int index) {
196 base.RemoveAt(index);
197 string_cache.RemoveAt(index);
198 OnCollectionChanged(index, -1);
200 #endregion // Public Instance Methods
202 #region Internal Methods and Events
203 internal void OnCollectionChanged(int index, int size_delta) {
204 CollectionChangedEventHandler handler = CollectionChanged;
206 if (handler != null) {
207 handler(index, size_delta);
211 internal void PrivSort() {
212 PrivSort(0, Count, Comparer.Default);
215 internal void PrivSort(int index, int count, IComparer comparer) {
216 object[] base_items = null; // this will refer to the base ArrayList private _items member
217 object[] string_cache_items = null; // and this will refer to that of the string_cache
219 FieldInfo items_field = null;
222 items_field = typeof(ArrayList).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
224 catch {} // security exceptions, perhaps...
226 if (items_field != null) {
227 base_items = items_field.GetValue(this) as object[];
228 string_cache_items = items_field.GetValue(string_cache) as object[];
231 if ((base_items == null) || (string_cache_items == null)) {
232 // oh poop =/ guess we have to completely repopulate the string cache
233 base.Sort(index, count, comparer);
235 for (int i=0; i < count; i++)
236 string_cache[i + index] = base[i + index].ToString();
239 // yay, this will be much faster than creating a whole bunch more items
240 Array.Sort(string_cache_items, base_items, index, count, comparer);
242 OnCollectionChanged(-1, 0);
246 internal void PrivSort(IComparer comparer) {
247 PrivSort(0, Count, comparer);
250 internal event CollectionChangedEventHandler CollectionChanged;
251 #endregion // Internal Methods and Events
253 #endregion // DomainUpDownItemCollection sub-class
255 #region Private Methods
256 // normally I'd use an EventArgs class, but I don't want to create spurious objects here
257 internal delegate void CollectionChangedEventHandler(int index, int size_delta);
259 internal void items_CollectionChanged(int index, int size_delta) {
260 bool new_item = false;
262 if ((index == selected_index) && (size_delta <= 0))
264 else if (index <= selected_index)
265 selected_index += size_delta;
267 if (sorted && (index >= 0)) // index < 0 means it is already sorting
273 OnSelectedItemChanged(this, EventArgs.Empty);
277 void go_to_user_input() {
280 if (typed_to_index >= 0) {
281 selected_index = typed_to_index;
282 OnSelectedItemChanged(this, EventArgs.Empty);
286 private void TextBoxLostFocus(object source, EventArgs e) {
287 Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
290 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
292 base.txtView.SelectionLength = 0;
296 if (base.txtView.SelectionLength == 0) {
297 base.txtView.SelectionStart = 0;
300 if (base.txtView.SelectionStart != 0) {
304 if (e.KeyChar == '\b') { // backspace
305 if (base.txtView.SelectionLength > 0) {
306 string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
310 if (typed_to_index < 0) {
315 for (int i=typed_to_index; i >= 0; i--) {
316 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
318 if (difference == 0) {
323 if (difference > 0) { // since it is sorted, no strings after this point will match
328 for (int i=0; i < items.Count; i++) {
329 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
340 Text = items.string_cache[typed_to_index].ToString();
344 Select(0, prefix.Length);
352 char key_char = e.KeyChar;
354 if (char.IsLetterOrDigit(key_char)
355 || char.IsNumber(key_char)
356 || char.IsPunctuation(key_char)
357 || char.IsSymbol(key_char)
358 || char.IsWhiteSpace(key_char)) {
359 string prefix = base.txtView.SelectedText + key_char;
363 if (typed_to_index < 0) {
368 for (int i=typed_to_index; i < items.Count; i++) {
369 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
371 if (difference == 0) {
376 if (difference <= 0) { // since it is sorted, no strings after this point will match
381 for (int i=0; i < items.Count; i++) {
382 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
393 Text = items.string_cache[typed_to_index].ToString();
398 Select(0, prefix.Length);
406 #endregion // Private Methods
408 #region Public Constructors
409 public DomainUpDown() {
415 items = new DomainUpDownItemCollection();
416 items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
418 this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
419 this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
421 #endregion // Public Constructors
423 #region Public Instance Properties
424 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
425 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
427 public DomainUpDownItemCollection Items {
435 public int SelectedIndex {
437 return selected_index;
440 object before = (selected_index >= 0) ? items[selected_index] : null;
442 selected_index = value;
445 object after = (selected_index >= 0) ? items[selected_index] : null;
447 if (!ReferenceEquals(before, after)) {
448 OnSelectedItemChanged(this, EventArgs.Empty);
454 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
455 public object SelectedItem {
457 if (selected_index >= 0) {
458 return items[selected_index];
465 SelectedIndex = items.IndexOf(value);
469 [DefaultValue(false)]
482 [DefaultValue(false)]
492 #endregion // Public Instance Properties
494 #region Public Instance Methods
495 public override void DownButton() {
499 int new_index = selected_index + 1;
501 if (new_index >= items.Count) {
508 SelectedIndex = new_index;
511 public override string ToString() {
512 return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
515 public override void UpButton() {
519 int new_index = selected_index - 1;
526 new_index = items.Count - 1;
529 SelectedIndex = new_index;
531 #endregion // Public Instance Methods
533 #region Protected Instance Methods
534 protected override AccessibleObject CreateAccessibilityInstance() {
535 AccessibleObject acc;
537 acc = new AccessibleObject(this);
538 acc.role = AccessibleRole.SpinButton;
543 protected override void OnChanged(object source, EventArgs e) {
544 base.OnChanged (source, e);
547 protected void OnSelectedItemChanged(object source, EventArgs e) {
548 EventHandler handler = SelectedItemChanged;
554 protected override void UpdateEditText() {
555 if ((selected_index >= 0) && (selected_index < items.Count)) {
557 Text = items.string_cache[selected_index].ToString();
561 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
562 base.OnTextBoxKeyDown (source, e);
565 #endregion // Protected Instance Methods
568 public event EventHandler SelectedItemChanged;