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 [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;
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
) {
116 #endregion // DomainUpDownAccessibleObject Constructors
118 #region DomainUpDownAccessibleObject Properties
119 public override AccessibleRole 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
146 internal DomainUpDownItemCollection() {}
147 #endregion // Constructors
149 #region Public Instance Properties
151 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
152 public override object this[int index
] {
159 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
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) {
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);
180 public override void Insert(int index
, object value) {
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
);
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;
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();
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))
265 else if (index
<= selected_index
)
266 selected_index
+= size_delta
;
268 if (sorted
&& (index
>= 0)) // index < 0 means it is already sorting
274 OnSelectedItemChanged(this, EventArgs
.Empty
);
278 void go_to_user_input() {
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
) {
293 base.txtView
.SelectionLength
= 0;
297 if (base.txtView
.SelectionLength
== 0) {
298 base.txtView
.SelectionStart
= 0;
301 if (base.txtView
.SelectionStart
!= 0) {
305 if (e
.KeyChar
== '\b') { // backspace
306 if (base.txtView
.SelectionLength
> 0) {
307 string prefix
= base.txtView
.SelectedText
.Substring(0, base.txtView
.SelectionLength
- 1);
311 if (typed_to_index
< 0) {
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) {
324 if (difference
> 0) { // since it is sorted, no strings after this point will match
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)) {
341 Text
= items
.string_cache
[typed_to_index
].ToString();
345 Select(0, prefix
.Length
);
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
;
364 if (typed_to_index
< 0) {
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) {
377 if (difference
<= 0) { // since it is sorted, no strings after this point will match
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)) {
394 Text
= items
.string_cache
[typed_to_index
].ToString();
399 Select(0, prefix
.Length
);
407 #endregion // Private Methods
409 #region Public Constructors
410 public DomainUpDown() {
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
))]
428 public DomainUpDownItemCollection Items
{
436 public int SelectedIndex
{
438 return selected_index
;
441 object before
= (selected_index
>= 0) ? items
[selected_index
] : null;
443 selected_index
= value;
446 object after
= (selected_index
>= 0) ? items
[selected_index
] : null;
448 if (!ReferenceEquals(before
, after
)) {
449 OnSelectedItemChanged(this, EventArgs
.Empty
);
455 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
456 public object SelectedItem
{
458 if (selected_index
>= 0) {
459 return items
[selected_index
];
466 SelectedIndex
= items
.IndexOf(value);
470 [DefaultValue(false)]
483 [DefaultValue(false)]
493 #endregion // Public Instance Properties
495 #region Public Instance Methods
496 public override void DownButton() {
500 int new_index
= selected_index
+ 1;
502 if (new_index
>= items
.Count
) {
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() {
520 int new_index
= selected_index
- 1;
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
;
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
;
555 protected override void UpdateEditText() {
556 if ((selected_index
>= 0) && (selected_index
< items
.Count
)) {
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
569 public event EventHandler SelectedItemChanged
;