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