Merge pull request #1074 from esdrubal/bug18421
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / NumericUpDown.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.Globalization;
34 using System.Runtime.InteropServices;
35 using System.Text;
36 using System.Windows.Forms;
37
38 namespace System.Windows.Forms {
39         [DefaultEvent("ValueChanged")]
40         [DefaultProperty("Value")]
41         [DefaultBindingProperty ("Value")]
42         [ClassInterface (ClassInterfaceType.AutoDispatch)]
43         [ComVisible (true)]
44         public class NumericUpDown : UpDownBase, ISupportInitialize {
45                 #region Local Variables
46                 private bool    suppress_validation;
47                 private int     decimal_places;
48                 private bool    hexadecimal;
49                 private decimal increment;
50                 private decimal maximum;
51                 private decimal minimum;
52                 private bool    thousands_separator;
53                 private decimal dvalue;
54                 
55
56                 NumericUpDownAccelerationCollection accelerations;
57 //              private long buttonPressedTicks;
58                 //private bool isSpinning;
59                 #endregion      // Local Variables
60
61                 #region UIA FrameWork Events
62                 static object UIAMinimumChangedEvent = new object ();
63
64                 internal event EventHandler UIAMinimumChanged {
65                         add { Events.AddHandler (UIAMinimumChangedEvent, value); }
66                         remove { Events.RemoveHandler (UIAMinimumChangedEvent, value); }
67                 }
68
69                 internal void OnUIAMinimumChanged (EventArgs e)
70                 {
71                         EventHandler eh = (EventHandler) Events [UIAMinimumChangedEvent];
72                         if (eh != null)
73                                 eh (this, e);
74                 }
75
76                 static object UIAMaximumChangedEvent = new object ();
77
78                 internal event EventHandler UIAMaximumChanged {
79                         add { Events.AddHandler (UIAMaximumChangedEvent, value); }
80                         remove { Events.RemoveHandler (UIAMaximumChangedEvent, value); }
81                 }
82
83                 internal void OnUIAMaximumChanged (EventArgs e)
84                 {
85                         EventHandler eh = (EventHandler) Events [UIAMaximumChangedEvent];
86                         if (eh != null)
87                                 eh (this, e);
88                 }
89
90                 static object UIASmallChangeChangedEvent = new object ();
91
92                 internal event EventHandler UIASmallChangeChanged {
93                         add { Events.AddHandler (UIASmallChangeChangedEvent, value); }
94                         remove { Events.RemoveHandler (UIASmallChangeChangedEvent, value); }
95                 }
96
97                 internal void OnUIASmallChangeChanged (EventArgs e)
98                 {
99                         EventHandler eh = (EventHandler) Events [UIASmallChangeChangedEvent];
100                         if (eh != null)
101                                 eh (this, e);
102                 }
103                 #endregion
104
105                 #region Public Constructors
106                 public NumericUpDown() {
107                         suppress_validation = false;
108                         decimal_places = 0;
109                         hexadecimal = false;
110                         increment = 1M;
111                         maximum = 100M;
112                         minimum = 0M;
113                         thousands_separator = false;
114
115                         Text = "0";
116                 }
117                 #endregion      // Public Constructors
118
119                 #region Public Instance Properties
120
121                 [Browsable (false)]
122                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
123                 public NumericUpDownAccelerationCollection Accelerations {
124                         get {
125                                 if (accelerations == null)
126                                         accelerations = new NumericUpDownAccelerationCollection ();
127                                 return accelerations;
128                         }
129                 }
130
131                 [DefaultValue(0)]
132                 public int DecimalPlaces {
133                         get {
134                                 return decimal_places;
135                         }
136
137                         set {
138                                 decimal_places = value;
139                                 UpdateEditText();
140                         }
141                 }
142
143                 [DefaultValue(false)]
144                 public bool Hexadecimal {
145                         get {
146                                 return hexadecimal;
147                         }
148
149                         set {
150                                 hexadecimal = value;
151                                 UpdateEditText();
152                         }
153                 }
154
155                 public decimal Increment {
156                         get {
157
158                                 return increment;
159                         }
160
161                         set {
162                                 if (value < 0) {
163                                         throw new ArgumentOutOfRangeException("value", value, "NumericUpDown increment cannot be negative");
164                                 }
165
166                                 increment = value;
167
168                                 // UIA Framework Event: SmallChange Changed
169                                 OnUIASmallChangeChanged (EventArgs.Empty);
170                         }
171                 }
172
173                 [RefreshProperties(RefreshProperties.All)]
174                 public decimal Maximum {
175                         get {
176                                 return maximum;
177                         }
178
179                         set {
180                                 maximum = value;
181
182                                 if (minimum > maximum)
183                                         minimum = maximum;
184
185                                 if (dvalue > maximum)
186                                         Value = maximum;
187
188                                 // UIA Framework Event: Maximum Changed
189                                 OnUIAMaximumChanged (EventArgs.Empty);
190                         }
191                 }
192
193                 [RefreshProperties(RefreshProperties.All)]
194                 public decimal Minimum {
195                         get {
196                                 return minimum;
197                         }
198
199                         set {
200                                 minimum = value;
201
202                                 if (maximum < minimum)
203                                         maximum = minimum;
204
205                                 if (dvalue < minimum)
206                                         Value = minimum;
207
208                                 // UIA Framework Event: Minimum Changed
209                                 OnUIAMinimumChanged (EventArgs.Empty);
210                         }
211                 }
212
213                 [Browsable (false)]
214                 [EditorBrowsable (EditorBrowsableState.Never)]
215                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
216                 public new Padding Padding {
217                         get { return Padding.Empty; }
218                         set { }
219                 }
220
221                 [Bindable(false)]
222                 [Browsable(false)]
223                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
224                 [EditorBrowsable(EditorBrowsableState.Never)]
225                 public override string Text {
226                         get {
227                                 return base.Text;
228                         }
229
230                         set {
231                                 base.Text = value;
232                         }
233                 }
234
235                 [DefaultValue(false)]
236                 [Localizable(true)]
237                 public bool ThousandsSeparator {
238                         get {
239                                 return thousands_separator;
240                         }
241
242                         set {
243                                 thousands_separator = value;
244                                 UpdateEditText();
245                         }
246                 }
247
248                 [Bindable(true)]
249                 public decimal Value {
250                         get {
251                                 if (UserEdit)
252                                         ValidateEditText ();
253                                 return dvalue;
254                         }
255
256                         set {
257                                 if (value != dvalue) {
258                                         if (!suppress_validation && ((value < minimum) || (value > maximum))) {
259                                                 throw new ArgumentOutOfRangeException ("value", "NumericUpDown.Value must be within the specified Minimum and Maximum values");
260                                         }
261
262                                         dvalue = value;
263                                         OnValueChanged (EventArgs.Empty);
264                                         UpdateEditText ();
265                                 }
266                         }
267                 }
268                 #endregion      // Public Instance Properties
269
270                 #region Public Instance Methods
271                 public void BeginInit() {
272                         suppress_validation = true;
273                 }
274
275                 public void EndInit() {
276                         suppress_validation = false;
277                         Value = Check (dvalue);
278                         UpdateEditText ();
279                 }
280
281                 public override string ToString() {
282                         return string.Format("{0}, Minimum = {1}, Maximum = {2}", base.ToString(), minimum, maximum);
283                 }
284
285                 public override void DownButton() {
286                         if (UserEdit) {
287                                 ParseEditText ();
288                         }
289
290                         Value = Math.Max (minimum, unchecked (dvalue - increment));
291
292                         // UIA Framework Event: DownButton Click
293                         OnUIADownButtonClick (EventArgs.Empty);
294                 }
295
296                 public override void UpButton() {
297                         if (UserEdit) {
298                                 ParseEditText ();
299                         }
300
301                         Value = Math.Min (maximum, unchecked (dvalue + increment));
302
303                         // UIA Framework Event: UpButton Click
304                         OnUIAUpButtonClick (EventArgs.Empty);
305                 }
306                 #endregion      // Public Instance Methods
307
308                 #region Protected Instance Methods
309                 protected override AccessibleObject CreateAccessibilityInstance() {
310                         AccessibleObject        acc;
311
312                         acc = new AccessibleObject(this);
313                         acc.role = AccessibleRole.SpinButton;
314
315                         return acc;
316                 }
317
318                 protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
319                         if ((ModifierKeys & ~Keys.Shift) != Keys.None) {
320                                 return;
321                         }
322
323                         NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
324                         string pressedKey = e.KeyChar.ToString ();
325
326                         if ((pressedKey != nfi.NegativeSign) && (pressedKey != nfi.NumberDecimalSeparator) && 
327                                 (pressedKey != nfi.NumberGroupSeparator)) {
328                                 string acceptable = hexadecimal ? "\b0123456789abcdefABCDEF" : "\b0123456789";
329                                 if (acceptable.IndexOf (e.KeyChar) == -1) {
330                                         // FIXME: produce beep to signal that "invalid" key was pressed
331                                         // prevent the key from reaching the text box
332                                         e.Handled = true;
333                                 }
334                         }
335
336                         base.OnTextBoxKeyPress(source, e);
337                 }
338
339                 protected virtual void OnValueChanged(EventArgs e) {
340                         EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
341                         if (eh != null)
342                                 eh (this, e);
343                 }
344
345                 protected void ParseEditText () {
346                         try {
347                                 if (!hexadecimal) {
348                                         Value = Check (decimal.Parse (Text, CultureInfo.CurrentCulture));
349                                 } else {
350                                         Value = Check (Convert.ToDecimal (Convert.ToInt32 (Text, 16)));
351                                 }
352                         }
353                         catch { }
354                         finally {
355                                 UserEdit = false;
356                         }
357                 }
358
359                 private decimal Check (decimal val)
360                 {
361                         decimal ret = val;
362                         if (ret < minimum) {
363                                 ret = minimum;
364                         }
365
366                         if (ret > maximum) {
367                                 ret = maximum;
368                         }
369
370                         return ret;
371                 }
372
373                 protected override void UpdateEditText () {
374                         if (suppress_validation)
375                                 return;
376
377                         if (UserEdit)
378                                 ParseEditText ();
379
380                         ChangingText = true;
381                         if (!hexadecimal) {
382                                 // "N" and "F" differ only in that "N" includes commas
383                                 // every 3 digits to the left of the decimal and "F"
384                                 // does not.
385
386                                 string format_string;
387
388                                 if (thousands_separator) {
389                                         format_string = "N";
390                                 } else {
391                                         format_string = "F";
392                                 }
393
394                                 format_string += decimal_places;
395
396                                 Text = dvalue.ToString (format_string, CultureInfo.CurrentCulture);
397
398                         } else {
399                                 // Cast to Int64 to be able to use the "X" formatter.
400                                 // If the value is below Int64.MinValue or above Int64.MaxValue,
401                                 // then an OverflowException will be thrown, as with .NET.
402                                 Text = ((Int64)dvalue).ToString("X", CultureInfo.CurrentCulture);
403                         }
404                 }
405
406
407                 protected override void ValidateEditText() {
408                         ParseEditText ();
409                         UpdateEditText ();
410                 }
411
412                 protected override void OnLostFocus(EventArgs e) {
413                         base.OnLostFocus(e);
414                         if (UserEdit)
415                                 UpdateEditText();
416                 }
417
418                 protected override void OnKeyUp (KeyEventArgs e)
419                 {
420 //                      isSpinning = false;
421                         base.OnKeyUp (e);
422                 }
423
424                 protected override void OnKeyDown (KeyEventArgs e)
425                 {
426 //                      buttonPressedTicks = DateTime.Now.Ticks;
427 //                      isSpinning = true;
428                         base.OnKeyDown (e);
429                 }
430                 #endregion      // Protected Instance Methods
431
432                 #region Events
433                 [Browsable (false)]
434                 [EditorBrowsable (EditorBrowsableState.Never)]
435                 public new event EventHandler PaddingChanged {
436                         add { base.PaddingChanged += value; }
437                         remove { base.PaddingChanged -= value; }
438                 }
439
440                 static object ValueChangedEvent = new object ();
441
442                 public event EventHandler ValueChanged {
443                         add { Events.AddHandler (ValueChangedEvent, value); }
444                         remove { Events.RemoveHandler (ValueChangedEvent, value); }
445                 }
446
447                 [Browsable(false)]
448                 [EditorBrowsable(EditorBrowsableState.Never)]
449                 public new event EventHandler TextChanged {
450                         add { base.TextChanged += value; }
451                         remove { base.TextChanged -= value; }
452                 }
453                 #endregion      // Events
454         }
455 }