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")]
41 [DefaultBindingProperty ("SelectedItem")]
42 [ClassInterface (ClassInterfaceType.AutoDispatch)]
44 public class DomainUpDown : UpDownBase {
45 #region Local Variables
46 private DomainUpDownItemCollection items;
47 private int selected_index = -1;
50 private int typed_to_index = -1;
51 #endregion // Local Variables
53 #region DomainUpDownAccessibleObject sub-class
55 public class DomainItemAccessibleObject : AccessibleObject {
56 #region DomainItemAccessibleObject Local Variables
57 private AccessibleObject parent;
58 #endregion // DomainItemAccessibleObject Local Variables
60 #region DomainItemAccessibleObject Constructors
61 public DomainItemAccessibleObject(string name, AccessibleObject parent) {
65 #endregion // DomainItemAccessibleObject Constructors
67 #region DomainItemAccessibleObject Properties
68 public override string Name {
78 public override AccessibleObject Parent {
84 public override AccessibleRole Role {
90 public override AccessibleStates State {
96 public override string Value {
101 #endregion // DomainItemAccessibleObject Properties
103 #endregion // DomainItemAccessibleObject sub-class
105 #region DomainUpDownAccessibleObject sub-class
107 public class DomainUpDownAccessibleObject : ControlAccessibleObject {
108 #region DomainUpDownAccessibleObject Local Variables
109 //private Control owner;
110 #endregion // DomainUpDownAccessibleObject Local Variables
112 #region DomainUpDownAccessibleObject Constructors
113 public DomainUpDownAccessibleObject(Control owner) : base(owner)
115 //this.owner = owner;
117 #endregion // DomainUpDownAccessibleObject Constructors
119 #region DomainUpDownAccessibleObject Properties
120 public override AccessibleRole Role {
125 #endregion // DomainUpDownAccessibleObject Properties
127 #region DomainUpDownAccessibleObject Methods
128 public override AccessibleObject GetChild(int index) {
129 return base.GetChild (index);
132 public override int GetChildCount() {
133 return base.GetChildCount ();
135 #endregion // DomainUpDownAccessibleObject Methods
137 #endregion // DomainUpDownAccessibleObject sub-class
139 #region DomainUpDownItemCollection sub-class
140 public class DomainUpDownItemCollection : 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 OnCollectionChanged(index, 0);
165 #endregion // Public Instance Properties
167 #region Public Instance Methods
168 public override int Add(object item) {
170 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
172 int ret = base.Add(item);
173 OnCollectionChanged(Count - 1, +1);
177 public override void Insert(int index, object item) {
179 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
181 base.Insert(index, item);
182 OnCollectionChanged(index, +1);
185 public override void Remove(object item) {
186 int index = IndexOf(item);
192 public override void RemoveAt(int item) {
194 OnCollectionChanged(item, -1);
196 #endregion // Public Instance Methods
198 #region Internal Methods and Events
199 internal void OnCollectionChanged(int index, int size_delta) {
200 CollectionChangedEventHandler handler = CollectionChanged;
202 if (handler != null) {
203 handler(index, size_delta);
207 internal void PrivSort()
209 base.Sort (new ToStringSorter ());
212 private class ToStringSorter : IComparer
214 public int Compare (object x, object y)
216 return string.Compare (x.ToString (), y.ToString ());
220 internal event CollectionChangedEventHandler CollectionChanged;
221 #endregion // Internal Methods and Events
223 #endregion // DomainUpDownItemCollection sub-class
225 #region Private Methods
226 // normally I'd use an EventArgs class, but I don't want to create spurious objects here
227 internal delegate void CollectionChangedEventHandler(int index, int size_delta);
229 internal void items_CollectionChanged(int index, int size_delta) {
230 bool new_item = false;
232 if ((index == selected_index) && (size_delta <= 0))
234 else if (index <= selected_index)
235 selected_index += size_delta;
237 if (sorted && (index >= 0)) // index < 0 means it is already sorting
240 // XXX this might be wrong - it might be an explict 'Text = ...' assignment.
244 OnSelectedItemChanged(this, EventArgs.Empty);
248 void go_to_user_input() {
251 if (typed_to_index >= 0) {
252 selected_index = typed_to_index;
253 OnSelectedItemChanged(this, EventArgs.Empty);
257 private void TextBoxLostFocus(object source, EventArgs e) {
258 Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
261 int SearchTextWithPrefix (char key_char)
263 string prefix = key_char.ToString ();
266 start_index = selected_index == -1 ? 0 : selected_index;
267 i = selected_index == -1 || selected_index + 1 >= items.Count ? 0 : start_index + 1;
270 string item_text = items [i].ToString ();
271 if (String.Compare (prefix, 0, item_text, 0, 1, true) == 0)
274 if (i + 1 >= items.Count)
279 if (i == start_index)
286 bool IsValidInput (char key_char)
288 return Char.IsLetterOrDigit (key_char)
289 || Char.IsNumber (key_char)
290 || Char.IsPunctuation (key_char)
291 || Char.IsSymbol (key_char)
292 || Char.IsWhiteSpace (key_char);
295 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
297 char key_char = e.KeyChar;
298 if (IsValidInput (key_char) && items.Count > 0) {
299 int idx = SearchTextWithPrefix (key_char);
310 base.txtView.SelectionLength = 0;
314 if (base.txtView.SelectionLength == 0) {
315 base.txtView.SelectionStart = 0;
318 if (base.txtView.SelectionStart != 0) {
322 if (e.KeyChar == '\b') { // backspace
323 if (base.txtView.SelectionLength > 0) {
324 string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
328 if (typed_to_index < 0) {
333 for (int i=typed_to_index; i >= 0; i--) {
334 int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
336 if (difference == 0) {
341 if (difference > 0) { // since it is sorted, no strings after this point will match
346 for (int i=0; i < items.Count; i++) {
347 if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
358 Text = items[typed_to_index].ToString();
362 Select(0, prefix.Length);
370 char key_char = e.KeyChar;
372 if (IsValidInput (key_char)) {
373 string prefix = base.txtView.SelectedText + key_char;
377 if (typed_to_index < 0) {
382 for (int i=typed_to_index; i < items.Count; i++) {
383 int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
385 if (difference == 0) {
390 if (difference <= 0) { // since it is sorted, no strings after this point will match
395 for (int i=0; i < items.Count; i++) {
396 if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
407 Text = items[typed_to_index].ToString();
412 Select(0, prefix.Length);
420 #endregion // Private Methods
422 #region Public Constructors
423 public DomainUpDown() {
429 items = new DomainUpDownItemCollection();
430 items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
432 this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
433 this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
437 #endregion // Public Constructors
439 #region Public Instance Properties
440 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
441 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
443 public DomainUpDownItemCollection Items {
450 [EditorBrowsable (EditorBrowsableState.Never)]
451 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
452 public new Padding Padding {
453 get { return Padding.Empty; }
459 public int SelectedIndex {
461 return selected_index;
464 object before = (selected_index >= 0) ? items[selected_index] : null;
466 selected_index = value;
469 object after = (selected_index >= 0) ? items[selected_index] : null;
471 if (!ReferenceEquals(before, after)) {
472 OnSelectedItemChanged(this, EventArgs.Empty);
478 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
479 public object SelectedItem {
481 if (selected_index >= 0) {
482 return items[selected_index];
489 SelectedIndex = items.IndexOf(value);
493 [DefaultValue(false)]
506 [DefaultValue(false)]
516 #endregion // Public Instance Properties
518 #region Public Instance Methods
519 public override void DownButton() {
523 int new_index = selected_index + 1;
525 if (new_index >= items.Count) {
532 SelectedIndex = new_index;
534 // UIA Framework Event: DownButton Click
535 OnUIADownButtonClick (EventArgs.Empty);
538 public override string ToString() {
539 return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
542 public override void UpButton() {
546 int new_index = selected_index - 1;
553 new_index = items.Count - 1;
556 SelectedIndex = new_index;
558 // UIA Framework Event: UpButton Click
559 OnUIAUpButtonClick (EventArgs.Empty);
561 #endregion // Public Instance Methods
563 #region Protected Instance Methods
564 protected override AccessibleObject CreateAccessibilityInstance() {
565 AccessibleObject acc;
567 acc = new AccessibleObject(this);
568 acc.role = AccessibleRole.SpinButton;
573 protected override void OnChanged(object source, EventArgs e) {
574 base.OnChanged (source, e);
577 protected void OnSelectedItemChanged(object source, EventArgs e) {
578 EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
583 protected override void UpdateEditText() {
584 if ((selected_index >= 0) && (selected_index < items.Count)) {
586 Text = items[selected_index].ToString();
590 protected override void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
592 base.OnTextBoxKeyPress (source, e);
594 #endregion // Protected Instance Methods
598 [EditorBrowsable (EditorBrowsableState.Never)]
599 public new event EventHandler PaddingChanged {
600 add { base.PaddingChanged += value; }
601 remove { base.PaddingChanged -= value; }
604 static object SelectedItemChangedEvent = new object ();
605 public event EventHandler SelectedItemChanged {
606 add { Events.AddHandler (SelectedItemChangedEvent, value); }
607 remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }