2005-06-05 Peter Bartok <pbartok@novell.com>
[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) 2004, 2005 Novell, Inc.
21 //
22 // Authors:
23 //      Miguel de Icaza (miguel@novell.com).
24 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
25 //
26 //
27
28 using System;
29 using System.ComponentModel;
30 using System.Globalization;
31
32 namespace System.Windows.Forms {
33         [DefaultEvent ("ValueChanged")]
34         [DefaultProperty ("Value")]
35         public class NumericUpDown : UpDownBase, ISupportInitialize {
36                 Decimal updown_value;
37                 Decimal min = 0m;
38                 Decimal max = 100m;
39                 Decimal increment = 1m;
40                 string format;
41                 bool thousand;
42                 bool on_init;
43                 int decimal_places;
44                 bool hexadecimal;
45                 
46                 public NumericUpDown ()
47                 {
48                         UpdateFormat ();
49                         UpdateEditText ();
50                 }
51                 
52                 #region Events
53                 public new event EventHandler TextChanged;
54                 public event EventHandler ValueChanged;
55                 #endregion Events
56                 
57 #region ISupportInitialize methods
58
59                 //
60                 // These are used for batch updates of properties:
61                 // checking of values should be disabled during this
62                 // time, assuming that the caller will set us up
63                 // correctly.
64                 //
65                 // See: http://www.vkarlsen.no/articles/02_isupportinitialize/ISupportInitialize.pdf
66                 // for the strategy.
67                 //
68                 public void BeginInit ()
69                 {
70                         on_init = true;
71                 }
72
73                 public void EndInit ()
74                 {
75                         on_init = false;
76                         if (updown_value < min)
77                                 updown_value = min;
78                         if (updown_value > max)
79                                 updown_value = max;
80                         
81                         UpdateEditText ();
82                 }
83 #endregion
84
85                 public override void DownButton ()
86                 {
87                         if (UserEdit)
88                                 ParseEditText ();
89
90                         if (updown_value-increment >= min){
91                                 Value -= increment;
92                                 UpdateEditText ();
93                         }
94                 }
95
96                 public override void UpButton ()
97                 {
98                         if (UserEdit)
99                                 ParseEditText ();
100
101                         if (updown_value + increment <= max){
102                                 Value += increment;
103                                 UpdateEditText ();
104                         }
105                 }
106
107                 void UpdateFormat ()
108                 {
109                         if (hexadecimal)
110                                 format = "X";
111                         else if (thousand)
112                                 format = "N" + decimal_places.ToString ();
113                         else
114                                 format = "F" + decimal_places.ToString ();
115                 }
116
117                 public override void UpdateEditText ()
118                 {
119                         if (on_init)
120                                 return;
121
122                         if (UserEdit)
123                                 ParseEditText ();
124
125                         ChangingText = true;
126                         if (hexadecimal) { // gotta convert to something, decimal throws with "X"
127                                 long val = (long) updown_value;
128                                 Text = val.ToString (format);
129                         } else {
130                                 Text = updown_value.ToString (format);
131                         }
132                 }
133
134                 public void ParseEditText ()
135                 {
136                         decimal res;
137                         try {
138                                 if (hexadecimal) {
139                                         res = Int64.Parse (Text, NumberStyles.HexNumber);
140                                 } else {
141                                         res = decimal.Parse (Text);
142                                 }
143                         } catch {
144                                 res = updown_value;
145                         }
146                         
147                         if (res < min)
148                                 res = min;
149                         else if (res > max)
150                                 res = max;
151
152                         Value = res;
153                         UserEdit = false;
154                 }
155
156                 protected override void ValidateEditText ()
157                 {
158                         ParseEditText ();
159                         UpdateEditText ();
160                 }
161
162                 
163                 protected override AccessibleObject CreateAccessibilityInstance ()
164                 {
165                         //TODO
166                         return base.CreateAccessibilityInstance ();
167                 }
168
169                 [Bindable(true)]
170                 public decimal Value {
171                         get {
172                                 return updown_value;
173                         }
174
175                         set {
176                                 if (updown_value ==  value) {
177                                         return;
178                                 }
179                                 
180                                 if (on_init){
181                                         updown_value = value;
182                                         return;
183                                 }
184                                 
185                                 if (value < min || value > max)
186                                         throw new ArgumentOutOfRangeException (
187                                                 String.Format ("Value {0} not within boundaries [{1}, {2}]", value, min, max));
188                                 
189                                 updown_value = value;
190                                 OnValueChanged (EventArgs.Empty);
191                                 UpdateEditText ();
192                         }
193                 }
194
195                 public decimal Increment {
196                         get {
197                                 return increment;
198                         }
199
200                         set {
201                                 increment = value;
202                         }
203                 }
204                 
205                 [RefreshProperties(RefreshProperties.All)]
206                 public decimal Maximum {
207                         get {
208                                 return max;
209                         }
210
211                         set {
212                                 max = value;
213                                 if (value > max)
214                                         value = max;
215                                 if (min > max)
216                                         min = max;
217                                 UpdateEditText ();
218                         }
219                 }
220
221                 [RefreshProperties(RefreshProperties.All)]
222                 public decimal Minimum {
223                         get {
224                                 return min;
225                         }
226
227                         set {
228                                 min = value;
229                                 if (value < min)
230                                         value = min;
231                                 if (min > max)
232                                         max = min;
233                                 UpdateEditText ();
234                         }
235                 }
236
237                 [Bindable(false)]
238                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
239                 [EditorBrowsable(EditorBrowsableState.Never)]
240                 [Browsable (false)]
241                 public override string Text {
242                         get {
243                                 return base.Text;
244                         }
245
246                         set {
247                                 base.Text = value;
248                         }
249                 }
250
251                 [DefaultValue (false)]
252                 [Localizable (true)]
253                 public bool ThousandsSeparator {
254                         get {
255                                 return thousand;
256                         }
257
258                         set {
259                                 if (value == thousand)
260                                         return;
261
262                                 thousand = value;
263                                 UpdateFormat ();
264                                 UpdateEditText ();
265                         }
266                 }
267
268                 [DefaultValue (0)]
269                 public int DecimalPlaces {
270                         get {
271                                 return decimal_places;
272                         }
273
274                         set {
275                                 if (value == decimal_places)
276                                         return;
277
278                                 decimal_places = value;
279                                 UpdateFormat ();
280                                 UpdateEditText ();
281                         }
282                 }
283
284                 [DefaultValue (false)]
285                 public bool Hexadecimal {
286                         get { return hexadecimal; }
287                         set {
288                                 if (value == hexadecimal)
289                                         return;
290
291                                 hexadecimal = value;
292                                 UpdateFormat ();
293                                 UpdateEditText ();
294                         }
295                 }
296                 
297 #region Overrides for Control hooks
298                 static bool CompareCharToString (char c, string str)
299                 {
300                         if (str.Length == 1)
301                                 return (c == str [0]);
302
303                         return (new string (c, 1) == str);
304                 }
305
306                 protected override void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
307                 {
308                         base.OnTextBoxKeyPress (source, e);
309
310                         bool handled = true;
311                         char key = e.KeyChar;
312                         bool is_digit = Char.IsDigit (key);
313                         if (is_digit || hexadecimal) {
314                                 handled = !(is_digit || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F'));
315                         } else {
316                                 NumberFormatInfo ninfo = CultureInfo.CurrentCulture.NumberFormat;
317                                 if (CompareCharToString (key, ninfo.NegativeSign)) {
318                                         handled = false;
319                                 } else if (CompareCharToString (key, ninfo.NumberDecimalSeparator)) {
320                                         handled = false;
321                                 } else if (CompareCharToString (key, ninfo.NumberGroupSeparator)) {
322                                         handled = false;
323                                 }
324                         }
325
326                         e.Handled = handled;
327                 }
328 #endregion
329                 protected virtual void OnValueChanged (EventArgs e)
330                 {
331                         if (ValueChanged != null)
332                                 ValueChanged (this, e);
333                 }
334
335                 public override string ToString ()
336                 {
337                         return String.Format ("{0} Min={0} Max={1}", base.ToString (), min, max);
338                 }
339         }
340 }