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