* TabControl.cs: Show the tooltip depending on the value
[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 using System;
30 using System.Collections;
31 using System.ComponentModel;
32 using System.Drawing;
33 using System.Reflection;
34 using System.Runtime.InteropServices;
35 using System.Windows.Forms;
36
37 namespace System.Windows.Forms
38 {
39         [DefaultProperty("Items")]
40         [DefaultEvent("SelectedItemChanged")]
41 #if NET_2_0
42         [DefaultBindingProperty ("SelectedItem")]
43         [ClassInterface (ClassInterfaceType.AutoDispatch)]
44         [ComVisible (true)]
45 #endif
46         public class DomainUpDown : UpDownBase {
47                 #region Local Variables
48                 private DomainUpDownItemCollection      items;
49                 private int                             selected_index = -1;
50                 private bool                            sorted;
51                 private bool                            wrap;
52                 private int                             typed_to_index = -1;
53                 #endregion      // Local Variables
54
55                 #region DomainUpDownAccessibleObject sub-class
56                 [ComVisible(true)]
57                 public class DomainItemAccessibleObject : AccessibleObject {
58                         #region DomainItemAccessibleObject Local Variables
59                         private AccessibleObject parent;
60                         #endregion      // DomainItemAccessibleObject Local Variables
61
62                         #region DomainItemAccessibleObject Constructors
63                         public DomainItemAccessibleObject(string name, AccessibleObject parent) {
64                                 this.name = name;
65                                 this.parent = parent;
66                         }
67                         #endregion      // DomainItemAccessibleObject Constructors
68
69                         #region DomainItemAccessibleObject Properties
70                         public override string Name {
71                                 get {
72                                         return base.Name;
73                                 }
74
75                                 set {
76                                         base.Name = value;
77                                 }
78                         }
79
80                         public override AccessibleObject Parent {
81                                 get {
82                                         return parent;
83                                 }
84                         }
85
86                         public override AccessibleRole Role {
87                                 get {
88                                         return base.Role;
89                                 }
90                         }
91
92                         public override AccessibleStates State {
93                                 get {
94                                         return base.State;
95                                 }
96                         }
97
98                         public override string Value {
99                                 get {
100                                         return base.Value;
101                                 }
102                         }
103                         #endregion      // DomainItemAccessibleObject Properties
104                 }
105                 #endregion      // DomainItemAccessibleObject sub-class
106
107                 #region DomainUpDownAccessibleObject sub-class
108                 [ComVisible(true)]
109                 public class DomainUpDownAccessibleObject : ControlAccessibleObject {
110                         #region DomainUpDownAccessibleObject Local Variables
111                         //private Control       owner;
112                         #endregion      // DomainUpDownAccessibleObject Local Variables
113
114                         #region DomainUpDownAccessibleObject Constructors
115                         public DomainUpDownAccessibleObject(Control owner) : base(owner)
116                         {
117                                 //this.owner = owner;
118                         }
119                         #endregion      // DomainUpDownAccessibleObject Constructors
120
121                         #region DomainUpDownAccessibleObject Properties
122                         public override AccessibleRole Role {
123                                 get {
124                                         return base.Role;
125                                 }
126                         }
127                         #endregion      // DomainUpDownAccessibleObject Properties
128
129                         #region DomainUpDownAccessibleObject Methods
130                         public override AccessibleObject GetChild(int index) {
131                                 return base.GetChild (index);
132                         }
133
134                         public override int GetChildCount() {
135                                 return base.GetChildCount ();
136                         }
137                         #endregion      // DomainUpDownAccessibleObject Methods
138                 }
139                 #endregion      // DomainUpDownAccessibleObject sub-class
140
141                 #region DomainUpDownItemCollection sub-class
142                 public class DomainUpDownItemCollection : ArrayList {
143                         #region Local Variables
144                         #endregion      // Local Variables
145
146                         #region Constructors
147                         internal DomainUpDownItemCollection() {}
148                         #endregion      // Constructors
149
150                         #region Public Instance Properties
151                         [Browsable(false)]
152                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
153                         public override object this[int index] {
154                                 get {
155                                         return base[index];
156                                 }
157
158                                 set {
159                                         if (value == null) {
160                                                 throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
161                                         }
162
163                                         base[index] = value;
164                                         OnCollectionChanged(index, 0);
165                                 }
166                         }
167                         #endregion      // Public Instance Properties
168
169                         #region Public Instance Methods
170                         public override int Add(object item) {
171                                 if (item == null)
172                                         throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
173
174                                 int ret = base.Add(item);
175                                 OnCollectionChanged(Count - 1, +1);
176                                 return ret;
177                         }
178
179                         public override void Insert(int index, object item) {
180                                 if (item == null)
181                                         throw new ArgumentNullException("value", "Cannot add null values to a DomainUpDownItemCollection");
182
183                                 base.Insert(index, item);
184                                 OnCollectionChanged(index, +1);
185                         }
186
187                         public override void Remove(object item) {
188                                 int index = IndexOf(item);
189
190                                 if (index >= 0)
191                                         RemoveAt(index);
192                         }
193
194                         public override void RemoveAt(int item) {
195                                 base.RemoveAt(item);
196                                 OnCollectionChanged(item, -1);
197                         }
198                         #endregion      // Public Instance Methods
199
200                         #region Internal Methods and Events
201                         internal void OnCollectionChanged(int index, int size_delta) {
202                                 CollectionChangedEventHandler handler = CollectionChanged;
203
204                                 if (handler != null) {
205                                         handler(index, size_delta);
206                                 }
207                         }
208
209                         internal void PrivSort()
210                         {
211                                 base.Sort (new ToStringSorter ());
212                         }
213
214                         private class ToStringSorter : IComparer
215                         {
216                                 public int Compare (object x, object y)
217                                 {
218                                         return string.Compare (x.ToString (), y.ToString ());
219                                 }
220                         }
221                         
222                         internal event CollectionChangedEventHandler CollectionChanged;
223                         #endregion      // Internal Methods and Events
224                 }
225                 #endregion      // DomainUpDownItemCollection sub-class
226
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);
230
231                 internal void items_CollectionChanged(int index, int size_delta) {
232                         bool new_item = false;
233
234                         if ((index == selected_index) && (size_delta <= 0))
235                                 new_item = true;
236                         else if (index <= selected_index)
237                                 selected_index += size_delta;
238
239                         if (sorted && (index >= 0)) // index < 0 means it is already sorting
240                                 items.PrivSort();
241
242                         // XXX this might be wrong - it might be an explict 'Text = ...' assignment.
243                         UpdateEditText();
244
245                         if (new_item) {
246                                 OnSelectedItemChanged(this, EventArgs.Empty);
247                         }
248                 }
249
250                 void go_to_user_input() {
251                         UserEdit = false;
252
253                         if (typed_to_index >= 0) {
254                                 selected_index = typed_to_index;
255                                 OnSelectedItemChanged(this, EventArgs.Empty);
256                         }
257                 }
258
259                 private void TextBoxLostFocus(object source, EventArgs e) {
260                         Select(base.txtView.SelectionStart + base.txtView.SelectionLength, 0);
261                 }
262
263                 int SearchTextWithPrefix (char key_char)
264                 {
265                         string prefix = key_char.ToString ();
266                         int start_index, i;
267
268                         start_index = selected_index == -1 ? 0 : selected_index;
269                         i = selected_index == -1 || selected_index + 1 >= items.Count ? 0 : start_index + 1;
270
271                         while (true) {
272                                 string item_text = items [i].ToString ();
273                                 if (String.Compare (prefix, 0, item_text, 0, 1, true) == 0)
274                                         return i;
275
276                                 if (i + 1 >= items.Count)
277                                         i = 0;
278                                 else
279                                         i++;
280
281                                 if (i == start_index)
282                                         break;
283                         }
284
285                         return -1;
286                 }
287
288                 bool IsValidInput (char key_char)
289                 {
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);
295                 }
296
297                 private void TextBoxKeyDown(object source, KeyPressEventArgs e) {
298                         if (ReadOnly) {
299                                 char key_char = e.KeyChar;
300                                 if (IsValidInput (key_char) && items.Count > 0) {
301                                         int idx = SearchTextWithPrefix (key_char);
302                                         if (idx > -1) {
303                                                 SelectedIndex = idx;
304                                                 e.Handled = true;
305                                         }
306                                 }
307
308                                 return;
309                         }
310
311                         if (!UserEdit) {
312                                 base.txtView.SelectionLength = 0;
313                                 typed_to_index = -1;
314                         }
315
316                         if (base.txtView.SelectionLength == 0) {
317                                 base.txtView.SelectionStart = 0;
318                         }
319
320                         if (base.txtView.SelectionStart != 0) {
321                                 return;
322                         }
323
324                         if (e.KeyChar == '\b') { // backspace
325                                 if (base.txtView.SelectionLength > 0) {
326                                         string prefix = base.txtView.SelectedText.Substring(0, base.txtView.SelectionLength - 1);
327
328                                         bool found = false;
329
330                                         if (typed_to_index < 0) {
331                                                 typed_to_index = 0;
332                                         }
333
334                                         if (sorted) {
335                                                 for (int i=typed_to_index; i >= 0; i--) {
336                                                         int difference = string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true);
337
338                                                         if (difference == 0) {
339                                                                 found = true;
340                                                                 typed_to_index = i;
341                                                         }
342
343                                                         if (difference > 0) { // since it is sorted, no strings after this point will match
344                                                                 break;
345                                                         }
346                                                 }
347                                         } else {
348                                                 for (int i=0; i < items.Count; i++) {
349                                                         if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
350                                                                 found = true;
351                                                                 typed_to_index = i;
352                                                                 break;
353                                                         }
354                                                 }
355                                         }
356
357                                         ChangingText = true;
358
359                                         if (found)
360                                                 Text = items[typed_to_index].ToString();
361                                         else
362                                                 Text = prefix;
363
364                                         Select(0, prefix.Length);
365
366                                         UserEdit = true;
367
368                                         e.Handled = true;
369                                 }
370                         }
371                         else {
372                                 char key_char = e.KeyChar;
373
374                                 if (IsValidInput (key_char)) {
375                                         string prefix = base.txtView.SelectedText + key_char;
376
377                                         bool found = false;
378
379                                         if (typed_to_index < 0) {
380                                                 typed_to_index = 0;
381                                         }
382
383                                         if (sorted) {
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);
386
387                                                         if (difference == 0) {
388                                                                 found = true;
389                                                                 typed_to_index = i;
390                                                         }
391
392                                                         if (difference <= 0) { // since it is sorted, no strings after this point will match
393                                                                 break;
394                                                         }
395                                                 }
396                                         } else {
397                                                 for (int i=0; i < items.Count; i++) {
398                                                         if (0 == string.Compare(prefix, 0, items[i].ToString(), 0, prefix.Length, true)) {
399                                                                 found = true;
400                                                                 typed_to_index = i;
401                                                                 break;
402                                                         }
403                                                 }
404                                         }
405
406                                         ChangingText = true;
407
408                                         if (found) {
409                                                 Text = items[typed_to_index].ToString();
410                                         } else {
411                                                 Text = prefix;
412                                         }
413
414                                         Select(0, prefix.Length);
415
416                                         UserEdit = true;
417
418                                         e.Handled = true;
419                                 }
420                         }
421                 }
422                 #endregion      // Private Methods
423
424                 #region Public Constructors
425                 public DomainUpDown() {
426                         selected_index = -1;
427                         sorted = false;
428                         wrap = false;
429                         typed_to_index = -1;
430
431                         items = new DomainUpDownItemCollection();
432                         items.CollectionChanged += new CollectionChangedEventHandler(items_CollectionChanged);
433
434                         this.txtView.LostFocus +=new EventHandler(TextBoxLostFocus);
435                         this.txtView.KeyPress += new KeyPressEventHandler(TextBoxKeyDown);
436
437                         UpdateEditText ();
438                 }
439                 #endregion      // Public Constructors
440
441                 #region Public Instance Properties
442                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
443                 [Editor("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
444                 [Localizable(true)]
445                 public DomainUpDownItemCollection Items {
446                         get {
447                                 return items;
448                         }
449                 }
450
451 #if NET_2_0
452                 [Browsable (false)]
453                 [EditorBrowsable (EditorBrowsableState.Never)]
454                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
455                 public new Padding Padding {
456                         get { return Padding.Empty; }
457                         set { }
458                 }
459 #endif
460
461                 [Browsable(false)]
462                 [DefaultValue(-1)]
463                 public int SelectedIndex {
464                         get {
465                                 return selected_index;
466                         }
467                         set {
468                                 object before = (selected_index >= 0) ? items[selected_index] : null;
469
470                                 selected_index = value;
471                                 UpdateEditText();
472
473                                 object after = (selected_index >= 0) ? items[selected_index] : null;
474
475                                 if (!ReferenceEquals(before, after)) {
476                                         OnSelectedItemChanged(this, EventArgs.Empty);
477                                 }
478                         }
479                 }
480
481                 [Browsable(false)]
482                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
483                 public object SelectedItem {
484                         get {
485                                 if (selected_index >= 0) {
486                                         return items[selected_index];
487                                 } else {
488                                         return null;
489                                 }
490                         }
491
492                         set {
493                                 SelectedIndex = items.IndexOf(value);
494                         }
495                 }
496
497                 [DefaultValue(false)]
498                 public bool Sorted {
499                         get {
500                                 return sorted;
501                         }
502                         set {
503                                 sorted = value;
504
505                                 if (sorted)
506                                         items.PrivSort();
507                         }
508                 }
509
510                 [DefaultValue(false)]
511                 [Localizable(true)]
512                 public bool Wrap {
513                         get {
514                                 return wrap;
515                         }
516                         set {
517                                 wrap = value;
518                         }
519                 }
520                 #endregion      // Public Instance Properties
521
522                 #region Public Instance Methods
523                 public override void DownButton() {
524                         if (UserEdit)
525                                 go_to_user_input();
526
527                         int new_index = selected_index + 1;
528
529                         if (new_index >= items.Count) {
530                                 if (!wrap)
531                                         return;
532
533                                 new_index = 0;
534                         }
535
536                         SelectedIndex = new_index;
537
538 #if NET_2_0
539                         // UIA Framework Event: DownButton Click
540                         OnUIADownButtonClick (EventArgs.Empty);
541 #endif
542                 }
543
544                 public override string ToString() {
545                         return base.ToString() + ", Items.Count: " + items.Count + ", SelectedIndex: " + selected_index;
546                 }
547
548                 public override void UpButton() {
549                         if (UserEdit)
550                                 go_to_user_input();
551
552                         int new_index = selected_index - 1;
553
554                         if (new_index < 0) {
555                                 if (!wrap) {
556                                         return;
557                                 }
558
559                                 new_index = items.Count - 1;
560                         }
561
562                         SelectedIndex = new_index;
563
564 #if NET_2_0
565                         // UIA Framework Event: UpButton Click
566                         OnUIAUpButtonClick (EventArgs.Empty);
567 #endif
568                 }
569                 #endregion      // Public Instance Methods
570
571                 #region Protected Instance Methods
572                 protected override AccessibleObject CreateAccessibilityInstance() {
573                         AccessibleObject        acc;
574
575                         acc = new AccessibleObject(this);
576                         acc.role = AccessibleRole.SpinButton;
577
578                         return acc;
579                 }
580
581                 protected override void OnChanged(object source, EventArgs e) {
582                         base.OnChanged (source, e);
583                 }
584
585                 protected void OnSelectedItemChanged(object source, EventArgs e) {
586                         EventHandler eh = (EventHandler)(Events [SelectedItemChangedEvent]);
587                         if (eh != null)
588                                 eh (this, e);
589                 }
590
591                 protected override void UpdateEditText() {
592                         if ((selected_index >= 0) && (selected_index < items.Count)) {
593                                 ChangingText = true;
594                                 Text = items[selected_index].ToString();
595                         }
596                 }
597
598 #if NET_2_0
599                 protected override void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
600                 {
601                         base.OnTextBoxKeyPress (source, e);
602                 }
603 #else
604                 protected override void OnTextBoxKeyDown(object source, KeyEventArgs e) {
605                         base.OnTextBoxKeyDown (source, e);
606                 }
607 #endif
608                 #endregion      // Protected Instance Methods
609
610                 #region Events
611 #if NET_2_0
612                 [Browsable (false)]
613                 [EditorBrowsable (EditorBrowsableState.Never)]
614                 public new event EventHandler PaddingChanged {
615                         add { base.PaddingChanged += value; }
616                         remove { base.PaddingChanged -= value; }
617                 }
618 #endif
619
620                 static object SelectedItemChangedEvent = new object ();
621                 public event EventHandler SelectedItemChanged {
622                         add { Events.AddHandler (SelectedItemChangedEvent, value); }
623                         remove { Events.RemoveHandler (SelectedItemChangedEvent, value); }
624                 }
625                 #endregion      // Events
626         }
627 }