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