Merge pull request #2311 from mlancione/master
[mono.git] / mcs / class / System.Windows.Forms / System.Windows.Forms / ScrollBar.cs
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
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
478                 internal void SetValues (int maximum, int large_change)
479                 {
480                         SetValues (-1, maximum, -1, large_change);
481                 }
482
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
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                 
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
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
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
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
670                 protected virtual void OnValueChanged (EventArgs e)
671                 {
672                         EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
673                         if (eh != null)
674                                 eh (this, e);
675                 }
676
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
683                 protected void UpdateScrollInfo ()
684                 {
685                         Refresh ();
686                 }
687
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
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
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
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
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
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
816                 internal override void OnPaintInternal (PaintEventArgs pevent)
817                 {
818                         ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
819                 }
820
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
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
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
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
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
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.
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                 
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
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
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
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
1322                 private void SetHoldButtonClickTimer ()
1323                 {
1324                         timer.Enabled = false;
1325                         timer.Interval = 200;
1326                         timer_type = TimerType.HoldButton;
1327                         timer.Enabled = true;
1328                 }
1329
1330                 private void SetRepeatButtonTimer ()
1331                 {
1332                         timer.Enabled = false;
1333                         timer.Interval = 50;
1334                         timer_type = TimerType.RepeatButton;
1335                         timer.Enabled = true;
1336                 }
1337
1338                 private void SetHoldThumbAreaTimer ()
1339                 {
1340                         timer.Enabled = false;
1341                         timer.Interval = 200;
1342                         timer_type = TimerType.HoldThumbArea;
1343                         timer.Enabled = true;
1344                 }
1345
1346                 private void SetRepeatThumbAreaTimer ()
1347                 {
1348                         timer.Enabled = false;
1349                         timer.Interval = 50;
1350                         timer_type = TimerType.RepeatThumbArea;
1351                         timer.Enabled = true;
1352                 }
1353
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
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
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
1449                 private void ClearDirty ()
1450                 {
1451                         dirty = Rectangle.Empty;
1452                 }
1453
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
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
1490                 private void InvalidateDirty ()
1491                 {
1492                         Invalidate (dirty);
1493                         Update();
1494                         dirty = Rectangle.Empty;
1495                 }
1496
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
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
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
1571                 internal void OnUIAScroll (ScrollEventArgs args)
1572                 {
1573                         ScrollEventHandler eh = (ScrollEventHandler) Events [UIAScrollEvent];
1574                         if (eh != null)
1575                                 eh (this, args);
1576                 }
1577
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                 //
1589                 internal void UIALargeIncrement ()
1590                 {
1591                         LargeIncrement ();
1592                 }
1593
1594                 internal void UIALargeDecrement ()
1595                 {
1596                         LargeDecrement ();
1597                 }
1598
1599                 internal void UIASmallIncrement ()
1600                 {
1601                         SmallIncrement ();
1602                 }
1603
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