copy from trunk for 1.1.13.2
[mono-project.git] / mcs / Managed.Windows.Forms / System.Windows.Forms / DomainUpDown.cs
blob06d8c3d61b3dc82b7079122fe9e050979611b385
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:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
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.
22 // Authors:
23 // Jonathan Gilbert <logic@deltaq.org>
25 // Integration into MWF:
26 // Peter Bartok <pbartok@novell.com>
29 // COMPLETE
31 using System;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Drawing;
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 [MonoTODO("Figure out what to do with the DomainItemAccessibleObject and DomainUpDownAccessibleObject classes")]
44 public class DomainUpDown : UpDownBase {
45 #region Local Variables
46 private DomainUpDownItemCollection items;
47 private int selected_index = -1;
48 private bool sorted;
49 private bool wrap;
50 private int typed_to_index = -1;
51 #endregion // Local Variables
53 #region DomainUpDownAccessibleObject sub-class
54 [ComVisible(true)]
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) {
62 this.name = name;
63 this.parent = parent;
65 #endregion // DomainItemAccessibleObject Constructors
67 #region DomainItemAccessibleObject Properties
68 public override string Name {
69 get {
70 return base.Name;
73 set {
74 base.Name = value;
78 public override AccessibleObject Parent {
79 get {
80 return parent;
84 public override AccessibleRole Role {
85 get {
86 return base.Role;
90 public override AccessibleStates State {
91 get {
92 return base.State;
96 public override string Value {
97 get {
98 return base.Value;
101 #endregion // DomainItemAccessibleObject Properties
103 #endregion // DomainItemAccessibleObject sub-class
105 #region DomainUpDownAccessibleObject sub-class
106 [ComVisible(true)]
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) {
114 this.owner = owner;
116 #endregion // DomainUpDownAccessibleObject Constructors
118 #region DomainUpDownAccessibleObject Properties
119 public override AccessibleRole Role {
120 get {
121 return base.Role;
124 #endregion // DomainUpDownAccessibleObject Properties
126 #region DomainUpDownAccessibleObject Methods
127 public override AccessibleObject GetChild(int index) {
128 return base.GetChild (index);
131 public override int GetChildCount() {
132 return base.GetChildCount ();
134 #endregion // DomainUpDownAccessibleObject Methods
136 #endregion // DomainUpDownAccessibleObject sub-class
138 #region DomainUpDownItemCollection sub-class
139 public class DomainUpDownItemCollection : ArrayList {
140 internal ArrayList string_cache = new ArrayList();
142 #region Local Variables
143 #endregion // Local Variables
145 #region Constructors
146 internal DomainUpDownItemCollection() {}
147 #endregion // Constructors
149 #region Public Instance Properties
150 [Browsable(false)]
151 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
152 public override object this[int index] {
153 get {
154 return base[index];
157 set {
158 if (value == null) {
159 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
162 base[index] = value;
163 string_cache[index] = value.ToString();
164 OnCollectionChanged(index, 0);
167 #endregion // Public Instance Properties
169 #region Public Instance Methods
170 public override int Add(object value) {
171 if (value == null)
172 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
174 int ret = base.Add(value);
175 string_cache.Add(value.ToString());
176 OnCollectionChanged(Count - 1, +1);
177 return ret;
180 public override void Insert(int index, object value) {
181 if (value == null)
182 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
184 base.Insert(index, value);
185 string_cache.Insert(index, value.ToString());
186 OnCollectionChanged(index, +1);
189 public override void Remove(object obj) {
190 int index = IndexOf(obj);
192 if (index >= 0)
193 RemoveAt(index);
196 public override void RemoveAt(int index) {
197 base.RemoveAt(index);
198 string_cache.RemoveAt(index);
199 OnCollectionChanged(index, -1);
201 #endregion // Public Instance Methods
203 #region Internal Methods and Events
204 internal void OnCollectionChanged(int index, int size_delta) {
205 CollectionChangedEventHandler handler = CollectionChanged;
207 if (handler != null) {
208 handler(index, size_delta);
212 internal void PrivSort() {
213 PrivSort(0, Count, Comparer.Default);
216 internal void PrivSort(int index, int count, IComparer comparer) {
217 object[] base_items = null; // this will refer to the base ArrayList private _items member
218 object[] string_cache_items = null; // and this will refer to that of the string_cache
220 FieldInfo items_field = null;
222 try {
223 items_field = typeof(ArrayList).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
225 catch {} // security exceptions, perhaps...
227 if (items_field != null) {
228 base_items = items_field.GetValue(this) as object[];
229 string_cache_items = items_field.GetValue(string_cache) as object[];
232 if ((base_items == null) || (string_cache_items == null)) {
233 // oh poop =/ guess we have to completely repopulate the string cache
234 base.Sort(index, count, comparer);
236 for (int i=0; i < count; i++)
237 string_cache[i + index] = base[i + index].ToString();
239 else {
240 // yay, this will be much faster than creating a whole bunch more items
241 Array.Sort(string_cache_items, base_items, index, count, comparer);
243 OnCollectionChanged(-1, 0);
247 internal void PrivSort(IComparer comparer) {
248 PrivSort(0, Count, comparer);
251 internal event CollectionChangedEventHandler CollectionChanged;
252 #endregion // Internal Methods and Events
254 #endregion // DomainUpDownItemCollection sub-class
256 #region Private Methods
257 // normally I'd use an EventArgs class, but I don't want to create spurious objects here
258 internal delegate void CollectionChangedEventHandler(int index, int size_delta);
260 internal void items_CollectionChanged(int index, int size_delta) {
261 bool new_item = false;
263 if ((index == selected_index) && (size_delta <= 0))
264 new_item = true;
265 else if (index <= selected_index)
266 selected_index += size_delta;
268 if (sorted && (index >= 0)) // index < 0 means it is already sorting
269 items.PrivSort();
271 UpdateEditText();
273 if (new_item) {
274 OnSelectedItemChanged(this, EventArgs.Empty);
278 void go_to_user_input() {
279 UserEdit = false;
281 if (typed_to_index >= 0) {
282 selected_index = typed_to_index;
283 OnSelectedItemChanged(this, EventArgs.Empty);
287 private void TextBoxLostFocus(object source, EventArgs e) {
288 Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
291 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
292 if (!UserEdit) {
293 base.txtView.SelectionLength = 0;
294 typed_to_index = -1;
297 if (base.txtView.SelectionLength == 0) {
298 base.txtView.SelectionStart = 0;
301 if (base.txtView.SelectionStart != 0) {
302 return;
305 if (e.KeyChar == '\b') { // backspace
306 if (base.txtView.SelectionLength > 0) {
307 string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
309 bool found = false;
311 if (typed_to_index < 0) {
312 typed_to_index = 0;
315 if (sorted) {
316 for (int i=typed_to_index; i >= 0; i--) {
317 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
319 if (difference == 0) {
320 found = true;
321 typed_to_index = i;
324 if (difference > 0) { // since it is sorted, no strings after this point will match
325 break;
328 } else {
329 for (int i=0; i < items.Count; i++) {
330 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
331 found = true;
332 typed_to_index = i;
333 break;
338 ChangingText = true;
340 if (found)
341 Text = items.string_cache[typed_to_index].ToString();
342 else
343 Text = prefix;
345 Select(0, prefix.Length);
347 UserEdit = true;
349 e.Handled = true;
352 else {
353 char key_char = e.KeyChar;
355 if (char.IsLetterOrDigit(key_char)
356 || char.IsNumber(key_char)
357 || char.IsPunctuation(key_char)
358 || char.IsSymbol(key_char)
359 || char.IsWhiteSpace(key_char)) {
360 string prefix = base.txtView.SelectedText + key_char;
362 bool found = false;
364 if (typed_to_index < 0) {
365 typed_to_index = 0;
368 if (sorted) {
369 for (int i=typed_to_index; i < items.Count; i++) {
370 int difference = string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true);
372 if (difference == 0) {
373 found = true;
374 typed_to_index = i;
377 if (difference <= 0) { // since it is sorted, no strings after this point will match
378 break;
381 } else {
382 for (int i=0; i < items.Count; i++) {
383 if (0 == string.Compare(prefix, 0, items.string_cache[i].ToString(), 0, prefix.Length, true)) {
384 found = true;
385 typed_to_index = i;
386 break;
391 ChangingText = true;
393 if (found) {
394 Text = items.string_cache[typed_to_index].ToString();
395 } else {
396 Text = prefix;
399 Select(0, prefix.Length);
401 UserEdit = true;
403 e.Handled = true;
407 #endregion // Private Methods
409 #region Public Constructors
410 public DomainUpDown() {
411 selected_index = -1;
412 sorted = false;
413 wrap = false;
414 typed_to_index = -1;
416 items = new DomainUpDownItemCollection();
417 items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
419 this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
420 this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
422 #endregion // Public Constructors
424 #region Public Instance Properties
425 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
426 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
427 [Localizable(true)]
428 public DomainUpDownItemCollection Items {
429 get {
430 return items;
434 [Browsable(false)]
435 [DefaultValue(-1)]
436 public int SelectedIndex {
437 get {
438 return selected_index;
440 set {
441 object before = (selected_index >= 0) ? items[selected_index] : null;
443 selected_index = value;
444 UpdateEditText();
446 object after = (selected_index >= 0) ? items[selected_index] : null;
448 if (!ReferenceEquals(before, after)) {
449 OnSelectedItemChanged(this, EventArgs.Empty);
454 [Browsable(false)]
455 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
456 public object SelectedItem {
457 get {
458 if (selected_index >= 0) {
459 return items[selected_index];
460 } else {
461 return null;
465 set {
466 SelectedIndex = items.IndexOf(value);
470 [DefaultValue(false)]
471 public bool Sorted {
472 get {
473 return sorted;
475 set {
476 sorted = value;
478 if (sorted)
479 items.PrivSort();
483 [DefaultValue(false)]
484 [Localizable(true)]
485 public bool Wrap {
486 get {
487 return wrap;
489 set {
490 wrap = value;
493 #endregion // Public Instance Properties
495 #region Public Instance Methods
496 public override void DownButton() {
497 if (UserEdit)
498 go_to_user_input();
500 int new_index = selected_index + 1;
502 if (new_index >= items.Count) {
503 if (!wrap)
504 return;
506 new_index = 0;
509 SelectedIndex = new_index;
512 public override string ToString() {
513 return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
516 public override void UpButton() {
517 if (UserEdit)
518 go_to_user_input();
520 int new_index = selected_index - 1;
522 if (new_index < 0) {
523 if (!wrap) {
524 return;
527 new_index = items.Count - 1;
530 SelectedIndex = new_index;
532 #endregion // Public Instance Methods
534 #region Protected Instance Methods
535 protected override AccessibleObject CreateAccessibilityInstance() {
536 AccessibleObject acc;
538 acc = new AccessibleObject(this);
539 acc.role = AccessibleRole.SpinButton;
541 return acc;
544 protected override void OnChanged(object source, EventArgs e) {
545 base.OnChanged (source, e);
548 protected void OnSelectedItemChanged(object source, EventArgs e) {
549 EventHandler handler = SelectedItemChanged;
551 if (handler != null)
552 handler(source, e);
555 protected override void UpdateEditText() {
556 if ((selected_index >= 0) && (selected_index < items.Count)) {
557 ChangingText = true;
558 Text = items.string_cache[selected_index].ToString();
562 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
563 base.OnTextBoxKeyDown (source, e);
566 #endregion // Protected Instance Methods
568 #region Events
569 public event EventHandler SelectedItemChanged;
570 #endregion // Events