* TreeView.cs: Don't draw the selected node when we lose
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DomainUpDown.cs
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.
19 //
20 // Copyright (c) 2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jonathan Gilbert        <logic@deltaq.org>
24 //
25 // Integration into MWF:
26 //      Peter Bartok            <pbartok@novell.com>
27 //
28
29 // COMPLETE
30
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;
38
39 namespace System.Windows.Forms
40 {
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
52
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
59
60                         #region DomainItemAccessibleObject Constructors
61                         public DomainItemAccessibleObject(string name, AccessibleObject parent) {
62                                 this.name = name;
63                                 this.parent = parent;
64                         }
65                         #endregion      // DomainItemAccessibleObject Constructors
66
67                         #region DomainItemAccessibleObject Properties
68                         public override string Name {
69                                 get {
70                                         return base.Name;
71                                 }
72
73                                 set {
74                                         base.Name = value;
75                                 }
76                         }
77
78                         public override AccessibleObject Parent {
79                                 get {
80                                         return parent;
81                                 }
82                         }
83
84                         public override AccessibleRole Role {
85                                 get {
86                                         return base.Role;
87                                 }
88                         }
89
90                         public override AccessibleStates State {
91                                 get {
92                                         return base.State;
93                                 }
94                         }
95
96                         public override string Value {
97                                 get {
98                                         return base.Value;
99                                 }
100                         }
101                         #endregion      // DomainItemAccessibleObject Properties
102                 }
103                 #endregion      // DomainItemAccessibleObject sub-class
104
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
111
112                         #region DomainUpDownAccessibleObject Constructors
113                         public DomainUpDownAccessibleObject(Control owner) : base(owner) {
114                                 this.owner = owner;
115                         }
116                         #endregion      // DomainUpDownAccessibleObject Constructors
117
118                         #region DomainUpDownAccessibleObject Properties
119                         public override AccessibleRole Role {
120                                 get {
121                                         return base.Role;
122                                 }
123                         }
124                         #endregion      // DomainUpDownAccessibleObject Properties
125
126                         #region DomainUpDownAccessibleObject Methods
127                         public override AccessibleObject GetChild(int index) {
128                                 return base.GetChild (index);
129                         }
130
131                         public override int GetChildCount() {
132                                 return base.GetChildCount ();
133                         }
134                         #endregion      // DomainUpDownAccessibleObject Methods
135                 }
136                 #endregion      // DomainUpDownAccessibleObject sub-class
137
138                 #region DomainUpDownItemCollection sub-class
139                 public class DomainUpDownItemCollection : ArrayList {
140                         internal ArrayList string_cache = new ArrayList();
141
142                         #region Local Variables
143                         #endregion      // Local Variables
144
145                         #region Constructors
146                         internal DomainUpDownItemCollection() {}
147                         #endregion      // Constructors
148
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];
155                                 }
156
157                                 set {
158                                         if (value == null) {
159                                                 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
160                                         }
161
162                                         base[index] = value;
163                                         string_cache[index] = value.ToString();
164                                         OnCollectionChanged(index, 0);
165                                 }
166                         }
167                         #endregion      // Public Instance Properties
168
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");
173
174                                 int ret = base.Add(value);
175                                 string_cache.Add(value.ToString());
176                                 OnCollectionChanged(Count - 1, +1);
177                                 return ret;
178                         }
179
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");
183
184                                 base.Insert(index, value);
185                                 string_cache.Insert(index, value.ToString());
186                                 OnCollectionChanged(index, +1);
187                         }
188
189                         public override void Remove(object obj) {
190                                 int index = IndexOf(obj);
191
192                                 if (index >= 0)
193                                         RemoveAt(index);
194                         }
195
196                         public override void RemoveAt(int index) {
197                                 base.RemoveAt(index);
198                                 string_cache.RemoveAt(index);
199                                 OnCollectionChanged(index, -1);
200                         }
201                         #endregion      // Public Instance Methods
202
203                         #region Internal Methods and Events
204                         internal void OnCollectionChanged(int index, int size_delta) {
205                                 CollectionChangedEventHandler handler = CollectionChanged;
206
207                                 if (handler != null) {
208                                         handler(index, size_delta);
209                                 }
210                         }
211
212                         internal void PrivSort() {
213                                 PrivSort(0, Count, Comparer.Default);
214                         }
215
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
219
220                                 FieldInfo items_field = null;
221         
222                                 try {
223                                         items_field = typeof(ArrayList).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
224                                 }
225                                 catch {} // security exceptions, perhaps...
226
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[];
230                                 }
231
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);
235
236                                         for (int i=0; i < count; i++)
237                                                 string_cache[i + index] = base[i + index].ToString();
238                                 }
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);
242
243                                         OnCollectionChanged(-1, 0);
244                                 }
245                         }
246
247                         internal void PrivSort(IComparer comparer) {
248                                 PrivSort(0, Count, comparer);
249                         }
250
251                         internal event CollectionChangedEventHandler CollectionChanged;
252                         #endregion      // Internal Methods and Events
253                 }
254                 #endregion      // DomainUpDownItemCollection sub-class
255
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);
259
260                 internal void items_CollectionChanged(int index, int size_delta) {
261                         bool new_item = false;
262
263                         if ((index == selected_index) && (size_delta <= 0))
264                                 new_item = true;
265                         else if (index <= selected_index)
266                                 selected_index += size_delta;
267
268                         if (sorted && (index >= 0)) // index < 0 means it is already sorting
269                                 items.PrivSort();
270
271                         UpdateEditText();
272
273                         if (new_item) {
274                                 OnSelectedItemChanged(this, EventArgs.Empty);
275                         }
276                 }
277
278                 void go_to_user_input() {
279                         UserEdit = false;
280
281                         if (typed_to_index >= 0) {
282                                 selected_index = typed_to_index;
283                                 OnSelectedItemChanged(this, EventArgs.Empty);
284                         }
285                 }
286
287                 private void TextBoxLostFocus(object source, EventArgs e) {
288                         Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
289                 }
290
291                 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
292                         if (!UserEdit) {
293                                 base.txtView.SelectionLength = 0;
294                                 typed_to_index = -1;
295                         }
296
297                         if (base.txtView.SelectionLength == 0) {
298                                 base.txtView.SelectionStart = 0;
299                         }
300
301                         if (base.txtView.SelectionStart != 0) {
302                                 return;
303                         }
304
305                         if (e.KeyChar == '\b') { // backspace
306                                 if (base.txtView.SelectionLength > 0) {
307                                         string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
308
309                                         bool found = false;
310
311                                         if (typed_to_index < 0) {
312                                                 typed_to_index = 0;
313                                         }
314
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);
318
319                                                         if (difference == 0) {
320                                                                 found = true;
321                                                                 typed_to_index = i;
322                                                         }
323
324                                                         if (difference > 0) { // since it is sorted, no strings after this point will match
325                                                                 break;
326                                                         }
327                                                 }
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;
334                                                         }
335                                                 }
336                                         }
337
338                                         ChangingText = true;
339
340                                         if (found)
341                                                 Text = items.string_cache[typed_to_index].ToString();
342                                         else
343                                                 Text = prefix;
344
345                                         Select(0, prefix.Length);
346
347                                         UserEdit = true;
348
349                                         e.Handled = true;
350                                 }
351                         }
352                         else {
353                                 char key_char = e.KeyChar;
354
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;
361
362                                         bool found = false;
363
364                                         if (typed_to_index < 0) {
365                                                 typed_to_index = 0;
366                                         }
367
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);
371
372                                                         if (difference == 0) {
373                                                                 found = true;
374                                                                 typed_to_index = i;
375                                                         }
376
377                                                         if (difference <= 0) { // since it is sorted, no strings after this point will match
378                                                                 break;
379                                                         }
380                                                 }
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;
387                                                         }
388                                                 }
389                                         }
390
391                                         ChangingText = true;
392
393                                         if (found) {
394                                                 Text = items.string_cache[typed_to_index].ToString();
395                                         } else {
396                                                 Text = prefix;
397                                         }
398
399                                         Select(0, prefix.Length);
400
401                                         UserEdit = true;
402
403                                         e.Handled = true;
404                                 }
405                         }
406                 }
407                 #endregion      // Private Methods
408
409                 #region Public Constructors
410                 public DomainUpDown() {
411                         selected_index = -1;
412                         sorted = false;
413                         wrap = false;
414                         typed_to_index = -1;
415
416                         items = new DomainUpDownItemCollection();
417                         items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
418
419                         this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
420                         this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
421                 }
422                 #endregion      // Public Constructors
423
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;
431                         }
432                 }
433
434                 [Browsable(false)]
435                 [DefaultValue(-1)]
436                 public int SelectedIndex {
437                         get {
438                                 return selected_index;
439                         }
440                         set {
441                                 object before = (selected_index >= 0) ? items[selected_index] : null;
442
443                                 selected_index = value;
444                                 UpdateEditText();
445
446                                 object after = (selected_index >= 0) ? items[selected_index] : null;
447
448                                 if (!ReferenceEquals(before, after)) {
449                                         OnSelectedItemChanged(this, EventArgs.Empty);
450                                 }
451                         }
452                 }
453
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;
462                                 }
463                         }
464
465                         set {
466                                 SelectedIndex = items.IndexOf(value);
467                         }
468                 }
469
470                 [DefaultValue(false)]
471                 public bool Sorted {
472                         get {
473                                 return sorted;
474                         }
475                         set {
476                                 sorted = value;
477
478                                 if (sorted)
479                                         items.PrivSort();
480                         }
481                 }
482
483                 [DefaultValue(false)]
484                 [Localizable(true)]
485                 public bool Wrap {
486                         get {
487                                 return wrap;
488                         }
489                         set {
490                                 wrap = value;
491                         }
492                 }
493                 #endregion      // Public Instance Properties
494
495                 #region Public Instance Methods
496                 public override void DownButton() {
497                         if (UserEdit)
498                                 go_to_user_input();
499
500                         int new_index = selected_index + 1;
501
502                         if (new_index >= items.Count) {
503                                 if (!wrap)
504                                         return;
505
506                                 new_index = 0;
507                         }
508
509                         SelectedIndex = new_index;
510                 }
511
512                 public override string ToString() {
513                         return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
514                 }
515
516                 public override void UpButton() {
517                         if (UserEdit)
518                                 go_to_user_input();
519
520                         int new_index = selected_index - 1;
521
522                         if (new_index < 0) {
523                                 if (!wrap) {
524                                         return;
525                                 }
526
527                                 new_index = items.Count - 1;
528                         }
529
530                         SelectedIndex = new_index;
531                 }
532                 #endregion      // Public Instance Methods
533
534                 #region Protected Instance Methods
535                 protected override AccessibleObject CreateAccessibilityInstance() {
536                         AccessibleObject        acc;
537
538                         acc = new AccessibleObject(this);
539                         acc.role = AccessibleRole.SpinButton;
540
541                         return acc;
542                 }
543
544                 protected override void OnChanged(object source, EventArgs e) {
545                         base.OnChanged (source, e);
546                 }
547
548                 protected void OnSelectedItemChanged(object source, EventArgs e) {
549                         EventHandler handler = SelectedItemChanged;
550
551                         if (handler != null)
552                                 handler(source, e);
553                 }
554
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();
559                         }
560                 }
561
562                 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
563                         base.OnTextBoxKeyDown (source, e);
564                 }
565
566                 #endregion      // Protected Instance Methods
567
568                 #region Events
569                 public event EventHandler SelectedItemChanged;
570                 #endregion      // Events
571         }
572 }