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
277 OnSelectedItemChanged(this, EventArgs.Empty);
281 void go_to_user_input() {
284 if (typed_to_index >= 0) {
285 selected_index = typed_to_index;
286 OnSelectedItemChanged(this, EventArgs.Empty);
290 private void TextBoxLostFocus(object source, EventArgs e) {
291 Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
294 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
296 base.txtView.SelectionLength = 0;
300 if (base.txtView.SelectionLength == 0) {
301 base.txtView.SelectionStart = 0;
304 if (base.txtView.SelectionStart != 0) {
308 if (e.KeyChar == '\b') { // backspace
309 if (base.txtView.SelectionLength > 0) {
310 string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
314 if (typed_to_index < 0) {
319 for (int i=typed_to_index; i >= 0; i--) {
320 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
322 if (difference == 0) {
327 if (difference > 0) { // since it is sorted, no strings after this point will match
332 for (int i=0; i < items.Count; i++) {
333 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
344 Text = items.string_cache[typed_to_index].ToString();
348 Select(0, prefix.Length);
356 char key_char = e.KeyChar;
358 if (char.IsLetterOrDigit(key_char)
359 || char.IsNumber(key_char)
360 || char.IsPunctuation(key_char)
361 || char.IsSymbol(key_char)
362 || char.IsWhiteSpace(key_char)) {
363 string prefix = base.txtView.SelectedText + key_char;
367 if (typed_to_index < 0) {
372 for (int i=typed_to_index; i < items.Count; i++) {
373 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
375 if (difference == 0) {
380 if (difference <= 0) { // since it is sorted, no strings after this point will match
385 for (int i=0; i < items.Count; i++) {
386 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
397 Text = items.string_cache[typed_to_index].ToString();
402 Select(0, prefix.Length);
410 #endregion // Private Methods
412 #region Public Constructors
413 public DomainUpDown() {
419 items = new DomainUpDownItemCollection();
420 items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
422 this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
423 this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
425 #endregion // Public Constructors
427 #region Public Instance Properties
428 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
429 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
431 public DomainUpDownItemCollection Items {
439 public int SelectedIndex {
441 return selected_index;
444 object before = (selected_index >= 0) ? items[selected_index] : null;
446 selected_index = value;
449 object after = (selected_index >= 0) ? items[selected_index] : null;
451 if (!ReferenceEquals(before, after)) {
452 OnSelectedItemChanged(this, EventArgs.Empty);
458 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
459 public object SelectedItem {
461 if (selected_index >= 0) {
462 return items[selected_index];
469 SelectedIndex = items.IndexOf(value);
473 [DefaultValue(false)]
486 [DefaultValue(false)]
496 #endregion // Public Instance Properties
498 #region Public Instance Methods
499 public override void DownButton() {
503 int new_index = selected_index + 1;
505 if (new_index >= items.Count) {
512 SelectedIndex = new_index;
515 public override string ToString() {
516 return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
519 public override void UpButton() {
523 int new_index = selected_index - 1;
530 new_index = items.Count - 1;
533 SelectedIndex = new_index;
535 #endregion // Public Instance Methods
537 #region Protected Instance Methods
538 protected override AccessibleObject CreateAccessibilityInstance() {
539 AccessibleObject acc;
541 acc = new AccessibleObject(this);
542 acc.role = AccessibleRole.SpinButton;
547 protected override void OnChanged(object source, EventArgs e) {
548 base.OnChanged (source, e);
551 protected void OnSelectedItemChanged(object source, EventArgs e) {
552 EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
557 protected override void UpdateEditText() {
558 if ((selected_index >= 0) && (selected_index < items.Count)) {
560 Text = items.string_cache[selected_index].ToString();
564 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
565 base.OnTextBoxKeyDown (source, e);
568 #endregion // Protected Instance Methods
571 static object SelectedItemChangedEvent = new object ();
572 public event EventHandler SelectedItemChanged {
573 add { Events.AddHandler (SelectedItemChangedEvent, value); }
574 remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }