2 // System.Windows.Forms.ScrollBar.cs
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Copyright (C) 2004-2005, Novell, Inc.
26 // Jordi Mas i Hernandez jordi@ximian.com
33 using System.Drawing.Imaging;
34 using System.Drawing.Drawing2D;
35 using System.ComponentModel;
36 using System.Runtime.InteropServices;
38 namespace System.Windows.Forms
42 [ClassInterface (ClassInterfaceType.AutoDispatch)]
44 [DefaultEvent ("Scroll")]
45 [DefaultProperty ("Value")]
46 public abstract class ScrollBar : Control
48 #region Local Variables
52 private int large_change;
53 private int small_change;
54 internal int scrollbutton_height;
55 internal int scrollbutton_width;
56 private Rectangle first_arrow_area = new Rectangle (); // up or left
57 private Rectangle second_arrow_area = new Rectangle (); // down or right
58 private Rectangle thumb_pos = new Rectangle ();
59 private Rectangle thumb_area = new Rectangle ();
60 internal ButtonState firstbutton_state = ButtonState.Normal;
61 internal ButtonState secondbutton_state = ButtonState.Normal;
62 private bool firstbutton_pressed = false;
63 private bool secondbutton_pressed = false;
64 private bool thumb_pressed = false;
65 private float pixel_per_pos = 0;
66 private Timer timer = new Timer ();
67 private TimerType timer_type;
68 private int thumb_size = 40;
69 private const int thumb_min_size = 8;
70 private const int thumb_notshown_size = 40;
72 internal bool implicit_control;
73 private int lastclick_pos; // Position of the last button-down event
74 private int thumbclick_offset; // Position of the last button-down event relative to the thumb edge
75 private Rectangle dirty;
77 internal ThumbMoving thumb_moving = ThumbMoving.None;
78 #endregion // Local Variables
80 private enum TimerType
88 internal enum ThumbMoving
98 [EditorBrowsable (EditorBrowsableState.Never)]
99 public new event EventHandler AutoSizeChanged {
100 add { base.AutoSizeChanged += value; }
101 remove { base.AutoSizeChanged -= value; }
106 [EditorBrowsable (EditorBrowsableState.Never)]
107 public new event EventHandler BackColorChanged {
108 add { base.BackColorChanged += value; }
109 remove { base.BackColorChanged -= value; }
113 [EditorBrowsable (EditorBrowsableState.Never)]
114 public new event EventHandler BackgroundImageChanged {
115 add { base.BackgroundImageChanged += value; }
116 remove { base.BackgroundImageChanged -= value; }
121 [EditorBrowsable (EditorBrowsableState.Never)]
122 public new event EventHandler BackgroundImageLayoutChanged {
123 add { base.BackgroundImageLayoutChanged += value; }
124 remove { base.BackgroundImageLayoutChanged -= value; }
129 [EditorBrowsable (EditorBrowsableState.Never)]
130 public new event EventHandler Click {
131 add { base.Click += value; }
132 remove { base.Click -= value; }
136 [EditorBrowsable (EditorBrowsableState.Never)]
137 public new event EventHandler DoubleClick {
138 add { base.DoubleClick += value; }
139 remove { base.DoubleClick -= value; }
143 [EditorBrowsable (EditorBrowsableState.Never)]
144 public new event EventHandler FontChanged {
145 add { base.FontChanged += value; }
146 remove { base.FontChanged -= value; }
150 [EditorBrowsable (EditorBrowsableState.Never)]
151 public new event EventHandler ForeColorChanged {
152 add { base.ForeColorChanged += value; }
153 remove { base.ForeColorChanged -= value; }
157 [EditorBrowsable (EditorBrowsableState.Never)]
158 public new event EventHandler ImeModeChanged {
159 add { base.ImeModeChanged += value; }
160 remove { base.ImeModeChanged -= value; }
165 [EditorBrowsable (EditorBrowsableState.Never)]
166 public new event MouseEventHandler MouseClick {
167 add { base.MouseClick += value; }
168 remove { base.MouseClick -= value; }
172 [EditorBrowsable (EditorBrowsableState.Never)]
173 public new event MouseEventHandler MouseDoubleClick {
174 add { base.MouseDoubleClick += value; }
175 remove { base.MouseDoubleClick -= value; }
180 [EditorBrowsable (EditorBrowsableState.Never)]
181 public new event MouseEventHandler MouseDown {
182 add { base.MouseDown += value; }
183 remove { base.MouseDown -= value; }
187 [EditorBrowsable (EditorBrowsableState.Never)]
188 public new event MouseEventHandler MouseMove {
189 add { base.MouseMove += value; }
190 remove { base.MouseMove -= value; }
194 [EditorBrowsable (EditorBrowsableState.Never)]
195 public new event MouseEventHandler MouseUp {
196 add { base.MouseUp += value; }
197 remove { base.MouseUp -= value; }
201 [EditorBrowsable (EditorBrowsableState.Never)]
202 public new event PaintEventHandler Paint {
203 add { base.Paint += value; }
204 remove { base.Paint -= value; }
207 static object ScrollEvent = new object ();
208 static object ValueChangedEvent = new object ();
210 public event ScrollEventHandler Scroll {
211 add { Events.AddHandler (ScrollEvent, value); }
212 remove { Events.RemoveHandler (ScrollEvent, value); }
216 [EditorBrowsable (EditorBrowsableState.Never)]
217 public new event EventHandler TextChanged {
218 add { base.TextChanged += value; }
219 remove { base.TextChanged -= value; }
222 public event EventHandler ValueChanged {
223 add { Events.AddHandler (ValueChangedEvent, value); }
224 remove { Events.RemoveHandler (ValueChangedEvent, value); }
236 timer.Tick += new EventHandler (OnTimer);
237 base.KeyDown += new KeyEventHandler (OnKeyDownSB);
238 base.MouseDown += new MouseEventHandler (OnMouseDownSB);
239 base.MouseUp += new MouseEventHandler (OnMouseUpSB);
240 base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
241 base.Resize += new EventHandler (OnResizeSB);
242 base.TabStop = false;
243 base.Cursor = Cursors.Default;
245 SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
247 | ControlStyles.UseTextForAccessibility
252 #region Internal & Private Properties
253 internal Rectangle FirstArrowArea {
255 return this.first_arrow_area;
259 this.first_arrow_area = value;
263 internal Rectangle SecondArrowArea {
265 return this.second_arrow_area;
269 this.second_arrow_area = value;
273 internal Rectangle ThumbPos {
282 #endregion // Internal & Private Properties
284 #region Public Properties
286 [EditorBrowsable (EditorBrowsableState.Never)]
288 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
289 public override bool AutoSize {
290 get { return base.AutoSize; }
291 set { base.AutoSize = value; }
295 [EditorBrowsable (EditorBrowsableState.Never)]
297 public override Color BackColor
299 get { return base.BackColor; }
301 if (base.BackColor == value)
303 base.BackColor = value;
308 [EditorBrowsable (EditorBrowsableState.Never)]
310 public override Image BackgroundImage
312 get { return base.BackgroundImage; }
314 if (base.BackgroundImage == value)
317 base.BackgroundImage = value;
322 [EditorBrowsable (EditorBrowsableState.Never)]
324 public override ImageLayout BackgroundImageLayout {
325 get { return base.BackgroundImageLayout; }
326 set { base.BackgroundImageLayout = value; }
330 protected override CreateParams CreateParams
332 get { return base.CreateParams; }
336 protected override Padding DefaultMargin {
337 get { return Padding.Empty; }
341 protected override ImeMode DefaultImeMode
343 get { return ImeMode.Disable; }
346 [EditorBrowsable (EditorBrowsableState.Never)]
348 public override Font Font
350 get { return base.Font; }
352 if (base.Font.Equals (value))
359 [EditorBrowsable (EditorBrowsableState.Never)]
361 public override Color ForeColor
363 get { return base.ForeColor; }
365 if (base.ForeColor == value)
368 base.ForeColor = value;
373 [EditorBrowsable (EditorBrowsableState.Never)]
375 public new ImeMode ImeMode
377 get { return base.ImeMode; }
379 if (base.ImeMode == value)
382 base.ImeMode = value;
387 [RefreshProperties(RefreshProperties.Repaint)]
388 [MWFDescription("Scroll amount when clicking in the scroll area"), MWFCategory("Behaviour")]
389 public int LargeChange {
391 if (large_change > maximum)
392 return (maximum + 1);
399 throw new ArgumentOutOfRangeException ("LargeChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
401 throw new ArgumentException( string.Format("Value '{0}' must be greater than or equal to 0.", value));
404 if (large_change != value) {
405 large_change = value;
407 // thumb area depends on large change value,
408 // so we need to recalculate it.
410 UpdatePos (Value, true);
417 [RefreshProperties(RefreshProperties.Repaint)]
418 [MWFDescription("Highest value for scrollbar"), MWFCategory("Behaviour")]
420 get { return maximum; }
422 if (maximum == value)
427 if (maximum < minimum)
432 // thumb area depends on maximum value,
433 // so we need to recalculate it.
435 UpdatePos (Value, true);
440 internal void SetValues (int maximum, int large_change)
442 SetValues (-1, maximum, -1, large_change);
445 internal void SetValues (int minimum, int maximum, int small_change, int large_change)
449 if (-1 != minimum && this.minimum != minimum) {
450 this.minimum = minimum;
452 if (minimum > this.maximum)
453 this.maximum = minimum;
456 // change the position if it is out of range now
457 position = Math.Max (position, minimum);
460 if (-1 != maximum && this.maximum != maximum) {
461 this.maximum = maximum;
463 if (maximum < this.minimum)
464 this.minimum = maximum;
467 // change the position if it is out of range now
468 position = Math.Min (position, maximum);
471 if (-1 != small_change && this.small_change != small_change) {
472 this.small_change = small_change;
475 if (this.large_change != large_change) {
476 this.large_change = large_change;
482 UpdatePos (Value, true);
488 [RefreshProperties(RefreshProperties.Repaint)]
489 [MWFDescription("Smallest value for scrollbar"), MWFCategory("Behaviour")]
491 get { return minimum; }
493 if (minimum == value)
498 if (minimum > maximum)
501 // thumb area depends on minimum value,
502 // so we need to recalculate it.
504 UpdatePos (Value, true);
510 [MWFDescription("Scroll amount when clicking scroll arrows"), MWFCategory("Behaviour")]
511 public int SmallChange {
512 get { return small_change; }
516 throw new ArgumentOutOfRangeException ("SmallChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
518 throw new ArgumentException( string.Format("Value '{0}' must be greater than or equal to 0.", value));
521 if (small_change != value) {
522 small_change = value;
523 UpdatePos (Value, true);
529 [DefaultValue (false)]
530 public new bool TabStop {
531 get { return base.TabStop; }
532 set { base.TabStop = value; }
535 [EditorBrowsable (EditorBrowsableState.Never)]
538 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539 public override string Text {
540 get { return base.Text; }
541 set { base.Text = value; }
546 [MWFDescription("Current value for scrollbar"), MWFCategory("Behaviour")]
548 get { return position; }
550 if ( value < minimum || value > maximum )
552 throw new ArgumentOutOfRangeException ("Value", string.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
554 throw new ArgumentException (string.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
557 if (position != value){
560 OnValueChanged (EventArgs.Empty);
562 if (IsHandleCreated) {
563 Rectangle thumb_rect = thumb_pos;
565 UpdateThumbPos ((vert ? thumb_area.Y : thumb_area.X) + (int)(((float)(position - minimum)) * pixel_per_pos), false, false);
567 MoveThumb (thumb_rect, vert ? thumb_pos.Y : thumb_pos.X);
573 #endregion //Public Properties
575 #region Public Methods
577 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
579 // Basically, we want to keep our small edge and scale the long edge
580 // ie: if we are vertical, don't scale our width
582 return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Height) | (specified & BoundsSpecified.Location));
584 return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Width) | (specified & BoundsSpecified.Location));
588 protected override void OnEnabledChanged (EventArgs e)
590 base.OnEnabledChanged (e);
593 firstbutton_state = secondbutton_state = ButtonState.Normal;
595 firstbutton_state = secondbutton_state = ButtonState.Inactive;
600 protected override void OnHandleCreated (System.EventArgs e)
602 base.OnHandleCreated (e);
606 UpdateThumbPos (thumb_area.Y + (int)(((float)(position - minimum)) * pixel_per_pos), true, false);
609 protected virtual void OnScroll (ScrollEventArgs se)
611 ScrollEventHandler eh = (ScrollEventHandler)(Events [ScrollEvent]);
615 if (se.NewValue < Minimum) {
616 se.NewValue = Minimum;
619 if (se.NewValue > Maximum) {
620 se.NewValue = Maximum;
626 private void SendWMScroll(ScrollBarCommands cmd) {
627 if ((Parent != null) && Parent.IsHandleCreated) {
629 XplatUI.SendMessage(Parent.Handle, Msg.WM_VSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
631 XplatUI.SendMessage(Parent.Handle, Msg.WM_HSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
636 protected virtual void OnValueChanged (EventArgs e)
638 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
643 public override string ToString()
645 return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
646 GetType( ).FullName.ToString( ), minimum, maximum, position);
649 protected void UpdateScrollInfo ()
654 protected override void WndProc (ref Message m)
656 base.WndProc (ref m);
659 #endregion //Public Methods
661 #region Private Methods
663 private void CalcButtonSizes ()
666 if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
667 scrollbutton_height = Height /2;
669 scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
672 if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
673 scrollbutton_width = Width /2;
675 scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
679 private void CalcThumbArea ()
684 thumb_area.Height = Height - scrollbutton_height - scrollbutton_height;
686 thumb_area.Y = scrollbutton_height;
687 thumb_area.Width = Width;
689 if (Height < thumb_notshown_size)
692 double per = ((double) this.LargeChange / (double)((1 + maximum - minimum)));
693 thumb_size = 1 + (int) (thumb_area.Height * per);
695 if (thumb_size < thumb_min_size)
696 thumb_size = thumb_min_size;
699 pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - this.LargeChange) + 1));
704 thumb_area.X = scrollbutton_width;
705 thumb_area.Height = Height;
706 thumb_area.Width = Width - scrollbutton_width - scrollbutton_width;
708 if (Width < thumb_notshown_size)
711 double per = ((double) this.LargeChange / (double)((1 + maximum - minimum)));
712 thumb_size = 1 + (int) (thumb_area.Width * per);
714 if (thumb_size < thumb_min_size)
715 thumb_size = thumb_min_size;
718 pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - this.LargeChange) + 1));
722 private void LargeIncrement ()
724 ScrollEventArgs event_args;
725 int pos = Math.Min (Maximum - large_change + 1, position + large_change);
727 event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
728 OnScroll (event_args);
729 Value = event_args.NewValue;
731 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
732 OnScroll (event_args);
733 Value = event_args.NewValue;
736 private void LargeDecrement ()
738 ScrollEventArgs event_args;
739 int pos = Math.Max (Minimum, position - large_change);
741 event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
742 OnScroll (event_args);
743 Value = event_args.NewValue;
745 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
746 OnScroll (event_args);
747 Value = event_args.NewValue;
750 private void OnResizeSB (Object o, EventArgs e)
752 if (Width <= 0 || Height <= 0)
757 UpdatePos (position, true);
762 internal override void OnPaintInternal (PaintEventArgs pevent)
764 ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
767 private void OnTimer (Object source, EventArgs e)
771 switch (timer_type) {
773 case TimerType.HoldButton:
774 SetRepeatButtonTimer ();
777 case TimerType.RepeatButton:
779 if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Minimum) {
781 SendWMScroll(ScrollBarCommands.SB_LINEUP);
784 if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Maximum) {
786 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
792 case TimerType.HoldThumbArea:
793 SetRepeatThumbAreaTimer ();
796 case TimerType.RepeatThumbArea:
798 Point pnt, pnt_screen;
799 Rectangle thumb_area_screen = thumb_area;
801 pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
802 thumb_area_screen.X = pnt_screen.X;
803 thumb_area_screen.Y = pnt_screen.Y;
805 if (thumb_area_screen.Contains (MousePosition) == false) {
806 timer.Enabled = false;
807 thumb_moving = ThumbMoving.None;
812 pnt = PointToClient (MousePosition);
815 lastclick_pos = pnt.Y;
817 lastclick_pos = pnt.X;
819 if (thumb_moving == ThumbMoving.Forward) {
820 if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
821 (!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
822 (thumb_area.Contains (pnt) == false)) {
823 timer.Enabled = false;
824 thumb_moving = ThumbMoving.None;
829 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
832 if ((vert && (thumb_pos.Y < lastclick_pos)) ||
833 (!vert && (thumb_pos.X < lastclick_pos))){
834 timer.Enabled = false;
835 thumb_moving = ThumbMoving.None;
836 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
840 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
853 private void MoveThumb (Rectangle original_thumbpos, int value)
855 /* so, the reason this works can best be
856 * described by the following 1 dimensional
859 * say you have a scrollbar thumb positioned
862 * <---------------------| |------------------------------>
864 * and you want it to end up looking like this:
866 * <-----------------------------| |---------------------->
868 * that can be done with the scrolling api by
869 * extending the rectangle to encompass both
872 * start of range end of range
874 * <---------------------| |-------|---------------------->
876 * so, we end up scrolling just this little region:
880 * and end up with ********| |
882 * where ****** is space that is automatically
885 * It's clear that in both cases (left to
886 * right, right to left) we need to extend the
887 * size of the scroll rectangle to encompass
888 * both. In the right to left case, we also
889 * need to decrement the X coordinate.
891 * We call Update after scrolling to make sure
892 * there's no garbage left in the window to be
893 * copied again if we're called before the
894 * paint events have been handled.
900 delta = value - original_thumbpos.Y;
903 original_thumbpos.Y += delta;
904 original_thumbpos.Height -= delta;
907 original_thumbpos.Height += delta;
910 XplatUI.ScrollWindow (Handle, original_thumbpos, 0, delta, false);
913 delta = value - original_thumbpos.X;
916 original_thumbpos.X += delta;
917 original_thumbpos.Width -= delta;
920 original_thumbpos.Width += delta;
923 XplatUI.ScrollWindow (Handle, original_thumbpos, delta, 0, false);
929 private void OnMouseMoveSB (object sender, MouseEventArgs e)
931 if (Enabled == false || thumb_size == 0)
934 if (firstbutton_pressed) {
935 if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
936 firstbutton_state = ButtonState.Normal;
937 Invalidate (first_arrow_area);
940 } else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
941 firstbutton_state = ButtonState.Pushed;
942 Invalidate (first_arrow_area);
946 } else if (secondbutton_pressed) {
947 if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
948 secondbutton_state = ButtonState.Normal;
949 Invalidate (second_arrow_area);
952 } else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
953 secondbutton_state = ButtonState.Pushed;
954 Invalidate (second_arrow_area);
958 } else if (thumb_pressed == true) {
960 int thumb_edge = e.Y - thumbclick_offset;
962 if (thumb_edge < thumb_area.Y)
963 thumb_edge = thumb_area.Y;
964 else if (thumb_edge > thumb_area.Bottom - thumb_size)
965 thumb_edge = thumb_area.Bottom - thumb_size;
967 if (thumb_edge != thumb_pos.Y) {
968 Rectangle thumb_rect = thumb_pos;
970 UpdateThumbPos (thumb_edge, false, true);
972 MoveThumb (thumb_rect, thumb_pos.Y);
974 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
976 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
978 int thumb_edge = e.X - thumbclick_offset;
980 if (thumb_edge < thumb_area.X)
981 thumb_edge = thumb_area.X;
982 else if (thumb_edge > thumb_area.Right - thumb_size)
983 thumb_edge = thumb_area.Right - thumb_size;
985 if (thumb_edge != thumb_pos.X) {
986 Rectangle thumb_rect = thumb_pos;
988 UpdateThumbPos (thumb_edge, false, true);
990 MoveThumb (thumb_rect, thumb_pos.X);
992 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
994 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1001 private void OnMouseDownSB (object sender, MouseEventArgs e)
1005 if (Enabled == false)
1008 if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
1009 SendWMScroll(ScrollBarCommands.SB_LINEUP);
1010 firstbutton_state = ButtonState.Pushed;
1011 firstbutton_pressed = true;
1012 Invalidate (first_arrow_area);
1014 if (!timer.Enabled) {
1015 SetHoldButtonClickTimer ();
1016 timer.Enabled = true;
1020 if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
1021 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1022 secondbutton_state = ButtonState.Pushed;
1023 secondbutton_pressed = true;
1024 Invalidate (second_arrow_area);
1026 if (!timer.Enabled) {
1027 SetHoldButtonClickTimer ();
1028 timer.Enabled = true;
1032 if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
1033 thumb_pressed = true;
1034 SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1036 thumbclick_offset = e.Y - thumb_pos.Y;
1037 lastclick_pos = e.Y;
1040 thumbclick_offset = e.X - thumb_pos.X;
1041 lastclick_pos = e.X;
1044 if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
1047 lastclick_pos = e.Y;
1049 if (e.Y > thumb_pos.Y + thumb_pos.Height) {
1050 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1052 thumb_moving = ThumbMoving.Forward;
1053 Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1054 ClientRectangle.Width,
1055 ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
1056 scrollbutton_height));
1058 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1060 thumb_moving = ThumbMoving.Backwards;
1061 Dirty (new Rectangle (0, scrollbutton_height,
1062 ClientRectangle.Width,
1063 thumb_pos.Y - scrollbutton_height));
1067 lastclick_pos = e.X;
1069 if (e.X > thumb_pos.X + thumb_pos.Width) {
1070 SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1071 thumb_moving = ThumbMoving.Forward;
1073 Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1074 ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
1076 ClientRectangle.Height));
1078 SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1079 thumb_moving = ThumbMoving.Backwards;
1081 Dirty (new Rectangle (scrollbutton_width, 0,
1082 thumb_pos.X - scrollbutton_width,
1083 ClientRectangle.Height));
1087 SetHoldThumbAreaTimer ();
1088 timer.Enabled = true;
1094 private void OnMouseUpSB (object sender, MouseEventArgs e)
1098 if (Enabled == false)
1101 timer.Enabled = false;
1102 if (thumb_moving != ThumbMoving.None) {
1104 thumb_moving = ThumbMoving.None;
1107 if (firstbutton_pressed) {
1108 firstbutton_state = ButtonState.Normal;
1109 if (first_arrow_area.Contains (e.X, e.Y)) {
1112 SendWMScroll(ScrollBarCommands.SB_LINEUP);
1113 firstbutton_pressed = false;
1114 Dirty (first_arrow_area);
1115 } else if (secondbutton_pressed) {
1116 secondbutton_state = ButtonState.Normal;
1117 if (second_arrow_area.Contains (e.X, e.Y)) {
1120 SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1121 Dirty (second_arrow_area);
1122 secondbutton_pressed = false;
1123 } else if (thumb_pressed == true) {
1124 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
1125 OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
1126 SendWMScroll(ScrollBarCommands.SB_THUMBPOSITION);
1127 thumb_pressed = false;
1134 private void OnKeyDownSB (Object o, KeyEventArgs key)
1136 if (Enabled == false)
1141 switch (key.KeyCode){
1179 // I hate to do this, but we don't have the resources to track
1180 // down everything internal that is setting a value outside the
1181 // correct range, so we'll clamp it to the acceptable values.
1182 internal void SafeValueSet (int value)
1184 value = Math.Min (value, maximum);
1185 value = Math.Max (value, minimum);
1190 private void SetEndPosition ()
1192 ScrollEventArgs event_args;
1193 int pos = Maximum - large_change + 1;
1195 event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
1196 OnScroll (event_args);
1197 pos = event_args.NewValue;
1199 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1200 OnScroll (event_args);
1201 pos = event_args.NewValue;
1206 private void SetHomePosition ()
1208 ScrollEventArgs event_args;
1211 event_args = new ScrollEventArgs (ScrollEventType.First, pos);
1212 OnScroll (event_args);
1213 pos = event_args.NewValue;
1215 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1216 OnScroll (event_args);
1217 pos = event_args.NewValue;
1222 private void SmallIncrement ()
1224 ScrollEventArgs event_args;
1225 int pos = Math.Min (Maximum - large_change + 1, position + small_change);
1227 event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
1228 OnScroll (event_args);
1229 Value = event_args.NewValue;
1231 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1232 OnScroll (event_args);
1233 Value = event_args.NewValue;
1236 private void SmallDecrement ()
1238 ScrollEventArgs event_args;
1239 int pos = Math.Max (Minimum, position - small_change);
1241 event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
1242 OnScroll (event_args);
1243 Value = event_args.NewValue;
1245 event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1246 OnScroll (event_args);
1247 Value = event_args.NewValue;
1250 private void SetHoldButtonClickTimer ()
1252 timer.Enabled = false;
1253 timer.Interval = 200;
1254 timer_type = TimerType.HoldButton;
1255 timer.Enabled = true;
1258 private void SetRepeatButtonTimer ()
1260 timer.Enabled = false;
1261 timer.Interval = 50;
1262 timer_type = TimerType.RepeatButton;
1263 timer.Enabled = true;
1266 private void SetHoldThumbAreaTimer ()
1268 timer.Enabled = false;
1269 timer.Interval = 200;
1270 timer_type = TimerType.HoldThumbArea;
1271 timer.Enabled = true;
1274 private void SetRepeatThumbAreaTimer ()
1276 timer.Enabled = false;
1277 timer.Interval = 50;
1278 timer_type = TimerType.RepeatThumbArea;
1279 timer.Enabled = true;
1282 private void UpdatePos (int newPos, bool update_thumbpos)
1286 if (newPos < minimum)
1289 if (newPos > maximum + 1 - large_change)
1290 pos = maximum + 1 - large_change;
1294 // pos can't be less than minimum
1298 if (update_thumbpos) {
1300 UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1302 UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1306 position = pos; // Updates directly the value to avoid thumb pos update
1309 // XXX some reason we don't call OnValueChanged?
1310 EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
1312 eh (this, EventArgs.Empty);
1316 private void UpdateThumbPos (int pixel, bool dirty, bool update_value)
1323 if (pixel < thumb_area.Y)
1324 thumb_pos.Y = thumb_area.Y;
1325 else if (pixel > thumb_area.Bottom - thumb_size)
1326 thumb_pos.Y = thumb_area.Bottom - thumb_size;
1328 thumb_pos.Y = pixel;
1331 thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
1332 thumb_pos.Height = thumb_size;
1333 new_pos = (float) (thumb_pos.Y - thumb_area.Y);
1334 new_pos = new_pos / pixel_per_pos;
1340 if (pixel < thumb_area.X)
1341 thumb_pos.X = thumb_area.X;
1342 else if (pixel > thumb_area.Right - thumb_size)
1343 thumb_pos.X = thumb_area.Right - thumb_size;
1345 thumb_pos.X = pixel;
1348 thumb_pos.Width = thumb_size;
1349 thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
1350 new_pos = (float) (thumb_pos.X - thumb_area.X);
1351 new_pos = new_pos / pixel_per_pos;
1358 UpdatePos ((int) new_pos + minimum, false);
1361 private void SetValue (int value)
1363 if ( value < minimum || value > maximum )
1364 throw new ArgumentException(
1365 String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
1367 if (position != value){
1370 OnValueChanged (EventArgs.Empty);
1371 UpdatePos (value, true);
1375 private void ClearDirty ()
1377 dirty = Rectangle.Empty;
1380 private void Dirty (Rectangle r)
1382 if (dirty == Rectangle.Empty) {
1386 dirty = Rectangle.Union (dirty, r);
1389 private void DirtyThumbArea ()
1391 if (thumb_moving == ThumbMoving.Forward) {
1393 Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1394 ClientRectangle.Width,
1395 ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
1396 scrollbutton_height));
1398 Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1399 ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
1401 ClientRectangle.Height));
1403 } else if (thumb_moving == ThumbMoving.Backwards) {
1405 Dirty(new Rectangle (0, scrollbutton_height,
1406 ClientRectangle.Width,
1407 thumb_pos.Y - scrollbutton_height));
1409 Dirty (new Rectangle (scrollbutton_width, 0,
1410 thumb_pos.X - scrollbutton_width,
1411 ClientRectangle.Height));
1416 private void InvalidateDirty ()
1420 dirty = Rectangle.Empty;
1423 #endregion //Private Methods
1425 protected override void OnMouseWheel (MouseEventArgs e)
1427 base.OnMouseWheel (e);