Make it build
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / UpDownBase.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 Novell, Inc.
21 //
22 // Authors:
23 //      Miguel de Icaza (miguel@novell.com).
24 //
25 //
26 /*
27
28 TODO:
29
30         - Actually paint the border, could not get it to work.
31
32         - Implement ContextMenu property.
33         
34         - Force the size of the entry: it can not be resized vertically
35           ever, the size is set by the size of the font
36
37         - Add defaults with [DefautValue ]
38
39         - Audit every place where ChangingText is used, whose meaning is
40           `Me, the library is changing the text'.  Kind of hack to deal with
41           loops on events.
42
43         - Hook up the keyboard events.
44
45 */
46 using System;
47 using System.Drawing;
48 using System.ComponentModel;
49 using System.Runtime.InteropServices;
50
51 namespace System.Windows.Forms {
52         public abstract class UpDownBase : ContainerControl {
53
54                 internal class Spinner : Control, IDisposable {
55                         UpDownBase updownbase;
56                         Rectangle up, down, pressed;
57                         Timer timer;
58                         bool up_pressed, down_pressed;
59                         bool captured, mouse_in;
60
61                         //
62                         // Interval values
63                         //
64                         const int StartInterval = 1000;
65                         const int RepeatInterval = 400;
66                         const int ChangeInterval = 75;
67                         const int MinimumInterval = 100;
68                         
69                         internal Spinner (UpDownBase updownbase)
70                         {
71                                 this.updownbase = updownbase;
72                         }
73
74                         protected override void OnPaint (PaintEventArgs pe)
75                         {
76                                 base.OnPaint (pe);
77
78                                 if (pe.ClipRectangle.Contains (up))
79                                         DrawUp (pe.Graphics);
80                                 if (pe.ClipRectangle.Contains (down))
81                                         DrawDown (pe.Graphics);
82                         }
83
84                         protected override void OnLayout (LayoutEventArgs args)
85                         {
86                                 base.OnLayout (args);
87                                 Rectangle bounds = Bounds;
88
89                                 up = new Rectangle (0, 0, bounds.Width, bounds.Height/2);
90                                 down = new Rectangle (0, bounds.Height/2, bounds.Width, bounds.Height/2);
91                         }
92
93                         protected override void OnMouseDown (MouseEventArgs args)
94                         {
95                                 base.OnMouseDown (args);
96
97                                 if (args.Button != MouseButtons.Left)
98                                         return;
99
100                                 if (up.Contains (args.X, args.Y)){
101                                         up_pressed = true;
102                                         pressed = up;
103                                 } else if (down.Contains (args.X, args.Y)){
104                                         down_pressed = true;
105                                         pressed = down;
106                                 } else
107                                         return;
108
109                                 Click ();
110                                 Invalidate (pressed);
111                                 Capture = true;
112                                 InitTimer ();
113                                 
114                                 mouse_in = down_pressed | up_pressed;
115                         }
116
117                         protected override void OnMouseUp (MouseEventArgs args)
118                         {
119                                 if (Capture){
120                                         if (up_pressed){
121                                                 up_pressed = false;
122                                                 Invalidate (up);
123                                         }
124                                         if (down_pressed){
125                                                 down_pressed = false;
126                                                 Invalidate (down);
127                                         }
128                                 }
129                                 Capture = false;
130                                 timer.Enabled = false;
131                         }
132
133                         //
134                         // Sets up the auto-repeat timer, we give a one second
135                         // delay, and then we use the keyboard settings for auto-repeat.
136                         //
137                         void InitTimer ()
138                         {
139                                 if (timer != null){
140                                         timer.Interval = StartInterval;
141                                         timer.Enabled = true;
142                                         return;
143                                 }
144                                 
145                                 timer = new Timer ();
146                                 int kd = SystemInformation.KeyboardDelay;
147                                 kd = kd < 0 ? 0 : (kd > 4 ? 4 : kd);
148                                 timer.Interval = StartInterval;
149                                 timer.Tick += new EventHandler (ClockTick);
150                                 timer.Enabled = true;
151                         }
152
153                         void ClockTick (object o, EventArgs a)
154                         {
155                                 if (timer == null)
156                                         throw new Exception ("The timer that owns this callback is null!");
157                                 
158                                 int interval = timer.Interval;
159
160                                 if (interval == StartInterval)
161                                         interval = RepeatInterval;
162                                 else
163                                         interval -= ChangeInterval;
164
165                                 if (interval < MinimumInterval)
166                                         interval = MinimumInterval;
167                                 timer.Interval = interval;
168
169                                 Click ();
170                         }
171
172                         void Click ()
173                         {
174                                 if (up_pressed){
175                                         updownbase.UpButton ();
176                                 }
177                                 if (down_pressed)
178                                         updownbase.DownButton ();
179                         }
180
181                         protected override void OnMouseMove (MouseEventArgs args)
182                         {
183                                 base.OnMouseMove (args);
184                                 if (Capture){
185                                         bool old = mouse_in;
186
187                                         if (pressed.Contains (args.X, args.Y)){
188                                                 if (timer == null)
189                                                         InitTimer ();
190                                                 mouse_in = true;
191                                         } else {
192                                                 if (timer != null){
193                                                         timer.Enabled = false;
194                                                         timer.Dispose ();
195                                                         timer = null;
196                                                 }
197                                                 mouse_in = false;
198                                         }
199                                         if (mouse_in ^ old){
200                                                 Console.WriteLine ("STATE CHANGE");
201                                                 if (mouse_in)
202                                                         Click ();
203                                                 Invalidate (pressed);
204                                         }
205                                 }
206                         }
207                         
208                         void DrawUp (Graphics dc)
209                         {
210                                 ButtonState bs;
211
212                                 bs = mouse_in && up_pressed ? ButtonState.Pushed : ButtonState.Normal;
213                                 ThemeEngine.Current.CPDrawScrollButton (dc, up, ScrollButton.Up, bs);
214                         }
215                         
216                         void DrawDown (Graphics dc)
217                         {
218                                 ButtonState bs;
219
220                                 bs = mouse_in && down_pressed ? ButtonState.Pushed : ButtonState.Normal;
221                                 ThemeEngine.Current.CPDrawScrollButton (dc, down, ScrollButton.Down, bs);
222                         }
223
224                         void IDisposable.Dispose ()
225                         {
226                                 if (timer != null){
227                                         timer.Stop ();
228                                         timer.Dispose ();
229                                 }
230                                 timer = null;
231                                 base.Dispose ();
232                         }
233                 }
234
235                 BorderStyle border_style = BorderStyle.Fixed3D;
236                 int desired_height = 0;
237                 TextBox entry;
238                 Spinner spinner;
239                 int border;
240                 int scrollbar_button_size = ThemeEngine.Current.ScrollBarButtonSize;
241                 LeftRightAlignment updown_align = LeftRightAlignment.Right;
242                 bool changing_text = false;
243                 bool user_edit = false;
244                 bool intercept = true;
245                 
246                 public UpDownBase () : base ()
247                 {
248                         SuspendLayout ();
249
250                         entry = new TextBox ();
251                         entry.Font = Font;
252                         entry.Size = new Size (120, Font.Height);
253                         entry.LostFocus += new EventHandler (EntryOnLostFocus);
254                         entry.TextChanged += new EventHandler (OnTextBoxTextChanged);
255                         entry.KeyDown += new KeyEventHandler (OnTextBoxKeyDown);
256                         entry.KeyPress += new KeyPressEventHandler (OnTextBoxKeyPress);
257                         entry.LostFocus += new EventHandler (OnTextBoxLostFocus);
258                         entry.Resize += new EventHandler (OnTextBoxResize);
259                         entry.TextChanged += new EventHandler (OnTextBoxTextChanged);
260                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
261                         
262                         entry.ReadOnly = false;
263                         Controls.Add (entry);
264
265                         spinner = new Spinner (this);
266                         Controls.Add (spinner);
267
268                         ComputeSizeAndLocation ();
269                         ResumeLayout ();
270                 }
271
272                 void EntryOnLostFocus (object sender, EventArgs e)
273                 {
274                         OnLostFocus (e);
275                 }
276
277                 void ComputeSizeAndLocation ()
278                 {
279                         if (BorderStyle == BorderStyle.Fixed3D)
280                                 border = 2;
281                         else if (BorderStyle == BorderStyle.FixedSingle)
282                                 border = 1;
283                         else
284                                 border = 0;
285                                 
286                         Size = (entry.Size + spinner.Size + new Size (border, border));
287                         
288                         entry.Location = new Point (border, border);
289                 }
290                 
291
292 #region UpDownBase overwritten methods
293                 
294                 protected override void OnMouseWheel (MouseEventArgs args)
295                 {
296                         base.OnMouseWheel (args);
297
298                         if (args.Delta > 0)
299                                 UpButton ();
300                         else if (args.Delta < 0)
301                                 DownButton ();
302                 }
303
304                 protected virtual void OnChanged (object source, EventArgs e)
305                 {
306                         // Not clear, the docs state that this will raise the
307                         // Changed event, but that event is not listed anywhere.
308                 }
309
310                 protected override void OnFontChanged (EventArgs e)
311                 {
312                         base.OnFontChanged (e);
313
314                         entry.Font = Font;
315                         desired_height = entry.Height;
316                         Height = desired_height;
317                 }
318
319                 protected override void OnHandleCreated (EventArgs e)
320                 {
321                         base.OnHandleCreated (e);
322                         desired_height = entry.Height;
323                 }
324                                 
325                 protected override void OnLayout (LayoutEventArgs args)
326                 {
327                         base.OnLayout (args);
328
329                         Rectangle bounds = Bounds;
330                         int entry_width = bounds.Right - scrollbar_button_size - 1;
331
332                         entry.SetBounds (bounds.X, bounds.Y, entry_width, bounds.Height);
333                         spinner.SetBounds (entry_width + 1, bounds.Y, scrollbar_button_size, bounds.Height);
334                 }
335
336 #if NET_2_0
337 #if false
338                 protected override void OnPaint (PaintEventArgs pe)
339                 {
340                         base.OnPaint (pe);
341
342                         if (pe.ClipRectangle.Contains (up))
343                                 DrawUp (pe.Graphics);
344                         if (pe.ClipRectangle.Contains (down))
345                                 DrawDown (pe.Graphics);
346                 }
347
348                 protected override void SetVisibleCore (bool state)
349                 {
350                         base.SetVisibleCore (state);
351                 }
352 #endif
353 #endif
354
355                 [EditorBrowsable(EditorBrowsableState.Advanced)]
356                 protected override void WndProc (ref Message m)
357                 {
358                         base.WndProc (ref m);
359                 }
360
361                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
362                 {
363                         //
364                         // Force the size to be our height.
365                         //
366                         base.SetBoundsCore (x, y, width, desired_height, specified);
367                 }
368                 
369                 protected override void Dispose (bool disposing)
370                 {
371                         if (spinner != null){
372                                 if (disposing){
373                                         spinner.Dispose ();
374                                         entry.Dispose ();
375                                 }
376                         }
377                         spinner = null;
378                         entry = null;
379                         base.Dispose (true);
380                 }
381                 
382 #endregion
383                 
384 #region UpDownBase virtual methods
385                 //
386                 // These are hooked up to the various events from the Entry line that
387                 // we do not have yet, and implement the keyboard behavior (use a different
388                 // widget to test)
389                 //
390                 protected virtual void OnTextBoxKeyDown (object source, KeyEventArgs e)
391                 {
392                         OnKeyDown(e);
393                 }
394                 
395                 protected virtual void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
396                 {
397                         OnKeyPress (e);
398                 }
399
400                 protected virtual void OnTextBoxLostFocus (object source, EventArgs e)
401                 {
402                         OnLostFocus (e);
403                 }
404
405                 protected virtual void OnTextBoxResize (object source, EventArgs e)
406                 {
407                         OnResize (e);
408                 }
409
410                 protected virtual void OnTextBoxTextChanged (object source, EventArgs e)
411                 {
412                         OnTextChanged (e);
413 #if false
414                         if (changing_text)
415                                 return;
416                         changing_text = false;
417                         user_edit = true;
418                         OnChanged (source, e);
419 #endif
420                 }
421
422 #endregion
423
424 #region UpDownBase Properties
425
426                 /* FIXME: Do not know what Autoscroll should do */
427                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
428                 [EditorBrowsable(EditorBrowsableState.Never)]
429                 [Browsable(false)]
430                 public virtual bool AutoScroll {
431                         get {
432                                 return base.AutoScroll;
433                         }
434
435                         set {
436                                 base.AutoScroll = value;
437                         }
438                 }
439
440                 /* FIXME: Do not know what AutoscrollMargin does */
441                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
442                 [EditorBrowsable(EditorBrowsableState.Never)]
443                 [Browsable(false)]
444                 public new Size AutoScrollMargin {
445                         get {
446                                 return base.AutoScrollMargin;
447                         }
448
449                         set {
450                                 base.AutoScrollMargin = value;
451                         }
452                 }
453
454                 /* FIXME: Do not know what AutoscrollMinSize does */
455                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
456                 [EditorBrowsable(EditorBrowsableState.Never)]
457                 [Browsable(false)]
458                 public new Size AutoScrollMinSize {
459                         get {
460                                 return base.AutoScrollMinSize;
461                         }
462
463                         set {
464                                 base.AutoScrollMinSize = value;
465                         }
466                 }
467
468                 public override Color BackColor {
469                         get {
470                                 return base.BackColor;
471                         }
472
473                         set {
474                                 entry.BackColor = value;
475                         }
476                 }
477
478                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
479                 [EditorBrowsable(EditorBrowsableState.Never)]
480                 [Browsable(false)]
481                 public override Image BackgroundImage {
482                         get {
483                                 return entry.BackgroundImage;
484                         }
485
486                         set {
487                                 entry.BackgroundImage = value;
488                         }
489                 }
490
491                 [DefaultValue(BorderStyle.Fixed3D)]
492                 [DispId(-504)]
493                 public BorderStyle BorderStyle {
494                         get {
495                                 return border_style;
496                         }
497
498                         set {
499                                 border_style = value;
500
501                                 SuspendLayout ();
502                                 ComputeSizeAndLocation ();
503                                 ResumeLayout ();
504                         }
505                 }
506
507                 //
508                 // Used internally to flag when the derivative classes are changing
509                 // the Text property as opposed to the user
510                 //
511                 protected bool ChangingText {
512                         get {
513                                 return changing_text;
514                         }
515
516                         set {
517                                 changing_text = value;
518                         }
519                 }
520
521                 // TODO: What should this do?
522                 public override ContextMenu ContextMenu {
523                         get {
524                                 return null;
525                         }
526
527                         set {
528                                 /* */
529                         }
530                 }
531
532                 protected override CreateParams CreateParams {
533                         get {
534                                 return base.CreateParams;
535                         }
536                 }
537
538                 protected bool UserEdit {
539                         get {
540                                 return user_edit;
541                         }
542
543                         set {
544                                 user_edit = value;
545                         }
546                 }
547
548                 public override string Text {
549                         get {
550                                 if (entry == null)
551                                         return String.Empty;
552                                 
553                                 return entry.Text;
554                         }
555
556                         set {
557                                 //
558                                 // The documentation is conflicts with itself, we can
559                                 // not call UpdateEditText, as this will call Text to
560                                 // set the value.
561                                 //
562                                 entry.Text = value;
563                                 
564                                 if (UserEdit)
565                                         ValidateEditText ();
566                                 
567                                 if (ChangingText)
568                                         ChangingText = false;
569                         }
570                 }
571
572                 public LeftRightAlignment UpDownAlign
573                 {
574                         get {
575                                 return updown_align;
576                         }
577                         
578                         set {
579                                 updown_align = value;
580                         }
581                 }
582
583                 [DefaultValue(false)]
584                 public bool ReadOnly {
585                         get {
586                                 return entry.ReadOnly;
587                         }
588
589                         set {
590                                 entry.ReadOnly = value;
591                         }
592                 }
593
594                 public override bool Focused {
595                         get {
596                                 return entry.Focused;
597                         }
598                 }
599
600                 public override Color ForeColor {
601                         get {
602                                 return base.ForeColor;
603                         }
604
605                         set {
606                                 base.ForeColor = value;
607                                 entry.ForeColor = value;
608                         }
609                         
610                 }
611
612                 public bool InterceptArrowKeys {
613                         get {
614                                 return intercept;
615                         }
616
617                         set {
618                                 intercept = value;
619                         }
620                 }
621 #endregion
622                 
623 #region UpDownBase standard methods
624                 public void Select (int start, int length)
625                 {
626                         entry.Select (start, length);
627                 }
628
629                 protected virtual void ValidateEditText ()
630                 {
631                         // 
632                 }
633 #endregion
634
635 #region Events
636                 //
637                 // All these events are just a proxy to the base class,
638                 // we must overwrite them for API compatibility
639                 //
640                 
641                 public new event EventHandler MouseEnter {
642                         add {
643                                 base.MouseEnter += value;
644                         }
645
646                         remove {
647                                 base.MouseEnter -= value;
648                         }
649                 }
650
651                 public new event EventHandler MouseHover {
652                         add {
653                                 base.MouseHover += value;
654                         }
655
656                         remove {
657                                 base.MouseHover -= value;
658                         }
659                 }
660
661                 public new event EventHandler MouseLeave {
662                         add {
663                                 base.MouseLeave += value;
664                         }
665
666                         remove {
667                                 base.MouseLeave -= value;
668                         }
669                 }
670
671                 public new event EventHandler BackgroundImageChanged {
672                         add {
673                                 base.BackgroundImageChanged += value;
674                         }
675
676                         remove {
677                                 base.BackgroundImageChanged -= value;
678                         }
679                 }
680 #endregion
681                 
682 #region Abstract methods
683                 public abstract void DownButton ();
684                 public abstract void UpButton ();
685                 public abstract void UpdateEditText ();
686 #endregion
687         }
688 }