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 #region Local Variables
144 #endregion // Local Variables
147 internal DomainUpDownItemCollection() {}
148 #endregion // Constructors
150 #region Public Instance Properties
152 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
153 public override object this[int index] {
160 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
164 OnCollectionChanged(index, 0);
167 #endregion // Public Instance Properties
169 #region Public Instance Methods
170 public override int Add(object item) {
172 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
174 int ret = base.Add(item);
175 OnCollectionChanged(Count - 1, +1);
179 public override void Insert(int index, object item) {
181 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
183 base.Insert(index, item);
184 OnCollectionChanged(index, +1);
187 public override void Remove(object item) {
188 int index = IndexOf(item);
194 public override void RemoveAt(int item) {
196 OnCollectionChanged(item, -1);
198 #endregion // Public Instance Methods
200 #region Internal Methods and Events
201 internal void OnCollectionChanged(int index, int size_delta) {
202 CollectionChangedEventHandler handler = CollectionChanged;
204 if (handler != null) {
205 handler(index, size_delta);
209 internal void PrivSort()
211 base.Sort (new ToStringSorter ());
214 private class ToStringSorter : IComparer
216 public int Compare (object x, object y)
218 return string.Compare (x.ToString (), y.ToString ());
222 internal event CollectionChangedEventHandler CollectionChanged;
223 #endregion // Internal Methods and Events
225 #endregion // DomainUpDownItemCollection sub-class
227 #region Private Methods
228 // normally I'd use an EventArgs class, but I don't want to create spurious objects here
229 internal delegate void CollectionChangedEventHandler(int index, int size_delta);
231 internal void items_CollectionChanged(int index, int size_delta) {
232 bool new_item = false;
234 if ((index == selected_index) && (size_delta <= 0))
236 else if (index <= selected_index)
237 selected_index += size_delta;
239 if (sorted && (index >= 0)) // index < 0 means it is already sorting
242 // XXX this might be wrong - it might be an explict 'Text = ...' assignment.
246 OnSelectedItemChanged(this, EventArgs.Empty);
250 void go_to_user_input() {
253 if (typed_to_index >= 0) {
254 selected_index = typed_to_index;
255 OnSelectedItemChanged(this, EventArgs.Empty);
259 private void TextBoxLostFocus(object source, EventArgs e) {
260 Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
263 int SearchTextWithPrefix (char key_char)
265 string prefix = key_char.ToString ();
268 start_index = selected_index == -1 ? 0 : selected_index;
269 i = selected_index == -1 || selected_index + 1 >= items.Count ? 0 : start_index + 1;
272 string item_text = items [i].ToString ();
273 if (String.Compare (prefix, 0, item_text, 0, 1, true) == 0)
276 if (i + 1 >= items.Count)
281 if (i == start_index)
288 bool IsValidInput (char key_char)
290 return Char.IsLetterOrDigit (key_char)
291 || Char.IsNumber (key_char)
292 || Char.IsPunctuation (key_char)
293 || Char.IsSymbol (key_char)
294 || Char.IsWhiteSpace (key_char);
297 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
299 char key_char = e.KeyChar;
300 if (IsValidInput (key_char) && items.Count > 0) {
301 int idx = SearchTextWithPrefix (key_char);
312 base.txtView.SelectionLength = 0;
316 if (base.txtView.SelectionLength == 0) {
317 base.txtView.SelectionStart = 0;
320 if (base.txtView.SelectionStart != 0) {
324 if (e.KeyChar == '\b') { // backspace
325 if (base.txtView.SelectionLength > 0) {
326 string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
330 if (typed_to_index < 0) {
335 for (int i=typed_to_index; i >= 0; i--) {
336 int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
338 if (difference == 0) {
343 if (difference > 0) { // since it is sorted, no strings after this point will match
348 for (int i=0; i < items.Count; i++) {
349 if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
360 Text = items[typed_to_index].ToString();
364 Select(0, prefix.Length);
372 char key_char = e.KeyChar;
374 if (IsValidInput (key_char)) {
375 string prefix = base.txtView.SelectedText + key_char;
379 if (typed_to_index < 0) {
384 for (int i=typed_to_index; i < items.Count; i++) {
385 int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
387 if (difference == 0) {
392 if (difference <= 0) { // since it is sorted, no strings after this point will match
397 for (int i=0; i < items.Count; i++) {
398 if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
409 Text = items[typed_to_index].ToString();
414 Select(0, prefix.Length);
422 #endregion // Private Methods
424 #region Public Constructors
425 public DomainUpDown() {
431 items = new DomainUpDownItemCollection();
432 items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
434 this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
435 this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
439 #endregion // Public Constructors
441 #region Public Instance Properties
442 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
443 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
445 public DomainUpDownItemCollection Items {
453 [EditorBrowsable (EditorBrowsableState.Never)]
454 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
455 public new Padding Padding {
456 get { return Padding.Empty; }
463 public int SelectedIndex {
465 return selected_index;
468 object before = (selected_index >= 0) ? items[selected_index] : null;
470 selected_index = value;
473 object after = (selected_index >= 0) ? items[selected_index] : null;
475 if (!ReferenceEquals(before, after)) {
476 OnSelectedItemChanged(this, EventArgs.Empty);
482 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
483 public object SelectedItem {
485 if (selected_index >= 0) {
486 return items[selected_index];
493 SelectedIndex = items.IndexOf(value);
497 [DefaultValue(false)]
510 [DefaultValue(false)]
520 #endregion // Public Instance Properties
522 #region Public Instance Methods
523 public override void DownButton() {
527 int new_index = selected_index + 1;
529 if (new_index >= items.Count) {
536 SelectedIndex = new_index;
539 // UIA Framework Event: DownButton Click
540 OnUIADownButtonClick (EventArgs.Empty);
544 public override string ToString() {
545 return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
548 public override void UpButton() {
552 int new_index = selected_index - 1;
559 new_index = items.Count - 1;
562 SelectedIndex = new_index;
565 // UIA Framework Event: UpButton Click
566 OnUIAUpButtonClick (EventArgs.Empty);
569 #endregion // Public Instance Methods
571 #region Protected Instance Methods
572 protected override AccessibleObject CreateAccessibilityInstance() {
573 AccessibleObject acc;
575 acc = new AccessibleObject(this);
576 acc.role = AccessibleRole.SpinButton;
581 protected override void OnChanged(object source, EventArgs e) {
582 base.OnChanged (source, e);
585 protected void OnSelectedItemChanged(object source, EventArgs e) {
586 EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
591 protected override void UpdateEditText() {
592 if ((selected_index >= 0) && (selected_index < items.Count)) {
594 Text = items[selected_index].ToString();
599 protected override void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
601 base.OnTextBoxKeyPress (source, e);
604 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
605 base.OnTextBoxKeyDown (source, e);
608 #endregion // Protected Instance Methods
613 [EditorBrowsable (EditorBrowsableState.Never)]
614 public new event EventHandler PaddingChanged {
615 add { base.PaddingChanged += value; }
616 remove { base.PaddingChanged -= value; }
620 static object SelectedItemChangedEvent = new object ();
621 public event EventHandler SelectedItemChanged {
622 add { Events.AddHandler (SelectedItemChangedEvent, value); }
623 remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }