1 //
2 // System.Windows.Forms.ScrollBar.cs
3 //
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:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
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.
22 //
23 // Copyright (C) 2004-2005, Novell, Inc.
24 //
25 // Authors:
26 //	Jordi Mas i Hernandez	jordi@ximian.com
27 //
28 //
29 
30 // COMPLETE
31 
32 using System.Drawing;
33 using System.Drawing.Imaging;
34 using System.Drawing.Drawing2D;
35 using System.ComponentModel;
36 using System.Runtime.InteropServices;
37 
38 namespace System.Windows.Forms
39 {
40 	[ComVisible (true)]
41 	[ClassInterface (ClassInterfaceType.AutoDispatch)]
42 	[DefaultEvent ("Scroll")]
43 	[DefaultProperty ("Value")]
44 	public abstract class ScrollBar : Control
45 	{
46 		#region Local Variables
47 		private int position;
48 		private int minimum;
49 		private int maximum;
50 		private int large_change;
51 		private int small_change;
52 		internal int scrollbutton_height;
53 		internal int scrollbutton_width;
54 		private Rectangle first_arrow_area = new Rectangle ();		// up or left
55 		private Rectangle second_arrow_area = new Rectangle ();		// down or right
56 		private Rectangle thumb_pos = new Rectangle ();
57 		private Rectangle thumb_area = new Rectangle ();
58 		internal ButtonState firstbutton_state = ButtonState.Normal;
59 		internal ButtonState secondbutton_state = ButtonState.Normal;
60 		private bool firstbutton_pressed = false;
61 		private bool secondbutton_pressed = false;
62 		private bool thumb_pressed = false;
63 		private float pixel_per_pos = 0;
64 		private Timer timer = new Timer ();
65 		private TimerType timer_type;
66 		private int thumb_size = 40;
67 		private const int thumb_min_size = 8;
68 		private const int thumb_notshown_size = 40;
69 		internal bool use_manual_thumb_size;
70 		internal int manual_thumb_size;
71 		internal bool vert;
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;
76 
77 		internal ThumbMoving thumb_moving = ThumbMoving.None;
78 		bool first_button_entered;
79 		bool second_button_entered;
80 		bool thumb_entered;
81 		#endregion	// Local Variables
82 
83 		private enum TimerType
84 		{
85 			HoldButton,
86 			RepeatButton,
87 			HoldThumbArea,
88 			RepeatThumbArea
89 		}
90 
91 		internal enum ThumbMoving
92 		{
93 			None,
94 			Forward,
95 			Backwards,
96 		}
97 
98 		#region events
99 		[Browsable (false)]
100 		[EditorBrowsable (EditorBrowsableState.Never)]
101 		public new event EventHandler AutoSizeChanged {
102 			add { base.AutoSizeChanged += value; }
103 			remove { base.AutoSizeChanged -= value; }
104 		}
105 
106 		[Browsable (false)]
107 		[EditorBrowsable (EditorBrowsableState.Never)]
108 		public new event EventHandler BackColorChanged {
109 			add { base.BackColorChanged += value; }
110 			remove { base.BackColorChanged -= value; }
111 		}
112 
113 		[Browsable (false)]
114 		[EditorBrowsable (EditorBrowsableState.Never)]
115 		public new event EventHandler BackgroundImageChanged {
116 			add { base.BackgroundImageChanged += value; }
117 			remove { base.BackgroundImageChanged -= value; }
118 		}
119 
120 		[Browsable (false)]
121 		[EditorBrowsable (EditorBrowsableState.Never)]
122 		public new event EventHandler BackgroundImageLayoutChanged {
123 			add { base.BackgroundImageLayoutChanged += value; }
124 			remove { base.BackgroundImageLayoutChanged -= value; }
125 		}
126 
127 		[Browsable (false)]
128 		[EditorBrowsable (EditorBrowsableState.Never)]
129 		public new event EventHandler Click {
130 			add { base.Click += value; }
131 			remove { base.Click -= value; }
132 		}
133 
134 		[Browsable (false)]
135 		[EditorBrowsable (EditorBrowsableState.Never)]
136 		public new event EventHandler DoubleClick {
137 			add { base.DoubleClick += value; }
138 			remove { base.DoubleClick -= value; }
139 		}
140 
141 		[Browsable (false)]
142 		[EditorBrowsable (EditorBrowsableState.Never)]
143 		public new event EventHandler FontChanged {
144 			add { base.FontChanged += value; }
145 			remove { base.FontChanged -= value; }
146 		}
147 
148 		[Browsable (false)]
149 		[EditorBrowsable (EditorBrowsableState.Never)]
150 		public new event EventHandler ForeColorChanged {
151 			add { base.ForeColorChanged += value; }
152 			remove { base.ForeColorChanged -= value; }
153 		}
154 
155 		[Browsable (false)]
156 		[EditorBrowsable (EditorBrowsableState.Never)]
157 		public new event EventHandler ImeModeChanged {
158 			add { base.ImeModeChanged += value; }
159 			remove { base.ImeModeChanged -= value; }
160 		}
161 
162 		[Browsable (false)]
163 		[EditorBrowsable (EditorBrowsableState.Never)]
164 		public new event MouseEventHandler MouseClick {
165 			add { base.MouseClick += value; }
166 			remove { base.MouseClick -= value; }
167 		}
168 
169 		[Browsable (false)]
170 		[EditorBrowsable (EditorBrowsableState.Never)]
171 		public new event MouseEventHandler MouseDoubleClick {
172 			add { base.MouseDoubleClick += value; }
173 			remove { base.MouseDoubleClick -= value; }
174 		}
175 
176 		[Browsable (false)]
177 		[EditorBrowsable (EditorBrowsableState.Never)]
178 		public new event MouseEventHandler MouseDown {
179 			add { base.MouseDown += value; }
180 			remove { base.MouseDown -= value; }
181 		}
182 
183 		[Browsable (false)]
184 		[EditorBrowsable (EditorBrowsableState.Never)]
185 		public new event MouseEventHandler MouseMove {
186 			add { base.MouseMove += value; }
187 			remove { base.MouseMove -= value; }
188 		}
189 
190 		[Browsable (false)]
191 		[EditorBrowsable (EditorBrowsableState.Never)]
192 		public new event MouseEventHandler MouseUp {
193 			add { base.MouseUp += value; }
194 			remove { base.MouseUp -= value; }
195 		}
196 
197 		[Browsable (false)]
198 		[EditorBrowsable (EditorBrowsableState.Never)]
199 		public new event PaintEventHandler Paint {
200 			add { base.Paint += value; }
201 			remove { base.Paint -= value; }
202 		}
203 
204 		static object ScrollEvent = new object ();
205 		static object ValueChangedEvent = new object ();
206 
207 		public event ScrollEventHandler Scroll {
208 			add { Events.AddHandler (ScrollEvent, value); }
209 			remove { Events.RemoveHandler (ScrollEvent, value); }
210 		}
211 
212 		[Browsable (false)]
213 		[EditorBrowsable (EditorBrowsableState.Never)]
214 		public new event EventHandler TextChanged {
215 			add { base.TextChanged += value; }
216 			remove { base.TextChanged -= value; }
217 		}
218 
219 		public event EventHandler ValueChanged {
220 			add { Events.AddHandler (ValueChangedEvent, value); }
221 			remove { Events.RemoveHandler (ValueChangedEvent, value); }
222 		}
223 		#endregion Events
224 
ScrollBar()225 		public ScrollBar ()
226 		{
227 			position = 0;
228 			minimum = 0;
229 			maximum = 100;
230 			large_change = 10;
231 			small_change = 1;
232 
233 			timer.Tick += new EventHandler (OnTimer);
234 			MouseEnter += new EventHandler (OnMouseEnter);
235 			MouseLeave += new EventHandler (OnMouseLeave);
236 			base.KeyDown += new KeyEventHandler (OnKeyDownSB);
237 			base.MouseDown += new MouseEventHandler (OnMouseDownSB);
238 			base.MouseUp += new MouseEventHandler (OnMouseUpSB);
239 			base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
240 			base.Resize += new EventHandler (OnResizeSB);
241 			base.TabStop = false;
242 			base.Cursor = Cursors.Default;
243 
244 			SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.UseTextForAccessibility, false);
245 		}
246 
247 		#region Internal & Private Properties
248 		internal Rectangle FirstArrowArea {
249 			get {
250 				return this.first_arrow_area;
251 			}
252 
253 			set {
254 				this.first_arrow_area = value;
255 			}
256 		}
257 
258 		internal Rectangle SecondArrowArea {
259 			get {
260 				return this.second_arrow_area;
261 			}
262 
263 			set {
264 				this.second_arrow_area = value;
265 			}
266 		}
267 
268 		int MaximumAllowed {
269 			get {
270 				return use_manual_thumb_size ? maximum - manual_thumb_size + 1 :
271 					maximum - LargeChange + 1;
272 			}
273 		}
274 
275 		internal Rectangle ThumbPos {
276 			get {
277 				return thumb_pos;
278 			}
279 
280 			set {
281 				thumb_pos = value;
282 			}
283 		}
284 
285 		internal bool FirstButtonEntered {
286 			get { return first_button_entered; }
287 			private set {
288 				if (first_button_entered == value)
289 					return;
290 				first_button_entered = value;
291 				if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
292 					Invalidate (first_arrow_area);
293 			}
294 		}
295 
296 		internal bool SecondButtonEntered {
297 			get { return second_button_entered; }
298 			private set {
299 				if (second_button_entered == value)
300 					return;
301 				second_button_entered = value;
302 				if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
303 					Invalidate (second_arrow_area);
304 			}
305 		}
306 
307 		internal bool ThumbEntered {
308 			get { return thumb_entered; }
309 			private set {
310 				if (thumb_entered == value)
311 					return;
312 				thumb_entered = value;
313 				if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
314 					Invalidate (thumb_pos);
315 			}
316 		}
317 
318 		internal bool ThumbPressed {
319 			get { return thumb_pressed; }
320 			private set {
321 				if (thumb_pressed == value)
322 					return;
323 				thumb_pressed = value;
324 				if (ThemeEngine.Current.ScrollBarHasPressedThumbStyle)
325 					Invalidate (thumb_pos);
326 			}
327 		}
328 
329 		#endregion	// Internal & Private Properties
330 
331 		#region Public Properties
332 		[EditorBrowsable (EditorBrowsableState.Never)]
333 		[Browsable (false)]
334 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
335 		public override bool AutoSize {
336 			get { return base.AutoSize; }
337 			set { base.AutoSize = value; }
338 		}
339 
340 		[EditorBrowsable (EditorBrowsableState.Never)]
341 		[Browsable (false)]
342 		public override Color BackColor
343 		{
344 			get { return base.BackColor; }
345 			set {
346 				if (base.BackColor == value)
347 					return;
348 				base.BackColor = value;
349 				Refresh ();
350 			}
351 		}
352 
353 		[EditorBrowsable (EditorBrowsableState.Never)]
354 		[Browsable (false)]
355 		public override Image BackgroundImage
356 		{
357 			get { return base.BackgroundImage; }
358 			set {
359 				if (base.BackgroundImage == value)
360 					return;
361 
362 				base.BackgroundImage = value;
363 			}
364 		}
365 
366 		[EditorBrowsable (EditorBrowsableState.Never)]
367 		[Browsable (false)]
368 		public override ImageLayout BackgroundImageLayout {
369 			get { return base.BackgroundImageLayout; }
370 			set { base.BackgroundImageLayout = value; }
371 		}
372 
373 		protected override CreateParams CreateParams
374 		{
375 			get {	return base.CreateParams; }
376 		}
377 
378 		protected override Padding DefaultMargin {
379 			get { return Padding.Empty; }
380 		}
381 
382 		protected override ImeMode DefaultImeMode
383 		{
384 			get { return ImeMode.Disable; }
385 		}
386 
387 		[EditorBrowsable (EditorBrowsableState.Never)]
388 		[Browsable (false)]
389 		public override Font Font
390 		{
391 			get { return base.Font; }
392 			set {
393 				if (base.Font.Equals (value))
394 					return;
395 
396 				base.Font = value;
397 			}
398 		}
399 
400 		[EditorBrowsable (EditorBrowsableState.Never)]
401 		[Browsable (false)]
402 		public override Color ForeColor
403 		{
404 			get { return base.ForeColor; }
405 			set {
406 				if (base.ForeColor == value)
407 					return;
408 
409 				base.ForeColor = value;
410 				Refresh ();
411 			}
412 		}
413 
414 		[EditorBrowsable (EditorBrowsableState.Never)]
415 		[Browsable (false)]
416 		public new ImeMode ImeMode
417 		{
418 			get { return base.ImeMode; }
419 			set {
420 				if (base.ImeMode == value)
421 					return;
422 
423 				base.ImeMode = value;
424 			}
425 		}
426 
427 		[DefaultValue (10)]
428 		[RefreshProperties(RefreshProperties.Repaint)]
429 		[MWFDescription("Scroll amount when clicking in the scroll area"), MWFCategory("Behaviour")]
430 		public int LargeChange {
431 			get { return Math.Min (large_change, maximum - minimum + 1); }
432 			set {
433 				if (value < 0)
434 					throw new ArgumentOutOfRangeException ("LargeChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
435 
436 				if (large_change != value) {
437 					large_change = value;
438 
439 					// thumb area depends on large change value,
440 					// so we need to recalculate it.
441 					CalcThumbArea ();
442 					UpdatePos (Value, true);
443 					InvalidateDirty ();
444 
445 					// UIA Framework: Generate UIA Event to indicate LargeChange change
446 					OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.LargeIncrement, value));
447 				}
448 			}
449 		}
450 
451 		[DefaultValue (100)]
452 		[RefreshProperties(RefreshProperties.Repaint)]
453 		[MWFDescription("Highest value for scrollbar"), MWFCategory("Behaviour")]
454 		public int Maximum {
455 			get { return maximum; }
456 			set {
457 				if (maximum == value)
458 					return;
459 
460 				maximum = value;
461 
462 				// UIA Framework: Generate UIA Event to indicate Maximum change
463 				OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.Last, value));
464 
465 				if (maximum < minimum)
466 					minimum = maximum;
467 				if (Value > maximum)
468 					Value = maximum;
469 
470 				// thumb area depends on maximum value,
471 				// so we need to recalculate it.
472 				CalcThumbArea ();
473 				UpdatePos (Value, true);
474 				InvalidateDirty ();
475 			}
476 		}
477 
SetValues(int maximum, int large_change)478 		internal void SetValues (int maximum, int large_change)
479 		{
480 			SetValues (-1, maximum, -1, large_change);
481 		}
482 
SetValues(int minimum, int maximum, int small_change, int large_change)483 		internal void SetValues (int minimum, int maximum, int small_change, int large_change)
484 		{
485 			bool update = false;
486 
487 			if (-1 != minimum && this.minimum != minimum) {
488 				this.minimum = minimum;
489 
490 				if (minimum > this.maximum)
491 					this.maximum = minimum;
492 				update = true;
493 
494 				// change the position if it is out of range now
495 				position = Math.Max (position, minimum);
496 			}
497 
498 			if (-1 != maximum && this.maximum != maximum) {
499 				this.maximum = maximum;
500 
501 				if (maximum < this.minimum)
502 					this.minimum = maximum;
503 				update = true;
504 
505 				// change the position if it is out of range now
506 				position = Math.Min (position, maximum);
507 			}
508 
509 			if (-1 != small_change && this.small_change != small_change) {
510 				this.small_change = small_change;
511 			}
512 
513 			if (this.large_change != large_change) {
514 				this.large_change = large_change;
515 				update = true;
516 			}
517 
518 			if (update) {
519 				CalcThumbArea ();
520 				UpdatePos (Value, true);
521 				InvalidateDirty ();
522 			}
523 		}
524 
525 		[DefaultValue (0)]
526 		[RefreshProperties(RefreshProperties.Repaint)]
527 		[MWFDescription("Smallest value for scrollbar"), MWFCategory("Behaviour")]
528 		public int Minimum {
529 			get { return minimum; }
530 			set {
531 				if (minimum == value)
532 					return;
533 
534 				minimum = value;
535 
536 				// UIA Framework: Generate UIA Event to indicate Minimum change
537 				OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.First, value));
538 
539 				if (minimum > maximum)
540 					maximum = minimum;
541 
542 				// thumb area depends on minimum value,
543 				// so we need to recalculate it.
544 				CalcThumbArea ();
545 				UpdatePos (Value, true);
546 				InvalidateDirty ();
547 			}
548 		}
549 
550 		[DefaultValue (1)]
551 		[MWFDescription("Scroll amount when clicking scroll arrows"), MWFCategory("Behaviour")]
552 		public int SmallChange {
553 			get { return small_change > LargeChange ? LargeChange : small_change; }
554 			set {
555 				if ( value < 0 )
556 					throw new ArgumentOutOfRangeException ("SmallChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
557 
558 				if (small_change != value) {
559 					small_change = value;
560 					UpdatePos (Value, true);
561 					InvalidateDirty ();
562 
563 					// UIA Framework: Generate UIA Event to indicate SmallChange change
564 					OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.SmallIncrement, value));
565 				}
566 			}
567 		}
568 
569 		[DefaultValue (false)]
570 		public new bool TabStop {
571 			get { return base.TabStop; }
572 			set { base.TabStop = value; }
573 		}
574 
575 		[EditorBrowsable (EditorBrowsableState.Never)]
576 		[Bindable (false)]
577 		[Browsable (false)]
578 		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
579 		public override string Text {
580 			 get { return base.Text;  }
581 			 set { base.Text = value; }
582 		}
583 
584 		[Bindable(true)]
585 		[DefaultValue (0)]
586 		[MWFDescription("Current value for scrollbar"), MWFCategory("Behaviour")]
587 		public int Value {
588 			get { return position; }
589 			set {
590 				if ( value < minimum || value > maximum )
591 					throw new ArgumentOutOfRangeException ("Value", string.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
592 
593 				if (position != value){
594 					position = value;
595 
596 					OnValueChanged (EventArgs.Empty);
597 
598 					if (IsHandleCreated) {
599 						Rectangle thumb_rect = thumb_pos;
600 
601 						UpdateThumbPos ((vert ? thumb_area.Y : thumb_area.X) + (int)(((float)(position - minimum)) * pixel_per_pos), false, false);
602 
603 						MoveThumb (thumb_rect, vert ? thumb_pos.Y : thumb_pos.X);
604 					}
605 				}
606 			}
607 		}
608 
609 		#endregion //Public Properties
610 
611 		#region Public Methods
GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified)612 		protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
613 		{
614 			// Basically, we want to keep our small edge and scale the long edge
615 			// ie: if we are vertical, don't scale our width
616 			if (vert)
617 				return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Height) | (specified & BoundsSpecified.Location));
618 			else
619 				return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Width) | (specified & BoundsSpecified.Location));
620 		}
621 
OnEnabledChanged(EventArgs e)622 		protected override void OnEnabledChanged (EventArgs e)
623 		{
624 			base.OnEnabledChanged (e);
625 
626 			if (Enabled)
627 				firstbutton_state = secondbutton_state = ButtonState.Normal;
628 			else
629 				firstbutton_state = secondbutton_state = ButtonState.Inactive;
630 
631 			Refresh ();
632 		}
633 
OnHandleCreated(System.EventArgs e)634 		protected override void OnHandleCreated (System.EventArgs e)
635 		{
636 			base.OnHandleCreated (e);
637 
638 			CalcButtonSizes ();
639 			CalcThumbArea ();
640 			UpdateThumbPos (thumb_area.Y + (int)(((float)(position - minimum)) * pixel_per_pos), true, false);
641 		}
642 
OnScroll(ScrollEventArgs se)643 		protected virtual void OnScroll (ScrollEventArgs se)
644 		{
645 			ScrollEventHandler eh = (ScrollEventHandler)(Events [ScrollEvent]);
646 			if (eh == null)
647 				return;
648 
649 			if (se.NewValue < Minimum) {
650 				se.NewValue = Minimum;
651 			}
652 
653 			if (se.NewValue > Maximum) {
654 				se.NewValue = Maximum;
655 			}
656 
657 			eh (this, se);
658 		}
659 
SendWMScroll(ScrollBarCommands cmd)660 		private void SendWMScroll(ScrollBarCommands cmd) {
661 			if ((Parent != null) && Parent.IsHandleCreated) {
662 				if (vert) {
663 					XplatUI.SendMessage(Parent.Handle, Msg.WM_VSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
664 				} else {
665 					XplatUI.SendMessage(Parent.Handle, Msg.WM_HSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
666 				}
667 			}
668 		}
669 
OnValueChanged(EventArgs e)670 		protected virtual void OnValueChanged (EventArgs e)
671 		{
672 			EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
673 			if (eh != null)
674 				eh (this, e);
675 		}
676 
ToString()677 		public override string ToString()
678 		{
679 			return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
680 						GetType( ).FullName, minimum, maximum, position);
681 		}
682 
UpdateScrollInfo()683 		protected void UpdateScrollInfo ()
684 		{
685 			Refresh ();
686 		}
687 
WndProc(ref Message m)688 		protected override void WndProc (ref Message m)
689 		{
690 			base.WndProc (ref m);
691 		}
692 
693 		#endregion //Public Methods
694 
695 		#region Private Methods
696 
CalcButtonSizes()697 		private void CalcButtonSizes ()
698     		{
699     			if (vert) {
700 				if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
701 					scrollbutton_height = Height /2;
702 				else
703 					scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
704 
705 			} else {
706 				if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
707 					scrollbutton_width = Width /2;
708 				else
709 					scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
710 			}
711 		}
712 
CalcThumbArea()713 		private void CalcThumbArea ()
714 		{
715 			int lchange = use_manual_thumb_size ? manual_thumb_size : LargeChange;
716 
717 			// Thumb area
718 			if (vert) {
719 
720 				thumb_area.Height = Height - scrollbutton_height -  scrollbutton_height;
721 				thumb_area.X = 0;
722 				thumb_area.Y = scrollbutton_height;
723 				thumb_area.Width = Width;
724 
725 				if (Height < thumb_notshown_size)
726 					thumb_size = 0;
727 				else {
728 					double per =  ((double) lchange / (double)((1 + maximum - minimum)));
729 					thumb_size = 1 + (int) (thumb_area.Height * per);
730 
731 					if (thumb_size < thumb_min_size)
732 						thumb_size = thumb_min_size;
733 
734 					// Give the user something to drag if LargeChange is zero
735 					if (LargeChange == 0)
736 						thumb_size = 17;
737 				}
738 
739 				pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
740 
741 			} else	{
742 
743 				thumb_area.Y = 0;
744 				thumb_area.X = scrollbutton_width;
745 				thumb_area.Height = Height;
746 				thumb_area.Width = Width - scrollbutton_width -  scrollbutton_width;
747 
748 				if (Width < thumb_notshown_size)
749 					thumb_size = 0;
750 				else {
751 					double per =  ((double) lchange / (double)((1 + maximum - minimum)));
752 					thumb_size = 1 + (int) (thumb_area.Width * per);
753 
754 					if (thumb_size < thumb_min_size)
755 						thumb_size = thumb_min_size;
756 
757 					// Give the user something to drag if LargeChange is zero
758 					if (LargeChange == 0)
759 						thumb_size = 17;
760 				}
761 
762 				pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
763 			}
764 		}
765 
LargeIncrement()766 		private void LargeIncrement ()
767     		{
768 			ScrollEventArgs event_args;
769     			int pos = Math.Min (MaximumAllowed, position + large_change);
770 
771     			event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
772     			OnScroll (event_args);
773 			Value = event_args.NewValue;
774 
775 			event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
776 			OnScroll (event_args);
777     			Value = event_args.NewValue;
778 
779 			// UIA Framework event invoked when the "LargeIncrement
780 			// Button" is "clicked" either by using the Invoke Pattern
781 			// or the space between the thumb and the bottom/right button
782 			OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeIncrement, Value));
783     		}
784 
LargeDecrement()785     		private void LargeDecrement ()
786     		{
787 			ScrollEventArgs event_args;
788     			int pos = Math.Max (Minimum, position - large_change);
789 
790     			event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
791     			OnScroll (event_args);
792     			Value = event_args.NewValue;
793 
794 			event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
795 			OnScroll (event_args);
796     			Value = event_args.NewValue;
797 
798 			// UIA Framework event invoked when the "LargeDecrement
799 			// Button" is "clicked" either by using the Invoke Pattern
800 			// or the space between the thumb and the top/left button
801 			OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeDecrement, Value));
802     		}
803 
OnResizeSB(Object o, EventArgs e)804     		private void OnResizeSB (Object o, EventArgs e)
805     		{
806     			if (Width <= 0 || Height <= 0)
807     				return;
808 
809 			CalcButtonSizes ();
810 			CalcThumbArea ();
811 			UpdatePos (position, true);
812 
813 			Refresh ();
814     		}
815 
OnPaintInternal(PaintEventArgs pevent)816 		internal override void OnPaintInternal (PaintEventArgs pevent)
817 		{
818 			ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
819 		}
820 
OnTimer(Object source, EventArgs e)821 		private void OnTimer (Object source, EventArgs e)
822 		{
823 			ClearDirty ();
824 
825 			switch (timer_type) {
826 
827 			case TimerType.HoldButton:
828 				SetRepeatButtonTimer ();
829 				break;
830 
831 			case TimerType.RepeatButton:
832 			{
833 				if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Minimum) {
834 					SmallDecrement();
835 					SendWMScroll(ScrollBarCommands.SB_LINEUP);
836 				}
837 
838 				if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Maximum) {
839 					SmallIncrement();
840 					SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
841 				}
842 
843 				break;
844 			}
845 
846 			case TimerType.HoldThumbArea:
847 				SetRepeatThumbAreaTimer ();
848 				break;
849 
850 			case TimerType.RepeatThumbArea:
851 			{
852 				Point pnt, pnt_screen;
853 				Rectangle thumb_area_screen = thumb_area;
854 
855 				pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
856 				thumb_area_screen.X = pnt_screen.X;
857 				thumb_area_screen.Y = pnt_screen.Y;
858 
859 				if (thumb_area_screen.Contains (MousePosition) == false) {
860 					timer.Enabled = false;
861 					thumb_moving = ThumbMoving.None;
862 					DirtyThumbArea ();
863 					InvalidateDirty ();
864 				}
865 
866 				pnt = PointToClient (MousePosition);
867 
868 				if (vert)
869 					lastclick_pos = pnt.Y;
870 				else
871 					lastclick_pos = pnt.X;
872 
873 				if (thumb_moving == ThumbMoving.Forward) {
874 					if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
875 					   (!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
876 					   (thumb_area.Contains (pnt) == false)) {
877 						timer.Enabled = false;
878     						thumb_moving = ThumbMoving.None;
879     						Refresh ();
880 						return;
881 					} else {
882 						LargeIncrement ();
883 						SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
884 					}
885 				} else {
886 					if ((vert && (thumb_pos.Y < lastclick_pos)) ||
887 					   (!vert && (thumb_pos.X  < lastclick_pos))){
888 						timer.Enabled = false;
889 						thumb_moving = ThumbMoving.None;
890 						SendWMScroll(ScrollBarCommands.SB_PAGEUP);
891 						Refresh ();
892 					} else {
893 						LargeDecrement ();
894 						SendWMScroll(ScrollBarCommands.SB_PAGEUP);
895 					}
896 				}
897 
898 				break;
899 			}
900 			default:
901 				break;
902 			}
903 
904 			InvalidateDirty ();
905 		}
906 
MoveThumb(Rectangle original_thumbpos, int value)907 		private void MoveThumb (Rectangle original_thumbpos, int value)
908 		{
909 			/* so, the reason this works can best be
910 			 * described by the following 1 dimensional
911 			 * pictures
912 			 *
913 			 * say you have a scrollbar thumb positioned
914 			 * thusly:
915 			 *
916 			 * <---------------------|          |------------------------------>
917 			 *
918 			 * and you want it to end up looking like this:
919 			 *
920 			 * <-----------------------------|          |---------------------->
921 			 *
922 			 * that can be done with the scrolling api by
923 			 * extending the rectangle to encompass both
924 			 * positions:
925 			 *
926 			 *               start of range          end of range
927 			 *                       \	    	    /
928 			 * <---------------------|	    |-------|---------------------->
929 			 *
930 			 * so, we end up scrolling just this little region:
931 			 *
932 			 *                       |          |-------|
933 			 *
934 			 * and end up with       ********|          |
935 			 *
936 			 * where ****** is space that is automatically
937 			 * redrawn.
938 			 *
939 			 * It's clear that in both cases (left to
940 			 * right, right to left) we need to extend the
941 			 * size of the scroll rectangle to encompass
942 			 * both.  In the right to left case, we also
943 			 * need to decrement the X coordinate.
944 			 *
945 			 * We call Update after scrolling to make sure
946 			 * there's no garbage left in the window to be
947 			 * copied again if we're called before the
948 			 * paint events have been handled.
949 			 *
950 			 */
951 			int delta;
952 
953 			if (vert) {
954 				delta = value - original_thumbpos.Y;
955 
956 				if (delta < 0) {
957 					original_thumbpos.Y += delta;
958 					original_thumbpos.Height -= delta;
959 				}
960 				else {
961 					original_thumbpos.Height += delta;
962 				}
963 
964 				XplatUI.ScrollWindow (Handle, original_thumbpos, 0, delta, false);
965 			}
966 			else {
967 				delta = value - original_thumbpos.X;
968 
969 				if (delta < 0) {
970 					original_thumbpos.X += delta;
971 					original_thumbpos.Width -= delta;
972 				}
973 				else {
974 					original_thumbpos.Width += delta;
975 				}
976 
977 				XplatUI.ScrollWindow (Handle, original_thumbpos, delta, 0, false);
978 			}
979 
980 			Update ();
981 		}
982 
OnMouseMoveSB(object sender, MouseEventArgs e)983     		private void OnMouseMoveSB (object sender, MouseEventArgs e)
984     		{
985 			if (Enabled == false)
986 				return;
987 
988 			FirstButtonEntered = first_arrow_area.Contains (e.Location);
989 			SecondButtonEntered = second_arrow_area.Contains (e.Location);
990 
991 			if (thumb_size == 0)
992 				return;
993 
994 			ThumbEntered = thumb_pos.Contains (e.Location);
995 
996 			if (firstbutton_pressed) {
997     				if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
998 					firstbutton_state = ButtonState.Normal;
999 					Invalidate (first_arrow_area);
1000 					Update();
1001 					return;
1002 				} else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
1003 					firstbutton_state = ButtonState.Pushed;
1004 					Invalidate (first_arrow_area);
1005 					Update();
1006 					return;
1007 				}
1008 			} else if (secondbutton_pressed) {
1009 				if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
1010 					secondbutton_state = ButtonState.Normal;
1011 					Invalidate (second_arrow_area);
1012 					Update();
1013 					return;
1014 				} else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
1015 					secondbutton_state = ButtonState.Pushed;
1016 					Invalidate (second_arrow_area);
1017 					Update();
1018 					return;
1019 				}
1020 			} else if (thumb_pressed == true) {
1021 				if (vert) {
1022 					int thumb_edge = e.Y - thumbclick_offset;
1023 
1024 					if (thumb_edge < thumb_area.Y)
1025 						thumb_edge = thumb_area.Y;
1026 					else if (thumb_edge > thumb_area.Bottom - thumb_size)
1027 						thumb_edge = thumb_area.Bottom - thumb_size;
1028 
1029 					if (thumb_edge != thumb_pos.Y) {
1030 						Rectangle thumb_rect = thumb_pos;
1031 
1032 						UpdateThumbPos (thumb_edge, false, true);
1033 
1034 						MoveThumb (thumb_rect, thumb_pos.Y);
1035 
1036 						OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
1037 					}
1038 					SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1039 				} else {
1040 					int thumb_edge = e.X - thumbclick_offset;
1041 
1042 					if (thumb_edge < thumb_area.X)
1043 						thumb_edge = thumb_area.X;
1044 					else if (thumb_edge > thumb_area.Right - thumb_size)
1045 						thumb_edge = thumb_area.Right - thumb_size;
1046 
1047 					if (thumb_edge != thumb_pos.X) {
1048 						Rectangle thumb_rect = thumb_pos;
1049 
1050 						UpdateThumbPos (thumb_edge, false, true);
1051 
1052 						MoveThumb (thumb_rect, thumb_pos.X);
1053 
1054 						OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
1055 					}
1056 					SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1057 				}
1058 
1059 			}
1060 
1061     		}
1062 
OnMouseDownSB(object sender, MouseEventArgs e)1063     		private void OnMouseDownSB (object sender, MouseEventArgs e)
1064     		{
1065 			ClearDirty ();
1066 
1067 			if (Enabled == false || (e.Button & MouseButtons.Left) == 0)
1068 				return;
1069 
1070     			if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
1071 				SendWMScroll(ScrollBarCommands.SB_LINEUP);
1072 				firstbutton_state = ButtonState.Pushed;
1073 				firstbutton_pressed = true;
1074 				Invalidate (first_arrow_area);
1075 				Update();
1076 				if (!timer.Enabled) {
1077 					SetHoldButtonClickTimer ();
1078 					timer.Enabled = true;
1079 				}
1080 			}
1081 
1082 			if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
1083 				SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1084 				secondbutton_state = ButtonState.Pushed;
1085 				secondbutton_pressed = true;
1086 				Invalidate (second_arrow_area);
1087 				Update();
1088 				if (!timer.Enabled) {
1089 					SetHoldButtonClickTimer ();
1090 					timer.Enabled = true;
1091 				}
1092 			}
1093 
1094 			if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
1095 				ThumbPressed = true;
1096 				SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
1097 				if (vert) {
1098 					thumbclick_offset = e.Y - thumb_pos.Y;
1099 					lastclick_pos = e.Y;
1100 				}
1101 				else {
1102 					thumbclick_offset = e.X - thumb_pos.X;
1103 					lastclick_pos = e.X;
1104 				}
1105 			} else {
1106 				if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
1107 
1108 					if (vert) {
1109 						lastclick_pos = e.Y;
1110 
1111 						if (e.Y > thumb_pos.Y + thumb_pos.Height) {
1112 							SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1113 							LargeIncrement ();
1114 							thumb_moving = ThumbMoving.Forward;
1115 							Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1116 										      ClientRectangle.Width,
1117 										      ClientRectangle.Height -	(thumb_pos.Y + thumb_pos.Height) -
1118 										      scrollbutton_height));
1119 						} else {
1120 							SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1121 							LargeDecrement ();
1122 							thumb_moving = ThumbMoving.Backwards;
1123 							Dirty (new Rectangle (0,  scrollbutton_height,
1124 										      ClientRectangle.Width,
1125 										      thumb_pos.Y - scrollbutton_height));
1126 						}
1127 					} else {
1128 
1129 						lastclick_pos = e.X;
1130 
1131 						if (e.X > thumb_pos.X + thumb_pos.Width) {
1132 							SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
1133 							thumb_moving = ThumbMoving.Forward;
1134 							LargeIncrement ();
1135 							Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1136 										      ClientRectangle.Width -  (thumb_pos.X + thumb_pos.Width) -
1137 										      scrollbutton_width,
1138 										      ClientRectangle.Height));
1139 						} else {
1140 							SendWMScroll(ScrollBarCommands.SB_PAGEUP);
1141 							thumb_moving = ThumbMoving.Backwards;
1142 							LargeDecrement ();
1143 							Dirty (new Rectangle (scrollbutton_width,  0,
1144 										      thumb_pos.X - scrollbutton_width,
1145 										      ClientRectangle.Height));
1146 						}
1147 					}
1148 
1149 					SetHoldThumbAreaTimer ();
1150 					timer.Enabled = true;
1151 					InvalidateDirty ();
1152 				}
1153 			}
1154     		}
1155 
OnMouseUpSB(object sender, MouseEventArgs e)1156     		private void OnMouseUpSB (object sender, MouseEventArgs e)
1157     		{
1158 			ClearDirty ();
1159 
1160 			if (Enabled == false)
1161 				return;
1162 
1163     			timer.Enabled = false;
1164     			if (thumb_moving != ThumbMoving.None) {
1165 				DirtyThumbArea ();
1166     				thumb_moving = ThumbMoving.None;
1167     			}
1168 
1169 			if (firstbutton_pressed) {
1170 				firstbutton_state = ButtonState.Normal;
1171 				if (first_arrow_area.Contains (e.X, e.Y)) {
1172 					SmallDecrement ();
1173 				}
1174 				SendWMScroll(ScrollBarCommands.SB_LINEUP);
1175 				firstbutton_pressed = false;
1176 				Dirty (first_arrow_area);
1177 			} else if (secondbutton_pressed) {
1178 				secondbutton_state = ButtonState.Normal;
1179 				if (second_arrow_area.Contains (e.X, e.Y)) {
1180 					SmallIncrement ();
1181 				}
1182 				SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
1183 				Dirty (second_arrow_area);
1184 				secondbutton_pressed = false;
1185 			} else if (thumb_pressed == true) {
1186 				OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
1187 				OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
1188 				SendWMScroll(ScrollBarCommands.SB_THUMBPOSITION);
1189 				ThumbPressed = false;
1190 				return;
1191 			}
1192 
1193 			InvalidateDirty ();
1194     		}
1195 
OnKeyDownSB(Object o, KeyEventArgs key)1196     		private void OnKeyDownSB (Object o, KeyEventArgs key)
1197 		{
1198 			if (Enabled == false)
1199 				return;
1200 
1201 			ClearDirty ();
1202 
1203 			switch (key.KeyCode){
1204 			case Keys.Up:
1205 			{
1206 				SmallDecrement ();
1207 				break;
1208 			}
1209 			case Keys.Down:
1210 			{
1211 				SmallIncrement ();
1212 				break;
1213 			}
1214 			case Keys.PageUp:
1215 			{
1216 				LargeDecrement ();
1217 				break;
1218 			}
1219 			case Keys.PageDown:
1220 			{
1221 				LargeIncrement ();
1222 				break;
1223 			}
1224 			case Keys.Home:
1225 			{
1226 				SetHomePosition ();
1227 				break;
1228 			}
1229 			case Keys.End:
1230 			{
1231 				SetEndPosition ();
1232 				break;
1233 			}
1234 			default:
1235 				break;
1236 			}
1237 
1238 			InvalidateDirty ();
1239 		}
1240 
1241 		// I hate to do this, but we don't have the resources to track
1242 		// down everything internal that is setting a value outside the
1243 		// correct range, so we'll clamp it to the acceptable values.
SafeValueSet(int value)1244 		internal void SafeValueSet (int value)
1245 		{
1246 			value = Math.Min (value, maximum);
1247 			value = Math.Max (value, minimum);
1248 
1249 			Value = value;
1250 		}
1251 
SetEndPosition()1252 		private void SetEndPosition ()
1253 		{
1254 			ScrollEventArgs event_args;
1255     			int pos = MaximumAllowed;
1256 
1257     			event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
1258     			OnScroll (event_args);
1259     			pos = event_args.NewValue;
1260 
1261 			event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1262 			OnScroll (event_args);
1263     			pos = event_args.NewValue;
1264 
1265 			SetValue (pos);
1266 		}
1267 
SetHomePosition()1268 		private void SetHomePosition ()
1269 		{
1270 			ScrollEventArgs event_args;
1271     			int pos = Minimum;
1272 
1273     			event_args = new ScrollEventArgs (ScrollEventType.First, pos);
1274     			OnScroll (event_args);
1275     			pos = event_args.NewValue;
1276 
1277 			event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1278 			OnScroll (event_args);
1279 			pos = event_args.NewValue;
1280 
1281 			SetValue (pos);
1282 		}
1283 
SmallIncrement()1284     		private void SmallIncrement ()
1285     		{
1286     			ScrollEventArgs event_args;
1287     			int pos = Math.Min (MaximumAllowed, position + SmallChange);
1288 
1289     			event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
1290     			OnScroll (event_args);
1291     			Value = event_args.NewValue;
1292 
1293 			event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1294 			OnScroll (event_args);
1295 			Value = event_args.NewValue;
1296 
1297 			// UIA Framework event invoked when the "SmallIncrement
1298 			// Button" (a.k.a bottom/right button) is "clicked" either
1299 			// by using the Invoke Pattern or the button itself
1300 			OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallIncrement, Value));
1301     		}
1302 
SmallDecrement()1303     		private void SmallDecrement ()
1304     		{
1305 			ScrollEventArgs event_args;
1306     			int pos = Math.Max (Minimum, position - SmallChange);
1307 
1308     			event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
1309     			OnScroll (event_args);
1310     			Value = event_args.NewValue;
1311 
1312 			event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
1313 			OnScroll (event_args);
1314 			Value = event_args.NewValue;
1315 
1316 			// UIA Framework event invoked when the "SmallDecrement
1317 			// Button" (a.k.a top/left button) is "clicked" either
1318 			// by using the Invoke Pattern or the button itself
1319 			OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallDecrement, Value));
1320     		}
1321 
SetHoldButtonClickTimer()1322     		private void SetHoldButtonClickTimer ()
1323 		{
1324 			timer.Enabled = false;
1325 			timer.Interval = 200;
1326 			timer_type = TimerType.HoldButton;
1327 			timer.Enabled = true;
1328 		}
1329 
SetRepeatButtonTimer()1330 		private void SetRepeatButtonTimer ()
1331 		{
1332 			timer.Enabled = false;
1333 			timer.Interval = 50;
1334 			timer_type = TimerType.RepeatButton;
1335 			timer.Enabled = true;
1336 		}
1337 
SetHoldThumbAreaTimer()1338 		private void SetHoldThumbAreaTimer ()
1339 		{
1340 			timer.Enabled = false;
1341 			timer.Interval = 200;
1342 			timer_type = TimerType.HoldThumbArea;
1343 			timer.Enabled = true;
1344 		}
1345 
SetRepeatThumbAreaTimer()1346 		private void SetRepeatThumbAreaTimer ()
1347 		{
1348 			timer.Enabled = false;
1349 			timer.Interval = 50;
1350 			timer_type = TimerType.RepeatThumbArea;
1351 			timer.Enabled = true;
1352 		}
1353 
UpdatePos(int newPos, bool update_thumbpos)1354     		private void UpdatePos (int newPos, bool update_thumbpos)
1355     		{
1356 			int pos;
1357 
1358     			if (newPos < minimum)
1359     				pos = minimum;
1360     			else
1361     				if (newPos > MaximumAllowed)
1362     					pos = MaximumAllowed;
1363 				else
1364 					pos = newPos;
1365 
1366 			// pos can't be less than minimum or greater than maximum
1367 			if (pos < minimum)
1368 				pos = minimum;
1369 			if (pos > maximum)
1370 				pos = maximum;
1371 
1372 			if (update_thumbpos) {
1373 				if (vert)
1374 					UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1375 				else
1376 					UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
1377 				SetValue (pos);
1378 			}
1379 			else {
1380 				position = pos; // Updates directly the value to avoid thumb pos update
1381 
1382 
1383 				// XXX some reason we don't call OnValueChanged?
1384 				EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
1385 				if (eh != null)
1386 					eh (this, EventArgs.Empty);
1387 			}
1388     		}
1389 
UpdateThumbPos(int pixel, bool dirty, bool update_value)1390     		private void UpdateThumbPos (int pixel, bool dirty, bool update_value)
1391     		{
1392     			float new_pos = 0;
1393 
1394     			if (vert) {
1395 				if (dirty)
1396 					Dirty (thumb_pos);
1397 	    			if (pixel < thumb_area.Y)
1398 	    				thumb_pos.Y = thumb_area.Y;
1399 	    			else if (pixel > thumb_area.Bottom - thumb_size)
1400 	    				thumb_pos.Y = thumb_area.Bottom - thumb_size;
1401 	    			else
1402 	    				thumb_pos.Y = pixel;
1403 
1404 				thumb_pos.X = 0;
1405 				thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
1406 				thumb_pos.Height = thumb_size;
1407 				new_pos = (float) (thumb_pos.Y - thumb_area.Y);
1408 				new_pos = new_pos / pixel_per_pos;
1409 				if (dirty)
1410 					Dirty (thumb_pos);
1411 			} else	{
1412 				if (dirty)
1413 					Dirty (thumb_pos);
1414 				if (pixel < thumb_area.X)
1415 	    				thumb_pos.X = thumb_area.X;
1416 	    			else if (pixel > thumb_area.Right - thumb_size)
1417 	    				thumb_pos.X = thumb_area.Right - thumb_size;
1418 	    			else
1419 	    				thumb_pos.X = pixel;
1420 
1421 				thumb_pos.Y = 0;
1422 				thumb_pos.Width =  thumb_size;
1423 				thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
1424 				new_pos = (float) (thumb_pos.X - thumb_area.X);
1425 				new_pos = new_pos / pixel_per_pos;
1426 
1427 				if (dirty)
1428 					Dirty (thumb_pos);
1429 			}
1430 
1431 			if (update_value)
1432 				UpdatePos ((int) new_pos + minimum, false);
1433     		}
1434 
SetValue(int value)1435 		private void SetValue (int value)
1436 		{
1437 			if ( value < minimum || value > maximum )
1438 				throw new ArgumentException(
1439 					String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
1440 
1441 			if (position != value){
1442 				position = value;
1443 
1444 				OnValueChanged (EventArgs.Empty);
1445 				UpdatePos (value, true);
1446 			}
1447 		}
1448 
ClearDirty()1449 		private void ClearDirty ()
1450 		{
1451 			dirty = Rectangle.Empty;
1452 		}
1453 
Dirty(Rectangle r)1454 		private void Dirty (Rectangle r)
1455 		{
1456 			if (dirty == Rectangle.Empty) {
1457 				dirty = r;
1458 				return;
1459 			}
1460 			dirty = Rectangle.Union (dirty, r);
1461 		}
1462 
DirtyThumbArea()1463 		private void DirtyThumbArea ()
1464 		{
1465 			if (thumb_moving == ThumbMoving.Forward) {
1466 				if (vert) {
1467 					Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1468 								      ClientRectangle.Width,
1469 								      ClientRectangle.Height -	(thumb_pos.Y + thumb_pos.Height) -
1470 								      scrollbutton_height));
1471 				} else {
1472 					Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1473 								      ClientRectangle.Width -  (thumb_pos.X + thumb_pos.Width) -
1474 								      scrollbutton_width,
1475 								      ClientRectangle.Height));
1476 				}
1477 			} else if (thumb_moving == ThumbMoving.Backwards) {
1478 				if (vert) {
1479 					Dirty(new Rectangle (0,	 scrollbutton_height,
1480 								      ClientRectangle.Width,
1481 								      thumb_pos.Y - scrollbutton_height));
1482 				} else {
1483 					Dirty (new Rectangle (scrollbutton_width,  0,
1484 								      thumb_pos.X - scrollbutton_width,
1485 								      ClientRectangle.Height));
1486 				}
1487 			}
1488 		}
1489 
InvalidateDirty()1490 		private void InvalidateDirty ()
1491 		{
1492 			Invalidate (dirty);
1493 			Update();
1494 			dirty = Rectangle.Empty;
1495 		}
1496 
OnMouseEnter(object sender, EventArgs e)1497 		void OnMouseEnter (object sender, EventArgs e)
1498 		{
1499 			if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
1500 				Region region_to_invalidate = new Region (first_arrow_area);
1501 				region_to_invalidate.Union (second_arrow_area);
1502 				Invalidate (region_to_invalidate);
1503 			}
1504 		}
1505 
OnMouseLeave(object sender, EventArgs e)1506 		void OnMouseLeave (object sender, EventArgs e)
1507 		{
1508 			Region region_to_invalidate = new Region ();
1509 			region_to_invalidate.MakeEmpty ();
1510 			bool dirty = false;
1511 			if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
1512 				region_to_invalidate.Union (first_arrow_area);
1513 				region_to_invalidate.Union (second_arrow_area);
1514 				dirty = true;
1515 			} else
1516 				if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
1517 					if (first_button_entered) {
1518 						region_to_invalidate.Union (first_arrow_area);
1519 						dirty = true;
1520 					} else if (second_button_entered) {
1521 						region_to_invalidate.Union (second_arrow_area);
1522 						dirty = true;
1523 					}
1524 			if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
1525 				if (thumb_entered) {
1526 					region_to_invalidate.Union (thumb_pos);
1527 					dirty = true;
1528 				}
1529 			first_button_entered = false;
1530 			second_button_entered = false;
1531 			thumb_entered = false;
1532 			if (dirty)
1533 				Invalidate (region_to_invalidate);
1534 			region_to_invalidate.Dispose ();
1535 		}
1536 		#endregion //Private Methods
OnMouseWheel(MouseEventArgs e)1537 		protected override void OnMouseWheel (MouseEventArgs e)
1538 		{
1539 			base.OnMouseWheel (e);
1540 		}
1541 
1542 		#region UIA Framework Section: Events, Methods and Properties.
1543 
1544 		//NOTE:
1545 		//	We are using Reflection to add/remove internal events.
1546 		//	Class ScrollBarButtonInvokePatternInvokeEvent uses the events.
1547 		//
1548     		// Types used to generate UIA InvokedEvent
1549 		// * args.Type = ScrollEventType.LargeIncrement. Space between Thumb and bottom/right Button
1550 		// * args.Type = ScrollEventType.LargeDecrement. Space between Thumb and top/left Button
1551 		// * args.Type = ScrollEventType.SmallIncrement. Small increment UIA Button (bottom/right Button)
1552     		// * args.Type = ScrollEventType.SmallDecrement. Small decrement UIA Button (top/left Button)
1553 		// Types used to generate RangeValue-related events
1554 		// * args.Type = ScrollEventType.LargeIncrement. LargeChange event
1555 		// * args.Type = ScrollEventType.Last. Maximum event
1556 		// * args.Type = ScrollEventType.First. Minimum event
1557 		// * args.Type = ScrollEventType.SmallIncrement. SmallChange event
1558 		static object UIAScrollEvent = new object ();
1559 		static object UIAValueChangeEvent = new object ();
1560 
1561 		internal event ScrollEventHandler UIAScroll {
1562 			add { Events.AddHandler (UIAScrollEvent, value); }
1563 			remove { Events.RemoveHandler (UIAScrollEvent, value); }
1564 		}
1565 
1566 		internal event ScrollEventHandler UIAValueChanged {
1567 			add { Events.AddHandler (UIAValueChangeEvent, value); }
1568 			remove { Events.RemoveHandler (UIAValueChangeEvent, value); }
1569 		}
1570 
OnUIAScroll(ScrollEventArgs args)1571 		internal void OnUIAScroll (ScrollEventArgs args)
1572 		{
1573 			ScrollEventHandler eh = (ScrollEventHandler) Events [UIAScrollEvent];
1574 			if (eh != null)
1575 				eh (this, args);
1576 		}
1577 
OnUIAValueChanged(ScrollEventArgs args)1578 		internal void OnUIAValueChanged (ScrollEventArgs args)
1579 		{
1580 			ScrollEventHandler eh = (ScrollEventHandler) Events [UIAValueChangeEvent];
1581 			if (eh != null)
1582 				eh (this, args);
1583 		}
1584 
1585 		//NOTE:
1586 		//	Wrapper methods used by the Reflection.
1587 		//	Class ScrollBarButtonInvokeProviderBehavior uses the events.
1588 		//
UIALargeIncrement()1589 		internal void UIALargeIncrement ()
1590 		{
1591 			LargeIncrement ();
1592 		}
1593 
UIALargeDecrement()1594 		internal void UIALargeDecrement ()
1595 		{
1596 			LargeDecrement ();
1597 		}
1598 
UIASmallIncrement()1599 		internal void UIASmallIncrement ()
1600 		{
1601 			SmallIncrement ();
1602 		}
1603 
UIASmallDecrement()1604 		internal void UIASmallDecrement ()
1605 		{
1606 			SmallDecrement ();
1607 		}
1608 
1609 		internal Rectangle UIAThumbArea {
1610 			get { return thumb_area; }
1611 		}
1612 
1613 		internal Rectangle UIAThumbPosition {
1614 			get { return thumb_pos; }
1615 		}
1616 
1617 		#endregion UIA Framework Section: Events, Methods and Properties.
1618 
1619 	 }
1620 }
1621 
1622 
1623 
1624