2006-07-14 Jonathan Pobst <monkey@ipobst.com>
[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) 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 // COMPLETE
30
31 using System;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Drawing;
35 using System.Runtime.InteropServices;
36 using System.Windows.Forms;
37
38 namespace System.Windows.Forms
39 {
40         [Designer("System.Windows.Forms.Design.UpDownBaseDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
41         public abstract class UpDownBase : System.Windows.Forms.ContainerControl {
42                 #region UpDownSpinner Sub-class
43                 internal sealed class UpDownSpinner : Control {
44                         #region Local Variables
45                         private const int       InitialRepeatDelay = 50;
46                         private UpDownBase      owner;
47                         private Timer           tmrRepeat;
48                         private Rectangle       top_button_rect;
49                         private Rectangle       bottom_button_rect;
50                         private int             mouse_pressed;
51                         private int             mouse_x;
52                         private int             mouse_y;
53                         private int             repeat_delay;
54                         private int             repeat_counter;
55                         #endregion      // Local Variables
56
57                         #region Constructors
58                         public UpDownSpinner(UpDownBase owner) {
59                                 this.owner = owner;
60
61                                 mouse_pressed = 0;
62
63                                 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
64                                 this.SetStyle(ControlStyles.DoubleBuffer, true);
65                                 this.SetStyle(ControlStyles.Opaque, true);
66                                 this.SetStyle(ControlStyles.ResizeRedraw, true);
67                                 this.SetStyle(ControlStyles.UserPaint, true);
68                                 this.SetStyle(ControlStyles.Selectable, false);
69                                 this.SetStyle(ControlStyles.FixedHeight, true);
70
71                                 tmrRepeat = new Timer();
72
73                                 tmrRepeat.Enabled = false;
74                                 tmrRepeat.Interval = 10;
75                                 tmrRepeat.Tick += new EventHandler(tmrRepeat_Tick);
76
77                                 compute_rects();
78                         }
79                         #endregion      // Constructors
80
81                         #region Private & Internal Methods
82                         private void compute_rects() {
83                                 int top_button_height;
84                                 int bottom_button_height;
85
86                                 top_button_height = ClientSize.Height / 2;
87                                 bottom_button_height = ClientSize.Height - top_button_height;
88
89                                 top_button_rect = new Rectangle(0, 0, ClientSize.Width, top_button_height);
90                                 bottom_button_rect = new Rectangle(0, top_button_height, ClientSize.Width, bottom_button_height);
91                         }
92
93                         private void redraw(Graphics graphics) {
94                                 ButtonState top_button_state;
95                                 ButtonState bottom_button_state;
96
97                                 top_button_state = bottom_button_state = ButtonState.Normal;
98
99                                 if (mouse_pressed != 0) {
100                                         if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y)) {
101                                                 top_button_state = ButtonState.Pushed;
102                                         }
103
104                                         if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y)) {
105                                                 bottom_button_state = ButtonState.Pushed;
106                                         }
107                                 }
108
109                                 ControlPaint.DrawScrollButton(graphics, top_button_rect, ScrollButton.Up, top_button_state);
110                                 ControlPaint.DrawScrollButton(graphics, bottom_button_rect, ScrollButton.Down, bottom_button_state);
111                         }
112
113                         private void tmrRepeat_Tick(object sender, EventArgs e) {
114                                 if (repeat_delay > 1) {
115                                         repeat_counter++;
116
117                                         if (repeat_counter < repeat_delay) {
118                                                 return;
119                                         }
120
121                                         repeat_counter = 0;
122                                         repeat_delay = (repeat_delay * 3 / 4);
123                                 }
124
125                                 if (mouse_pressed == 0) {
126                                         tmrRepeat.Enabled = false;
127                                 }
128
129                                 if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y)) {
130                                         owner.UpButton();
131                                 }
132
133                                 if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y)) {
134                                         owner.DownButton();
135                                 }
136                         }
137                         #endregion      // Private & Internal Methods
138
139                         #region Protected Instance Methods
140                         protected override void OnMouseDown(MouseEventArgs e) {
141                                 this.Select(owner.txtView);
142
143                                 if (e.Button != MouseButtons.Left) {
144                                         return;
145                                 }
146
147                                 if (top_button_rect.Contains(e.X, e.Y)) {
148                                         mouse_pressed = 1;
149                                         owner.UpButton();
150                                 } else if (bottom_button_rect.Contains(e.X, e.Y)) {
151                                         mouse_pressed = 2;
152                                         owner.DownButton();
153                                 }
154
155                                 mouse_x = e.X;
156                                 mouse_y = e.Y;
157                                 Capture = true;
158
159                                 tmrRepeat.Enabled = true;
160                                 repeat_counter = 0;
161                                 repeat_delay = InitialRepeatDelay;
162
163                                 Refresh ();
164                         }
165
166                         protected override void OnMouseMove(MouseEventArgs e) {
167                                 ButtonState before, after;
168
169                                 before = ButtonState.Normal;
170                                 if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y))
171                                         before = ButtonState.Pushed;
172                                 if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y))
173                                         before = ButtonState.Pushed;
174
175                                 mouse_x = e.X;
176                                 mouse_y = e.Y;
177
178                                 after = ButtonState.Normal;
179                                 if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y))
180                                         after = ButtonState.Pushed;
181                                 if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y))
182                                         after = ButtonState.Pushed;
183
184                                 if (before != after) {
185                                         if (after == ButtonState.Pushed) {
186                                                 tmrRepeat.Enabled = true;
187                                                 repeat_counter = 0;
188                                                 repeat_delay = InitialRepeatDelay;
189
190                                                 // fire off one right now too for good luck
191                                                 if (mouse_pressed == 1)
192                                                         owner.UpButton();
193                                                 if (mouse_pressed == 2)
194                                                         owner.DownButton();
195                                         }
196                                         else
197                                                 tmrRepeat.Enabled = false;
198
199                                         Refresh ();
200                                 }
201                         }
202
203                         protected override void OnMouseUp(MouseEventArgs e) {
204                                 mouse_pressed = 0;
205                                 Capture = false;
206
207                                 Refresh ();
208                         }
209
210                         protected override void OnMouseWheel(MouseEventArgs e) {
211                                 if (e.Delta > 0)
212                                         owner.UpButton();
213                                 else if (e.Delta < 0)
214                                         owner.DownButton();
215                         }
216
217                         protected override void OnPaint(PaintEventArgs e) {
218                                 redraw(e.Graphics);
219                         }
220
221                         protected override void OnResize(EventArgs e) {
222                                 base.OnResize(e);
223                                 compute_rects();
224                         }
225                         #endregion      // Protected Instance Methods
226                 }
227                 #endregion      // UpDownSpinner Sub-class
228
229                 #region Local Variables
230                 internal TextBox                txtView;
231                 private UpDownSpinner           spnSpinner;
232                 private bool                    _InterceptArrowKeys = true;
233                 private LeftRightAlignment      _UpDownAlign;
234                 private bool                    changing_text;
235                 private bool                    user_edit;
236                 #endregion      // Local Variables
237
238                 #region Public Constructors
239                 public UpDownBase() {
240                         _UpDownAlign = LeftRightAlignment.Right;
241                         border_style = BorderStyle.Fixed3D;
242
243                         spnSpinner = new UpDownSpinner(this);
244
245                         txtView = new FixedSizeTextBox(false, true);
246                         txtView.ModifiedChanged += new EventHandler(OnChanged);
247                         txtView.AcceptsReturn = true;
248                         txtView.AutoSize = false;
249                         txtView.BorderStyle = BorderStyle.None;
250                         txtView.Location = new System.Drawing.Point(17, 17);
251                         txtView.TabIndex = TabIndex;
252
253                         SuspendLayout ();
254                         Controls.Add (spnSpinner);
255                         Controls.Add (txtView);
256                         ResumeLayout ();
257
258                         this.ActiveControl = txtView;
259
260                         Height = PreferredHeight;
261                         base.BackColor = txtView.BackColor;
262
263                         GotFocus += new EventHandler (GotFocusHandler);
264                         TabIndexChanged += new EventHandler (TabIndexChangedHandler);
265                         
266                         txtView.MouseWheel += new MouseEventHandler(txtView_MouseWheel);
267                         txtView.KeyDown += new KeyEventHandler(OnTextBoxKeyDown);
268                         txtView.KeyPress += new KeyPressEventHandler(OnTextBoxKeyPress);
269                         txtView.LostFocus += new EventHandler(OnTextBoxLostFocus);
270                         txtView.Resize += new EventHandler(OnTextBoxResize);
271                         txtView.TextChanged += new EventHandler(OnTextBoxTextChanged);
272
273                         txtView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
274
275                         SetStyle(ControlStyles.FixedHeight, true);
276
277                         UpdateEditText();
278                 }
279                 #endregion
280
281                 #region Private Methods
282                 void reseat_controls() {
283                         int text_displacement = 0;
284
285                         int spinner_width = 16;
286                         //int spinner_width = ClientSize.Height;
287
288                         if (_UpDownAlign == LeftRightAlignment.Left) {
289                                 spnSpinner.Bounds = new Rectangle(0, 0, spinner_width, ClientSize.Height);
290                                 text_displacement = spnSpinner.Width;
291
292                                 spnSpinner.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left;
293                         } else {
294                                 spnSpinner.Bounds = new Rectangle(ClientSize.Width - spinner_width, 0, spinner_width, ClientSize.Height);
295
296                                 spnSpinner.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
297                         }
298                         
299                         txtView.Bounds = new Rectangle(text_displacement, 0, ClientSize.Width - spinner_width, Height);
300                 }
301
302                 private void txtView_MouseWheel(object sender, MouseEventArgs e) {
303                         if (e.Delta > 0) {
304                                 UpButton();
305                         } else if (e.Delta < 0) {
306                                 DownButton();
307                         }
308                 }
309
310                 private void GotFocusHandler (object sender, EventArgs e)
311                 {
312                         txtView.Focus ();
313                 }
314
315                 private void TabIndexChangedHandler (object sender, EventArgs e)
316                 {
317                         txtView.TabIndex = TabIndex;
318                 }
319
320                 internal override void OnPaintInternal (PaintEventArgs e) {
321                         e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), ClientRectangle);
322                 }
323                 #endregion      // Private Methods
324
325                 #region Public Instance Properties
326                 [Browsable(false)]
327                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
328                 [EditorBrowsable(EditorBrowsableState.Never)]
329                 public override bool AutoScroll {
330                         get {
331                                 return base.AutoScroll;
332                         }
333
334                         set {
335                                 base.AutoScroll = value;
336                         }
337                 }
338
339                 [Browsable(false)]
340                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
341                 [EditorBrowsable(EditorBrowsableState.Never)]
342                 public Size AutoScrollMargin {
343                         get {
344                                 return base.AutoScrollMargin;
345                         }
346
347                         set {
348                                 base.AutoScrollMargin = value;
349                         }
350                 }
351
352                 [Browsable(false)]
353                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
354                 [EditorBrowsable(EditorBrowsableState.Never)]
355                 public Size AutoScrollMinSize {
356                         get {
357                                 return base.AutoScrollMinSize;
358                         }
359
360                         set {
361                                 base.AutoScrollMinSize = value;
362                         }
363                 }
364
365                 public override Color BackColor {
366                         get {
367                                 return base.BackColor;
368                         }
369
370                         set {
371                                 base.BackColor = value;
372                                 txtView.BackColor = value;
373                         }
374                 }
375
376                 [Browsable(false)]
377                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
378                 [EditorBrowsable(EditorBrowsableState.Never)]
379                 public override Image BackgroundImage {
380                         get {
381                                 return base.BackgroundImage;
382                         }
383                         set {
384                                 base.BackgroundImage = value;
385                                 txtView.BackgroundImage = value;
386                         }
387                 }
388
389
390                 [DefaultValue(BorderStyle.Fixed3D)]
391                 [DispId(-504)]
392                 public BorderStyle BorderStyle {
393                         get { return InternalBorderStyle; }
394                         set { InternalBorderStyle = value; }
395                 }
396
397                 public override ContextMenu ContextMenu {
398                         get {
399                                 return base.ContextMenu;
400                         }
401                         set {
402                                 base.ContextMenu = value;
403                                 txtView.ContextMenu = value;
404                                 spnSpinner.ContextMenu = value;
405                         }
406                 }
407
408                 [Browsable(false)]
409                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
410                 [EditorBrowsable(EditorBrowsableState.Never)]
411                 public DockPaddingEdges DockPadding {
412                         get {
413                                 return base.DockPadding;
414                         }
415                 }
416
417                 [Browsable(false)]
418                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
419                 public override bool Focused {
420                         get {
421                                 return txtView.Focused;
422                         }
423                 }
424
425                 public override Color ForeColor {
426                         get {
427                                 return base.ForeColor;
428                         }
429                         set {
430                                 base.ForeColor = value;
431                                 txtView.ForeColor = value;
432                         }
433                 }
434
435                 [DefaultValue(true)]
436                 public bool InterceptArrowKeys {
437                         get {
438                                 return _InterceptArrowKeys;
439                         }
440                         set {
441                                 _InterceptArrowKeys = value;
442                         }
443                 }
444
445                 [Browsable(false)]
446                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
447                 [EditorBrowsable(EditorBrowsableState.Advanced)]
448                 public int PreferredHeight {
449                         get {
450                                 // For some reason, the TextBox's PreferredHeight does not
451                                 // change when the Font property is assigned. Without a
452                                 // border, it will always be Font.Height anyway.
453                                 //int text_box_preferred_height = (txtView != null) ? txtView.PreferredHeight : Font.Height;
454                                 int text_box_preferred_height = Font.Height;
455
456                                 switch (border_style) {
457                                         case BorderStyle.FixedSingle:
458                                         case BorderStyle.Fixed3D:
459                                                 text_box_preferred_height += 3; // magic number? :-)
460
461                                                 return text_box_preferred_height + 4;
462
463                                         case BorderStyle.None:
464                                         default:
465                                                 return text_box_preferred_height;
466                                 }
467                         }
468                 }
469
470                 [DefaultValue(false)]
471                 public bool ReadOnly {
472                         get {
473                                 return txtView.ReadOnly;
474                         }
475                         set {
476                                 txtView.ReadOnly = value;
477                         }
478                 }
479
480                 [Localizable(true)]
481                 public override string Text {
482                         get {
483                                 if (txtView != null) {
484                                         return txtView.Text;
485                                 }
486                                 return "";
487                         }
488                         set {
489                                 bool suppress_validation = changing_text;
490
491                                 txtView.Text = value;
492
493                                 if (!suppress_validation)
494                                         ValidateEditText();
495                         }
496                 }
497
498                 [DefaultValue(HorizontalAlignment.Left)]
499                 [Localizable(true)]
500                 public HorizontalAlignment TextAlign {
501                         get {
502                                 return txtView.TextAlign;
503                         }
504                         set{
505                                 txtView.TextAlign = value;
506                         }
507                 }
508
509                 [DefaultValue(LeftRightAlignment.Right)]
510                 [Localizable(true)]
511                 public LeftRightAlignment UpDownAlign {
512                         get {
513                                 return _UpDownAlign;
514                         }
515                         set {
516                                 _UpDownAlign = value;
517
518                                 reseat_controls();
519                         }
520                 }
521                 #endregion      // Public Instance Properties
522
523                 #region Protected Instance Properties
524                 protected bool ChangingText {
525                         get {
526                                 return changing_text;
527                         }
528                         set {
529                                 changing_text = value;
530                         }
531                 }
532
533                 protected override CreateParams CreateParams {
534                         get {
535                                 return base.CreateParams;
536                         }
537                 }
538
539                 protected override Size DefaultSize {
540                         get {
541                                 return new Size(120, this.PreferredHeight);
542                         }
543                 }
544
545                 protected bool UserEdit {
546                         get {
547                                 return user_edit;
548                         }
549                         set {
550                                 user_edit = value;
551                         }
552                 }
553                 #endregion      // Protected Instance Properties
554
555                 #region Public Instance Methods
556                 public abstract void DownButton();
557                 public void Select(int start, int length) {
558                         txtView.Select(start, length);
559                 }
560
561                 public abstract void UpButton();
562                 #endregion      // Public Instance Methods
563
564                 #region Protected Instance Methods
565                 protected override void Dispose(bool disposing) {
566                         if (disposing) {
567                                 txtView.Dispose();
568                                 txtView = null;
569
570                                 spnSpinner.Dispose();
571                                 spnSpinner = null;
572                         }
573                         base.Dispose (disposing);
574                 }
575
576                 protected virtual void OnChanged(object source, EventArgs e) {
577                 }
578
579                 protected override void OnFontChanged(EventArgs e) {
580                         txtView.Font = this.Font;
581                         Height = PreferredHeight;
582                 }
583
584                 protected override void OnHandleCreated(EventArgs e) {
585                         base.OnHandleCreated (e);
586                 }
587
588                 protected override void OnLayout(LayoutEventArgs e) {
589                         base.OnLayout(e);
590                 }
591
592                 protected override void OnMouseWheel(MouseEventArgs e) {
593                         // prevent this event from firing twice for the same mouse action!
594                         if (GetChildAtPoint(new Point(e.X, e.Y)) == null)
595                                 txtView_MouseWheel(null, e);
596                 }
597
598                 protected virtual void OnTextBoxKeyDown(object source, KeyEventArgs e) {
599                         if (_InterceptArrowKeys) {
600                                 if ((e.KeyCode == Keys.Up) || (e.KeyCode == Keys.Down)) {
601                                         e.Handled = true;
602
603                                         if (e.KeyCode == Keys.Up)
604                                                 UpButton();
605                                         if (e.KeyCode == Keys.Down)
606                                                 DownButton();
607                                 }
608                         }
609
610                         OnKeyDown(e);
611                 }
612
613                 protected virtual void OnTextBoxKeyPress(object source, KeyPressEventArgs e) {
614                         if (e.KeyChar == '\r') {
615                                 e.Handled = true;
616                                 ValidateEditText();
617                         }
618                         OnKeyPress(e);
619                 }
620
621                 protected virtual void OnTextBoxLostFocus(object source, EventArgs e) {
622                         if (user_edit) {
623                                 ValidateEditText();
624                         }
625                 }
626
627                 protected virtual void OnTextBoxResize(object source, EventArgs e) {
628                         // compute the new height, taking the border into account
629                         Height = PreferredHeight;
630
631                         // let anchoring reposition the controls
632                 }
633
634                 protected virtual void OnTextBoxTextChanged(object source, EventArgs e) {
635                         if (changing_text) {
636                                 ChangingText = false;
637                         } else {
638                                 UserEdit = true;
639                         }
640
641                         OnTextChanged(e);
642                 }
643
644                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
645                         base.SetBoundsCore(x, y, width, height, specified);
646
647                         if ((specified & BoundsSpecified.Size) != BoundsSpecified.None) {
648                                 reseat_controls();
649                         }
650                 }
651
652                 protected abstract void UpdateEditText();
653
654                 protected virtual void ValidateEditText() {
655                         // to be overridden by subclassers
656                 }
657
658                 [EditorBrowsable(EditorBrowsableState.Advanced)]
659                 protected override void WndProc(ref Message m) {
660                         base.WndProc (ref m);
661                 }
662                 #endregion      // Protected Instance Methods
663
664                 #region Events
665                 [Browsable(false)]
666                 [EditorBrowsable(EditorBrowsableState.Never)]
667                 public new event EventHandler BackgroundImageChanged {
668                         add { base.BackgroundImageChanged += value; }
669                         remove { base.BackgroundImageChanged -= value; }
670                 }
671
672                 [Browsable(false)]
673                 [EditorBrowsable(EditorBrowsableState.Never)]
674                 public new event EventHandler MouseEnter {
675                         add { base.MouseEnter += value; }
676                         remove { base.MouseEnter -= value; }
677                 }
678
679                 [Browsable(false)]
680                 [EditorBrowsable(EditorBrowsableState.Never)]
681                 public new event EventHandler MouseHover {
682                         add { base.MouseHover += value; }
683                         remove { base.MouseHover -= value; }
684                 }
685
686                 [Browsable(false)]
687                 [EditorBrowsable(EditorBrowsableState.Never)]
688                 public new event EventHandler MouseLeave {
689                         add { base.MouseLeave += value; }
690                         remove { base.MouseLeave -= value; }
691                 }
692
693                 [Browsable(false)]
694                 [EditorBrowsable(EditorBrowsableState.Never)]
695                 public new event MouseEventHandler MouseMove {
696                         add { base.MouseMove += value; }
697                         remove { base.MouseMove -= value; }
698                 }
699                 #endregion      // Events
700         }
701 }