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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc.
23 // Jonathan Gilbert <logic@deltaq.org>
25 // Integration into MWF:
26 // Peter Bartok <pbartok@novell.com>
30 using System.Collections;
31 using System.ComponentModel;
33 using System.Runtime.InteropServices;
34 using System.Windows.Forms;
36 namespace System.Windows.Forms
38 [ClassInterface (ClassInterfaceType.AutoDispatch)]
40 [Designer("System.Windows.Forms.Design.UpDownBaseDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
41 public abstract class UpDownBase : 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;
53 private int repeat_delay;
54 private int repeat_counter;
55 bool top_button_entered;
56 bool bottom_button_entered;
57 #endregion // Local Variables
60 public UpDownSpinner(UpDownBase owner)
66 SetStyle(ControlStyles.AllPaintingInWmPaint, true);
67 SetStyle(ControlStyles.DoubleBuffer, true);
68 SetStyle(ControlStyles.Opaque, true);
69 SetStyle(ControlStyles.ResizeRedraw, true);
70 SetStyle(ControlStyles.UserPaint, true);
71 SetStyle(ControlStyles.FixedHeight, true);
72 SetStyle(ControlStyles.Selectable, false);
74 tmrRepeat = new Timer();
76 tmrRepeat.Enabled = false;
77 tmrRepeat.Interval = 10;
78 tmrRepeat.Tick += new EventHandler(tmrRepeat_Tick);
82 #endregion // Constructors
84 #region Private & Internal Methods
85 private void compute_rects ()
87 int top_button_height;
88 int bottom_button_height;
90 top_button_height = ClientSize.Height / 2;
91 bottom_button_height = ClientSize.Height - top_button_height;
93 top_button_rect = new Rectangle(0, 0, ClientSize.Width, top_button_height);
94 bottom_button_rect = new Rectangle(0, top_button_height, ClientSize.Width, bottom_button_height);
97 private void redraw (Graphics graphics)
99 VisualStyles.PushButtonState top_button_state = VisualStyles.PushButtonState.Normal;
100 VisualStyles.PushButtonState bottom_button_state = VisualStyles.PushButtonState.Normal;
103 if (mouse_pressed != 0) {
104 if (mouse_pressed == 1 && top_button_rect.Contains(mouse_x, mouse_y))
105 top_button_state = VisualStyles.PushButtonState.Pressed;
107 if (mouse_pressed == 2 && bottom_button_rect.Contains(mouse_x, mouse_y))
108 bottom_button_state = VisualStyles.PushButtonState.Pressed;
110 if (top_button_entered)
111 top_button_state = VisualStyles.PushButtonState.Hot;
112 if (bottom_button_entered)
113 bottom_button_state = VisualStyles.PushButtonState.Hot;
116 top_button_state = VisualStyles.PushButtonState.Disabled;
117 bottom_button_state = VisualStyles.PushButtonState.Disabled;
119 ThemeEngine.Current.UpDownBaseDrawButton (graphics, top_button_rect, true, top_button_state);
120 ThemeEngine.Current.UpDownBaseDrawButton (graphics, bottom_button_rect, false, bottom_button_state);
123 private void tmrRepeat_Tick (object sender, EventArgs e)
125 if (repeat_delay > 1) {
128 if (repeat_counter < repeat_delay) {
133 repeat_delay = (repeat_delay * 3 / 4);
136 if (mouse_pressed == 0) {
137 tmrRepeat.Enabled = false;
140 if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y)) {
144 if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y)) {
148 #endregion // Private & Internal Methods
150 #region Protected Instance Methods
151 protected override void OnMouseDown (MouseEventArgs e)
153 if (e.Button != MouseButtons.Left) {
157 if (top_button_rect.Contains(e.X, e.Y)) {
160 } else if (bottom_button_rect.Contains(e.X, e.Y)) {
169 tmrRepeat.Enabled = true;
171 repeat_delay = InitialRepeatDelay;
176 protected override void OnMouseMove (MouseEventArgs e)
178 ButtonState before, after;
180 before = ButtonState.Normal;
181 if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y))
182 before = ButtonState.Pushed;
183 if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y))
184 before = ButtonState.Pushed;
189 after = ButtonState.Normal;
190 if ((mouse_pressed == 1) && top_button_rect.Contains(mouse_x, mouse_y))
191 after = ButtonState.Pushed;
192 if ((mouse_pressed == 2) && bottom_button_rect.Contains(mouse_x, mouse_y))
193 after = ButtonState.Pushed;
195 bool new_top_button_entered = top_button_rect.Contains (e.Location);
196 bool new_bottom_button_entered = bottom_button_rect.Contains (e.Location);
198 if (before != after) {
199 if (after == ButtonState.Pushed) {
200 tmrRepeat.Enabled = true;
202 repeat_delay = InitialRepeatDelay;
204 // fire off one right now too for good luck
205 if (mouse_pressed == 1)
207 if (mouse_pressed == 2)
211 tmrRepeat.Enabled = false;
213 top_button_entered = new_top_button_entered;
214 bottom_button_entered = new_bottom_button_entered;
218 if (ThemeEngine.Current.UpDownBaseHasHotButtonStyle) {
219 Region area_to_invalidate = new Region ();
221 area_to_invalidate.MakeEmpty ();
222 if (top_button_entered != new_top_button_entered) {
223 top_button_entered = new_top_button_entered;
224 area_to_invalidate.Union (top_button_rect);
227 if (bottom_button_entered != new_bottom_button_entered) {
228 bottom_button_entered = new_bottom_button_entered;
229 area_to_invalidate.Union (bottom_button_rect);
233 Invalidate (area_to_invalidate);
234 area_to_invalidate.Dispose ();
236 top_button_entered = new_top_button_entered;
237 bottom_button_entered = new_bottom_button_entered;
242 protected override void OnMouseUp(MouseEventArgs e)
250 protected override void OnMouseWheel(MouseEventArgs e)
254 else if (e.Delta < 0)
258 protected override void OnMouseLeave (EventArgs e)
260 if (top_button_entered) {
261 top_button_entered = false;
262 if (ThemeEngine.Current.UpDownBaseHasHotButtonStyle)
263 Invalidate (top_button_rect);
265 if (bottom_button_entered) {
266 bottom_button_entered = false;
267 if (ThemeEngine.Current.UpDownBaseHasHotButtonStyle)
268 Invalidate (bottom_button_rect);
272 protected override void OnPaint(PaintEventArgs e)
277 protected override void OnResize(EventArgs e)
282 #endregion // Protected Instance Methods
284 #endregion // UpDownSpinner Sub-class
286 internal class UpDownTextBox : TextBox {
288 private UpDownBase owner;
290 public UpDownTextBox (UpDownBase owner)
294 SetStyle (ControlStyles.FixedWidth, false);
295 SetStyle (ControlStyles.Selectable, false);
299 // The following can be shown to be present by
300 // adding events to both the UpDown and the
301 // internal textbox. the textbox doesn't
302 // generate any, and the updown generates them
304 protected override void OnGotFocus (EventArgs e)
306 ShowSelection = true;
307 owner.OnGotFocus (e);
311 protected override void OnLostFocus (EventArgs e)
313 ShowSelection = false;
314 owner.OnLostFocus (e);
318 protected override void OnMouseDown (MouseEventArgs e)
320 // XXX look into whether or not the
321 // mouse event args are altered in
324 owner.OnMouseDown (e);
325 base.OnMouseDown (e);
328 protected override void OnMouseUp (MouseEventArgs e)
330 // XXX look into whether or not the
331 // mouse event args are altered in
338 // XXX there are likely more events that forward up to the UpDown
341 #region Local Variables
342 internal UpDownTextBox txtView;
343 private UpDownSpinner spnSpinner;
344 private bool _InterceptArrowKeys = true;
345 private LeftRightAlignment _UpDownAlign;
346 private bool changing_text;
347 private bool user_edit;
348 #endregion // Local Variables
350 #region Public Constructors
353 _UpDownAlign = LeftRightAlignment.Right;
354 InternalBorderStyle = BorderStyle.Fixed3D;
356 spnSpinner = new UpDownSpinner(this);
358 txtView = new UpDownTextBox (this);
359 txtView.ModifiedChanged += new EventHandler(OnChanged);
360 txtView.AcceptsReturn = true;
361 txtView.AutoSize = false;
362 txtView.BorderStyle = BorderStyle.None;
363 txtView.Location = new System.Drawing.Point(17, 17);
364 txtView.TabIndex = TabIndex;
366 spnSpinner.Width = 16;
367 spnSpinner.Dock = DockStyle.Right;
369 txtView.Dock = DockStyle.Fill;
372 Controls.Add (spnSpinner);
373 Controls.Add (txtView);
376 Height = PreferredHeight;
377 base.BackColor = txtView.BackColor;
379 TabIndexChanged += new EventHandler (TabIndexChangedHandler);
381 txtView.KeyDown += new KeyEventHandler(OnTextBoxKeyDown);
382 txtView.KeyPress += new KeyPressEventHandler(OnTextBoxKeyPress);
383 // txtView.LostFocus += new EventHandler(OnTextBoxLostFocus);
384 txtView.Resize += new EventHandler(OnTextBoxResize);
385 txtView.TextChanged += new EventHandler(OnTextBoxTextChanged);
387 // So the child controls don't get auto selected when the updown is selected
388 auto_select_child = false;
389 SetStyle(ControlStyles.FixedHeight, true);
390 SetStyle(ControlStyles.Selectable, true);
391 SetStyle (ControlStyles.Opaque | ControlStyles.ResizeRedraw, true);
392 SetStyle (ControlStyles.StandardClick | ControlStyles.UseTextForAccessibility, false);
396 #region UIA Framework Events
397 static object UIAUpButtonClickEvent = new object ();
399 internal event EventHandler UIAUpButtonClick {
400 add { Events.AddHandler (UIAUpButtonClickEvent, value); }
401 remove { Events.RemoveHandler (UIAUpButtonClickEvent, value); }
404 internal void OnUIAUpButtonClick (EventArgs e)
406 EventHandler eh = (EventHandler) Events [UIAUpButtonClickEvent];
411 static object UIADownButtonClickEvent = new object ();
413 internal event EventHandler UIADownButtonClick {
414 add { Events.AddHandler (UIADownButtonClickEvent, value); }
415 remove { Events.RemoveHandler (UIADownButtonClickEvent, value); }
418 internal void OnUIADownButtonClick (EventArgs e)
420 EventHandler eh = (EventHandler) Events [UIADownButtonClickEvent];
426 #region Private Methods
427 private void TabIndexChangedHandler (object sender, EventArgs e)
429 txtView.TabIndex = TabIndex;
432 internal override void OnPaintInternal (PaintEventArgs e)
434 e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(BackColor), ClientRectangle);
437 #endregion // Private Methods
439 #region Public Instance Properties
441 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
442 [EditorBrowsable(EditorBrowsableState.Never)]
443 public override bool AutoScroll {
445 return base.AutoScroll;
449 base.AutoScroll = value;
454 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
455 [EditorBrowsable(EditorBrowsableState.Never)]
456 public new Size AutoScrollMargin {
457 get { return base.AutoScrollMargin; }
458 set { base.AutoScrollMargin = value; }
462 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
463 [EditorBrowsable(EditorBrowsableState.Never)]
464 public new Size AutoScrollMinSize {
465 get { return base.AutoScrollMinSize; }
466 set { base.AutoScrollMinSize = value; }
470 [EditorBrowsable (EditorBrowsableState.Always)]
471 [DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)]
472 public override bool AutoSize {
473 get { return base.AutoSize; }
474 set { base.AutoSize = value; }
477 public override Color BackColor {
479 return base.BackColor;
483 base.BackColor = value;
484 txtView.BackColor = value;
489 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
490 [EditorBrowsable(EditorBrowsableState.Never)]
491 public override Image BackgroundImage {
493 return base.BackgroundImage;
496 base.BackgroundImage = value;
497 txtView.BackgroundImage = value;
502 [EditorBrowsable (EditorBrowsableState.Never)]
503 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
504 public override ImageLayout BackgroundImageLayout {
505 get { return base.BackgroundImageLayout; }
506 set { base.BackgroundImageLayout = value; }
509 [DefaultValue(BorderStyle.Fixed3D)]
511 public BorderStyle BorderStyle {
512 get { return InternalBorderStyle; }
513 set { InternalBorderStyle = value; }
516 public override ContextMenu ContextMenu {
518 return base.ContextMenu;
521 base.ContextMenu = value;
522 txtView.ContextMenu = value;
523 spnSpinner.ContextMenu = value;
527 public override ContextMenuStrip ContextMenuStrip {
528 get { return base.ContextMenuStrip; }
530 base.ContextMenuStrip = value;
531 txtView.ContextMenuStrip = value;
532 spnSpinner.ContextMenuStrip = value;
537 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
538 [EditorBrowsable(EditorBrowsableState.Never)]
539 public new DockPaddingEdges DockPadding {
540 get { return base.DockPadding; }
544 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
545 public override bool Focused {
547 return txtView.Focused;
551 public override Color ForeColor {
553 return base.ForeColor;
556 base.ForeColor = value;
557 txtView.ForeColor = value;
562 public bool InterceptArrowKeys {
564 return _InterceptArrowKeys;
567 _InterceptArrowKeys = value;
571 public override Size MaximumSize {
572 get { return base.MaximumSize; }
573 set { base.MaximumSize = new Size (value.Width, 0); }
576 public override Size MinimumSize {
577 get { return base.MinimumSize; }
578 set { base.MinimumSize = new Size (value.Width, 0); }
582 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
583 [EditorBrowsable(EditorBrowsableState.Advanced)]
584 public int PreferredHeight {
586 // For some reason, the TextBox's PreferredHeight does not
587 // change when the Font property is assigned. Without a
588 // border, it will always be Font.Height anyway.
589 //int text_box_preferred_height = (txtView != null) ? txtView.PreferredHeight : Font.Height;
590 int text_box_preferred_height = Font.Height;
592 switch (border_style) {
593 case BorderStyle.FixedSingle:
594 case BorderStyle.Fixed3D:
595 text_box_preferred_height += 3; // magic number? :-)
597 return text_box_preferred_height + 4;
599 case BorderStyle.None:
601 return text_box_preferred_height;
606 [DefaultValue(false)]
607 public bool ReadOnly {
609 return txtView.ReadOnly;
612 txtView.ReadOnly = value;
617 public override string Text {
619 if (txtView != null) {
625 txtView.Text = value;
629 txtView.SelectionLength = 0;
633 [DefaultValue(HorizontalAlignment.Left)]
635 public HorizontalAlignment TextAlign {
637 return txtView.TextAlign;
640 txtView.TextAlign = value;
644 [DefaultValue(LeftRightAlignment.Right)]
646 public LeftRightAlignment UpDownAlign {
651 if (_UpDownAlign != value) {
652 _UpDownAlign = value;
654 if (value == LeftRightAlignment.Left)
655 spnSpinner.Dock = DockStyle.Left;
657 spnSpinner.Dock = DockStyle.Right;
661 #endregion // Public Instance Properties
663 #region Protected Instance Properties
664 protected bool ChangingText {
666 return changing_text;
669 changing_text = value;
673 protected override CreateParams CreateParams {
675 return base.CreateParams;
679 protected override Size DefaultSize {
681 return new Size(120, this.PreferredHeight);
685 protected bool UserEdit {
693 #endregion // Protected Instance Properties
695 #region Public Instance Methods
696 public abstract void DownButton ();
697 public void Select(int start, int length)
699 txtView.Select(start, length);
702 public abstract void UpButton ();
703 #endregion // Public Instance Methods
705 #region Protected Instance Methods
706 protected virtual void OnChanged (object source, EventArgs e)
710 protected override void OnFontChanged (EventArgs e)
712 txtView.Font = this.Font;
713 Height = PreferredHeight;
716 protected override void OnHandleCreated (EventArgs e)
718 base.OnHandleCreated (e);
721 protected override void OnHandleDestroyed (EventArgs e)
723 base.OnHandleDestroyed (e);
726 protected override void OnLayout (LayoutEventArgs e)
731 protected override void OnMouseDown (MouseEventArgs e)
733 base.OnMouseDown (e);
736 protected override void OnMouseUp (MouseEventArgs mevent)
738 base.OnMouseUp (mevent);
741 protected override void OnMouseWheel (MouseEventArgs e)
745 else if (e.Delta < 0)
749 protected override void OnPaint (PaintEventArgs e)
754 protected virtual void OnTextBoxKeyDown (object source, KeyEventArgs e)
756 if (_InterceptArrowKeys) {
757 if ((e.KeyCode == Keys.Up) || (e.KeyCode == Keys.Down)) {
760 if (e.KeyCode == Keys.Up)
762 if (e.KeyCode == Keys.Down)
770 protected virtual void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
772 if (e.KeyChar == '\r') {
779 protected virtual void OnTextBoxLostFocus (object source, EventArgs e)
786 protected virtual void OnTextBoxResize (object source, EventArgs e)
788 // compute the new height, taking the border into account
789 Height = PreferredHeight;
791 // let anchoring reposition the controls
794 protected virtual void OnTextBoxTextChanged (object source, EventArgs e)
797 ChangingText = false;
804 internal override void SetBoundsCoreInternal(int x, int y, int width, int height, BoundsSpecified specified)
806 base.SetBoundsCoreInternal (x, y, width, Math.Min (width, PreferredHeight), specified);
809 protected abstract void UpdateEditText ();
811 protected virtual void ValidateEditText ()
813 // to be overridden by subclassers
816 [EditorBrowsable(EditorBrowsableState.Advanced)]
817 protected override void WndProc (ref Message m)
819 switch((Msg) m.Msg) {
823 XplatUI.SendMessage (txtView.Handle, (Msg) m.Msg, m.WParam, m.LParam);
825 case Msg.WM_SETFOCUS:
826 ActiveControl = txtView;
828 case Msg.WM_KILLFOCUS:
829 ActiveControl = null;
832 base.WndProc (ref m);
836 #endregion // Protected Instance Methods
840 [EditorBrowsable (EditorBrowsableState.Always)]
841 public new event EventHandler AutoSizeChanged {
842 add { base.AutoSizeChanged += value; }
843 remove { base.AutoSizeChanged -= value; }
847 [EditorBrowsable(EditorBrowsableState.Never)]
848 public new event EventHandler BackgroundImageChanged {
849 add { base.BackgroundImageChanged += value; }
850 remove { base.BackgroundImageChanged -= value; }
854 [EditorBrowsable (EditorBrowsableState.Never)]
855 public new event EventHandler BackgroundImageLayoutChanged {
856 add { base.BackgroundImageLayoutChanged += value; }
857 remove { base.BackgroundImageLayoutChanged -= value; }
861 [EditorBrowsable(EditorBrowsableState.Never)]
862 public new event EventHandler MouseEnter {
863 add { base.MouseEnter += value; }
864 remove { base.MouseEnter -= value; }
868 [EditorBrowsable(EditorBrowsableState.Never)]
869 public new event EventHandler MouseHover {
870 add { base.MouseHover += value; }
871 remove { base.MouseHover -= value; }
875 [EditorBrowsable(EditorBrowsableState.Never)]
876 public new event EventHandler MouseLeave {
877 add { base.MouseLeave += value; }
878 remove { base.MouseLeave -= value; }
882 [EditorBrowsable(EditorBrowsableState.Never)]
883 public new event MouseEventHandler MouseMove {
884 add { base.MouseMove += value; }
885 remove { base.MouseMove -= value; }