In tests/attributes:
[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         [DefaultEvent ("Scroll")]
41         [DefaultProperty ("Value")]
42         public abstract class ScrollBar : Control
43         {
44                 #region Local Variables
45                 private int position;
46                 private int minimum;
47                 private int maximum;
48                 private int large_change;
49                 private int small_change;
50                 internal int scrollbutton_height;
51                 internal int scrollbutton_width;                
52                 private ScrollBars type;
53                 private Rectangle first_arrow_area = new Rectangle ();          // up or left
54                 private Rectangle second_arrow_area = new Rectangle ();         // down or right
55                 private Rectangle thumb_pos = new Rectangle ();
56                 private Rectangle thumb_area = new Rectangle ();
57                 internal ButtonState firstbutton_state = ButtonState.Normal;
58                 internal ButtonState secondbutton_state = ButtonState.Normal;
59                 private bool firstbutton_pressed = false;
60                 private bool secondbutton_pressed = false;
61                 private bool thumb_pressed = false;
62                 private float pixel_per_pos = 0;
63                 private Timer timer = new Timer ();
64                 private TimerType timer_type;
65                 private int thumb_pixel_click_move;
66                 private int thumb_pixel_click_move_prev;
67                 private int thumb_size = 40;
68                 private const int thumb_min_size = 8;
69                 private const int thumb_notshown_size = 40;
70                 internal bool vert;
71                 private int lastclick_pos;      // Position of the last button-down event
72                 private int lastclick_pos_thumb;      // Position of the last button-down event relative to the thumb           
73                 private bool outside_thumbarea_right = false;
74                 private bool outside_thumbarea_left = false;
75
76                 private Rectangle dirty;
77
78                 internal ThumbMoving thumb_moving = ThumbMoving.None;
79                 #endregion      // Local Variables
80
81                 private enum TimerType
82                 {
83                         HoldButton,
84                         RepeatButton,
85                         HoldThumbArea,
86                         RepeatThumbArea
87                 }
88                 
89                 internal enum ThumbMoving
90                 {
91                         None,
92                         Forward,
93                         Backwards,
94                 }
95
96                 #region events
97                 
98                 [Browsable (false)]
99                 [EditorBrowsable (EditorBrowsableState.Never)]
100                 public new event EventHandler BackColorChanged;
101                 
102                 [Browsable (false)]
103                 [EditorBrowsable (EditorBrowsableState.Never)]
104                 public new event EventHandler BackgroundImageChanged;
105                 
106                 [Browsable (false)]
107                 [EditorBrowsable (EditorBrowsableState.Never)]
108                 public new event EventHandler Click;
109                 
110                 [Browsable (false)]
111                 [EditorBrowsable (EditorBrowsableState.Never)]
112                 public new event EventHandler DoubleClick;
113                 
114                 [Browsable (false)]
115                 [EditorBrowsable (EditorBrowsableState.Never)]
116                 public new event EventHandler FontChanged;
117                 
118                 [Browsable (false)]
119                 [EditorBrowsable (EditorBrowsableState.Never)]
120                 public new event EventHandler ForeColorChanged;
121                 
122                 [Browsable (false)]
123                 [EditorBrowsable (EditorBrowsableState.Never)]
124                 public new event EventHandler ImeModeChanged;
125                 
126                 [Browsable (false)]
127                 [EditorBrowsable (EditorBrowsableState.Never)]
128                 public new event MouseEventHandler MouseDown;
129                 
130                 [Browsable (false)]
131                 [EditorBrowsable (EditorBrowsableState.Never)]
132                 public new event MouseEventHandler MouseMove;
133                 
134                 [Browsable (false)]
135                 [EditorBrowsable (EditorBrowsableState.Never)]
136                 public new event MouseEventHandler MouseUp;
137                 
138                 [Browsable (false)]
139                 [EditorBrowsable (EditorBrowsableState.Never)]
140                 public new event PaintEventHandler Paint;
141                 
142                 public event ScrollEventHandler Scroll;
143                 
144                 [Browsable (false)]
145                 [EditorBrowsable (EditorBrowsableState.Never)]
146                 public new event EventHandler TextChanged;
147                 
148                 public event EventHandler ValueChanged;
149                 #endregion Events
150
151                 public ScrollBar ()
152                 {
153                         position = 0;
154                         minimum = 0;
155                         maximum = 100;
156                         large_change = 10;
157                         small_change = 1;
158
159                         timer.Tick += new EventHandler (OnTimer);
160                         base.KeyDown += new KeyEventHandler (OnKeyDownSB);
161                         base.MouseDown += new MouseEventHandler (OnMouseDownSB);
162                         base.MouseUp += new MouseEventHandler (OnMouseUpSB);
163                         base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
164                         base.Resize += new EventHandler (OnResizeSB);
165                         base.TabStop = false;
166
167                         if (ThemeEngine.Current.DoubleBufferingSupported == true) {
168                                 double_buffering = true;
169                         } else {
170                                 double_buffering = false;
171                         }
172
173                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
174                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
175                 }
176
177                 #region Internal & Private Properties
178                 internal Rectangle FirstArrowArea {
179                         get {
180                                 return this.first_arrow_area;
181                         }
182
183                         set {
184                                 this.first_arrow_area = value;
185                         }
186                 }
187
188                 internal Rectangle SecondArrowArea {
189                         get {
190                                 return this.second_arrow_area;
191                         }
192
193                         set {
194                                 this.second_arrow_area = value;
195                         }
196                 }
197
198                 internal Rectangle ThumbPos {
199                         get {
200                                 return thumb_pos;
201                         }
202
203                         set {
204                                 thumb_pos = value;
205                         }
206                 }
207                 #endregion      // Internal & Private Properties
208
209                 #region Public Properties
210
211                 [EditorBrowsable (EditorBrowsableState.Never)]
212                 [Browsable (false)]
213                 public override Color BackColor
214                 {
215                         get { return base.BackColor; }
216                         set {
217                                 if (base.BackColor == value)
218                                         return;
219
220                                 if (BackColorChanged != null)
221                                         BackColorChanged (this, EventArgs.Empty);
222
223                                 base.BackColor = value;
224                                 Refresh ();
225                         }
226                 }
227
228                 [EditorBrowsable (EditorBrowsableState.Never)]
229                 [Browsable (false)]
230                 public override Image BackgroundImage
231                 {
232                         get { return base.BackgroundImage; }
233                         set {
234                                 if (base.BackgroundImage == value)
235                                         return;
236
237                                 if (BackgroundImageChanged != null)
238                                         BackgroundImageChanged (this, EventArgs.Empty);
239
240                                 base.BackgroundImage = value;
241                         }
242                 }
243
244                 protected override CreateParams CreateParams
245                 {
246                         get {   return base.CreateParams; }
247                 }
248
249                 protected override ImeMode DefaultImeMode
250                 {
251                         get { return ImeMode.Disable; }
252                 }
253
254                 [EditorBrowsable (EditorBrowsableState.Never)]
255                 [Browsable (false)]
256                 public override Font Font
257                 {
258                         get { return base.Font; }
259                         set {
260                                 if (base.Font.Equals (value))
261                                         return;
262
263                                 if (FontChanged != null)
264                                         FontChanged (this, EventArgs.Empty);
265
266                                 base.Font = value;
267                         }
268                 }
269
270                 [EditorBrowsable (EditorBrowsableState.Never)]
271                 [Browsable (false)]
272                 public override Color ForeColor
273                 {
274                         get { return base.ForeColor; }
275                         set {
276                                 if (base.ForeColor == value)
277                                         return;
278
279                                 if (ForeColorChanged != null)
280                                         ForeColorChanged (this, EventArgs.Empty);
281
282                                 base.ForeColor = value;
283                                 Refresh ();
284                         }
285                 }
286
287                 [EditorBrowsable (EditorBrowsableState.Never)]
288                 [Browsable (false)]
289                 public new ImeMode ImeMode
290                 {
291                         get { return base.ImeMode; }
292                         set {
293                                 if (base.ImeMode == value)
294                                         return;
295
296                                 if (ImeModeChanged != null)
297                                         ImeModeChanged (this, EventArgs.Empty);
298
299                                 base.ImeMode = value;
300                         }
301                 }
302
303                 [DefaultValue (10)]
304                 [RefreshProperties(RefreshProperties.Repaint)]
305                 public int LargeChange {
306                         get {
307                                 if (large_change > maximum)
308                                         return (maximum + 1);
309                                 else
310                                         return large_change;
311                         }
312                         set {
313                                 if (value < 0)
314                                         throw new Exception( string.Format("Value '{0}' must be greater than or equal to 0.", value));
315
316                                 if (large_change != value) {
317                                         large_change = value;
318
319                                         // thumb area depends on large change value,
320                                         // so we need to recalculate it.
321                                         CalcThumbArea ();
322                                         UpdatePos (Value, true);
323                                         Refresh ();
324                                 }
325                         }
326                 }
327
328                 [DefaultValue (100)]
329                 [RefreshProperties(RefreshProperties.Repaint)]
330                 public int Maximum {
331                         get { return maximum; }
332                         set {
333                                 if (maximum == value)
334                                         return;
335                                         
336                                 maximum = value;
337
338                                 if (maximum < minimum)
339                                         minimum = maximum;
340
341                                 // thumb area depends on maximum value,
342                                 // so we need to recalculate it.
343                                 CalcThumbArea ();
344                                 UpdatePos (Value, true);
345                                 Refresh ();
346                         }
347                 }
348
349                 [DefaultValue (0)]
350                 [RefreshProperties(RefreshProperties.Repaint)]
351                 public int Minimum {
352                         get { return minimum; }
353                         set {
354                                 if (minimum == value)
355                                         return;
356                                         
357                                 minimum = value;
358
359                                 if (minimum > maximum)
360                                         maximum = minimum;
361
362                                 // thumb area depends on minimum value,
363                                 // so we need to recalculate it.
364                                 CalcThumbArea ();
365                                 UpdatePos (Value, true);
366                                 Refresh ();
367                         }
368                 }
369
370                 [DefaultValue (1)]
371                 public int SmallChange {
372                         get { return small_change; }
373                         set {
374                                 if ( value < 0 )
375                                         throw new Exception( string.Format("Value '{0}' must be greater than or equal to 0.", value));
376
377                                 if (small_change != value) {
378                                         small_change = value;
379                                         UpdatePos (Value, true);
380                                         Refresh ();
381                                 }
382                         }
383                 }
384
385                 [DefaultValue (false)]
386                 public new bool TabStop {
387                         get { return base.TabStop; }
388                         set { base.TabStop = value; }
389                 }
390
391                 [EditorBrowsable (EditorBrowsableState.Never)]
392                 [Bindable (false)]
393                 [Browsable (false)]
394                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
395                 public override string Text {
396                          get { return base.Text;  }
397                          set { base.Text = value; }
398                 }
399
400                 [Bindable(true)]
401                 [DefaultValue (0)]
402                 public int Value {
403                         get { return position; }
404                         set {
405                                 if ( value < minimum || value > maximum )
406                                         throw new ArgumentException(
407                                                 string.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
408
409                                 if (position != value){
410                                         position = value;
411
412                                         OnValueChanged (EventArgs.Empty);
413
414                                         ClearDirty ();
415                                         UpdatePos (Value, true);
416                                         InvalidateDirty ();
417                                 }
418                         }
419                 }
420
421                 #endregion //Public Properties
422
423                 #region Public Methods
424                 
425                 protected override void OnEnabledChanged (EventArgs e)
426                 {
427                         base.OnEnabledChanged (e);
428
429                         if (Enabled)
430                                 firstbutton_state = secondbutton_state = ButtonState.Normal;
431                         else
432                                 firstbutton_state = secondbutton_state = ButtonState.Inactive;
433
434                         Refresh ();
435                 }
436                 
437                 protected override void OnHandleCreated (System.EventArgs e)
438                 {
439                         base.OnHandleCreated (e);               
440
441                         CalcButtonSizes ();
442                         CalcThumbArea ();
443                         UpdatePos (Value, true);
444                 }
445
446                 protected virtual void OnScroll (ScrollEventArgs event_args)
447                 {
448                         if (Scroll == null)
449                                 return;
450
451                         Scroll (this, event_args);
452                 }
453
454                 protected virtual void OnValueChanged (EventArgs e)
455                 {
456                         if (ValueChanged != null)
457                                 ValueChanged (this, e);
458                 }
459
460                 public override string ToString()
461                 {
462                         return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
463                                                 GetType( ).FullName.ToString( ), minimum, maximum, position);
464                 }
465
466                 protected void UpdateScrollInfo ()
467                 {
468                         Refresh ();
469                 }
470
471                 protected override void WndProc (ref Message m)
472                 {
473                         switch ((Msg) m.Msg)
474                         {
475                                 case Msg.WM_PAINT:
476                                 {
477                                         PaintEventArgs  paint_event;
478
479                                         paint_event = XplatUI.PaintEventStart (Handle);
480                                         OnPaintSB (paint_event);
481                                         XplatUI.PaintEventEnd (Handle);
482                                         return;
483                                 }
484
485
486                                 case Msg.WM_ERASEBKGND:
487                                         m.Result = (IntPtr) 1; /// Disable background painting to avoid flickering
488                                         return;
489
490                                 default:
491                                         break;
492                         }
493
494                         base.WndProc (ref m);
495                 }
496
497                 #endregion //Public Methods
498
499                 #region Private Methods
500                 
501                 private void CalcButtonSizes ()
502                 {               
503                         if (vert) {
504                                 if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
505                                         scrollbutton_height = Height /2;
506                                 else
507                                         scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
508                                 
509                         } else {
510                                 if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
511                                         scrollbutton_width = Width /2;
512                                 else
513                                         scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
514                         }
515                 }
516                                 
517                 private void CalcThumbArea ()
518                 {
519                         // Thumb area
520                         if (vert) {
521
522                                 thumb_area.Height = Height - scrollbutton_height -  scrollbutton_height;
523                                 thumb_area.X = 0;
524                                 thumb_area.Y = scrollbutton_height;
525                                 thumb_area.Width = Width;
526
527                                 if (Height < thumb_notshown_size)
528                                         thumb_size = 0;
529                                 else {
530                                         double per =  ((double) this.LargeChange / (double)((1 + maximum - minimum)));
531                                         thumb_size = 1 + (int) (thumb_area.Height * per);                                       
532                                         
533                                         if (thumb_size < thumb_min_size)
534                                                 thumb_size = thumb_min_size;
535                                 }                               
536
537                                 pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - this.LargeChange) + 1));
538
539                         } else  {
540
541                                 thumb_area.Y = 0;
542                                 thumb_area.X = scrollbutton_width;
543                                 thumb_area.Height = Height;
544                                 thumb_area.Width = Width - scrollbutton_width -  scrollbutton_width;    
545                                 
546                                 if (Width < thumb_notshown_size)
547                                         thumb_size = 0;
548                                 else {
549                                         double per =  ((double) this.LargeChange / (double)((1 + maximum - minimum)));
550                                         thumb_size = 1 + (int) (thumb_area.Width * per);
551                                         
552                                         if (thumb_size < thumb_min_size)
553                                                 thumb_size = thumb_min_size;
554                                 }
555                                 
556                                 pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - this.LargeChange) + 1));
557                         }
558                 }
559                 
560                 private void Draw (Rectangle clip)
561                 {
562                         ThemeEngine.Current.DrawScrollBar(DeviceContext, clip, this);
563                 }
564
565                 private void LargeIncrement ()
566                 {                       
567                         ScrollEventArgs event_args;
568                         int pos = position + large_change;
569                         
570                         event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
571                         OnScroll (event_args);                          
572                         pos = event_args.NewValue;                      
573                         
574                         event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
575                         OnScroll (event_args);          
576                         pos = event_args.NewValue;                      
577
578                         UpdatePos (pos, true);
579                 }
580
581                 private void LargeDecrement ()
582                 {                       
583                         ScrollEventArgs event_args;
584                         int pos = position - large_change;
585                         
586                         event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
587                         OnScroll (event_args);
588                         pos = event_args.NewValue;                      
589                         
590                         event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
591                         OnScroll (event_args);
592                         pos = event_args.NewValue;
593                         
594
595                         UpdatePos (pos, true);
596                 }               
597                 
598                 private void OnResizeSB (Object o, EventArgs e)
599                 {                       
600                         if (Width <= 0 || Height <= 0)
601                                 return;
602                         
603                         CalcButtonSizes ();
604                         CalcThumbArea ();
605                         UpdatePos (position, true);
606                 }
607
608                 private void OnPaintSB (PaintEventArgs pevent)
609                 {
610                         if (Paint != null) {
611                                 Paint (this, pevent);
612                         }
613                         
614                         if (Width <= 0 || Height <=  0 || Visible == false)
615                                 return;
616
617                         Draw (pevent.ClipRectangle);
618                         pevent.Graphics.DrawImage (ImageBuffer, pevent.ClipRectangle, pevent.ClipRectangle, GraphicsUnit.Pixel);
619                 }
620
621                 private void OnTimer (Object source, EventArgs e)
622                 {
623                         ClearDirty ();
624
625                         switch (timer_type) {
626
627                         case TimerType.HoldButton:
628                                 SetRepeatButtonTimer ();
629                                 break;
630
631                         case TimerType.RepeatButton:
632                         {
633                                 if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)
634                                         SmallDecrement();
635
636                                 if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)
637                                         SmallIncrement();
638
639                                 break;
640                         }
641
642                         case TimerType.HoldThumbArea:
643                                 SetRepeatThumbAreaTimer ();
644                                 break;
645
646                         case TimerType.RepeatThumbArea:
647                         {
648                                 Point pnt, pnt_screen;
649                                 Rectangle thumb_area_screen = thumb_area;
650
651                                 pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
652                                 thumb_area_screen.X = pnt_screen.X;
653                                 thumb_area_screen.Y = pnt_screen.Y;
654                                 
655                                 if (thumb_area_screen.Contains (MousePosition) == false) {                                      
656                                         timer.Enabled = false;
657                                         thumb_moving = ThumbMoving.None;
658                                         DirtyThumbArea ();
659                                         InvalidateDirty ();
660                                 }                               
661                                 
662                                 pnt = PointToClient (MousePosition);
663                                 
664                                 if (vert)
665                                         lastclick_pos = pnt.Y;
666                                 else
667                                         lastclick_pos = pnt.X;
668
669                                 if (thumb_moving == ThumbMoving.Forward) {
670                                         if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
671                                                 (!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
672                                                 (thumb_area.Contains (pnt) == false)){
673                                                 timer.Enabled = false;                                          
674                                                 thumb_moving = ThumbMoving.None;
675                                                 Refresh ();                     
676                                                 return;
677                                         } else {
678                                                 LargeIncrement ();
679                                 }
680                                 }
681                                 else
682                                         if ((vert && (thumb_pos.Y < lastclick_pos)) ||
683                                                 (!vert && (thumb_pos.X  < lastclick_pos))){
684                                                 timer.Enabled = false;
685                                                 thumb_moving = ThumbMoving.None;
686                                                 Refresh ();                                             
687                                         } else {
688                                                 LargeDecrement ();
689                                         }
690
691                                 break;
692                         }
693                         default:
694                                 break;
695                         }
696
697                         InvalidateDirty ();
698                 }               
699
700                 private void OnMouseMoveSB (object sender, MouseEventArgs e)
701                 {
702                         if (MouseMove != null) {
703                                 MouseMove (this, e);
704                         }
705                                 
706                         if (Enabled == false || thumb_size == 0)
707                                 return;
708
709                         if (firstbutton_pressed) {
710                                 if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
711                                         firstbutton_state = ButtonState.Normal;
712                                         Invalidate (first_arrow_area);
713                                         return;
714                                 } else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
715                                         firstbutton_state = ButtonState.Pushed;
716                                         Invalidate (first_arrow_area);
717                                         return;
718                                 }
719                         } else if (secondbutton_pressed) {
720                                 if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
721                                         secondbutton_state = ButtonState.Normal;
722                                         Invalidate (second_arrow_area);
723                                         return;
724                                 } else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
725                                         secondbutton_state = ButtonState.Pushed;
726                                         Invalidate (second_arrow_area);
727                                         return;
728                                 }
729                         } else if (thumb_pressed == true) {
730                                 int pixel_pos;
731
732                                 if (vert) {
733
734                                         int mouse_click = e.Y;
735                                         int outside_curpos = thumb_area.Y + thumb_area.Height - thumb_size + lastclick_pos_thumb;
736                                         
737                                         
738                                         if (mouse_click > thumb_area.Y + thumb_area.Height) {
739                                                 outside_thumbarea_right = true;
740                                                 mouse_click = thumb_area.Y + thumb_area.Height;
741                                         }
742
743                                         if (mouse_click < thumb_area.Y) {
744                                                 outside_thumbarea_left = true;
745                                                 mouse_click = thumb_area.Y;
746                                         }
747
748                                         if (outside_thumbarea_right && mouse_click < outside_curpos) {
749                                                 outside_thumbarea_right = false;
750                                                 thumb_pixel_click_move_prev =
751                                                 thumb_pixel_click_move = outside_curpos;
752                                         }
753
754                                         if (outside_thumbarea_left && mouse_click > thumb_area.Y + lastclick_pos_thumb) {
755                                                 outside_thumbarea_left = false;
756                                                 thumb_pixel_click_move_prev =
757                                                 thumb_pixel_click_move = thumb_area.Y + lastclick_pos_thumb;
758                                         }
759
760                                         if (outside_thumbarea_right == false && outside_thumbarea_left == false) {
761                                                 pixel_pos = thumb_pos.Y + (thumb_pixel_click_move - thumb_pixel_click_move_prev);
762                                                 thumb_pixel_click_move_prev = thumb_pixel_click_move;
763                                                 thumb_pixel_click_move = mouse_click;
764                                                 
765                                                 UpdateThumbPos (pixel_pos, true);
766                                                 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
767                                         }
768
769                                 }
770                                 else {
771                                         int mouse_click = e.X;
772                                         int outside_curpos = thumb_area.X + thumb_area.Width - thumb_size + lastclick_pos_thumb;
773                                                                                 
774                                         if (mouse_click >  thumb_area.X + thumb_area.Width) {
775                                                 outside_thumbarea_right = true;
776                                                 mouse_click = thumb_area.X + thumb_area.Width;
777                                         }
778
779                                         if (mouse_click <  thumb_area.X) {
780                                                 outside_thumbarea_left = true;
781                                                 mouse_click = thumb_area.X;
782                                         }
783
784                                         if (outside_thumbarea_right && mouse_click < outside_curpos) {
785                                                 outside_thumbarea_right = false;
786                                                 thumb_pixel_click_move_prev =
787                                                 thumb_pixel_click_move = outside_curpos;
788                                         }
789
790                                         if (outside_thumbarea_left && mouse_click > thumb_area.X + lastclick_pos_thumb) {
791                                                 outside_thumbarea_left = false;
792                                                 thumb_pixel_click_move_prev =
793                                                 thumb_pixel_click_move = thumb_area.X + lastclick_pos_thumb;
794                                         }
795
796                                         if (outside_thumbarea_right == false && outside_thumbarea_left == false) {
797                                                 pixel_pos = thumb_pos.X + (thumb_pixel_click_move - thumb_pixel_click_move_prev);
798                                                 thumb_pixel_click_move_prev = thumb_pixel_click_move;
799                                                 thumb_pixel_click_move = mouse_click;
800                                                 UpdateThumbPos (pixel_pos, true);                                               
801                                                 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
802                                         }
803
804                                 }
805
806                                 Refresh ();
807                         }
808
809                 }
810
811                 private void OnMouseDownSB (object sender, MouseEventArgs e)
812                 {
813                         ClearDirty ();
814                         
815                         if (e.Button == MouseButtons.Right) {
816                                 if (MouseDown != null) {
817                                         MouseDown (this, e);
818                                 }
819                         }
820                         
821                         if (Enabled == false)
822                                 return;
823
824                         if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
825                                 this.Capture = true;                            
826                                 firstbutton_state = ButtonState.Pushed;
827                                 firstbutton_pressed = true;
828                                 Invalidate (first_arrow_area);
829                                 if (!timer.Enabled) {
830                                         SetHoldButtonClickTimer ();
831                                         timer.Enabled = true;
832                                 }
833                         }
834
835                         if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
836                                 this.Capture = true;                            
837                                 secondbutton_state = ButtonState.Pushed;
838                                 secondbutton_pressed = true;
839                                 Invalidate (second_arrow_area);
840                                 if (!timer.Enabled) {
841                                         SetHoldButtonClickTimer ();
842                                         timer.Enabled = true;
843                                 }
844                         }
845
846                         if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
847                                 thumb_pressed = true;
848                                 this.Capture = true;
849                                 if (vert) {
850                                         lastclick_pos_thumb = e.Y - thumb_pos.Y;
851                                         lastclick_pos = e.Y;                                    
852                                         thumb_pixel_click_move_prev = thumb_pixel_click_move = e.Y;
853                                 }
854                                 else {
855                                         lastclick_pos_thumb = e.X - thumb_pos.X;
856                                         lastclick_pos = e.X;
857                                         thumb_pixel_click_move_prev = thumb_pixel_click_move = e.X;
858                                 }
859                         } else {
860                                 if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
861
862                                         if (vert) {
863                                                 lastclick_pos_thumb = e.Y - thumb_pos.Y;
864                                                 lastclick_pos = e.Y;
865
866                                                 if (e.Y > thumb_pos.Y + thumb_pos.Height) {
867                                                         LargeIncrement ();                                                      
868                                                         thumb_moving = ThumbMoving.Forward;                                                     
869                                                         Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
870                                                                                       ClientRectangle.Width,
871                                                                                       ClientRectangle.Height -  (thumb_pos.Y + thumb_pos.Height) -
872                                                                                       scrollbutton_height));
873                                                 } else {
874                                                         LargeDecrement ();                                                      
875                                                         thumb_moving = ThumbMoving.Backwards;
876                                                         Dirty (new Rectangle (0,  scrollbutton_height,
877                                                                                       ClientRectangle.Width,
878                                                                                       thumb_pos.Y - scrollbutton_height));
879                                                 }
880                                         } else {
881
882                                                 lastclick_pos_thumb = e.X - thumb_pos.X;
883                                                 lastclick_pos = e.X;
884
885                                                 if (e.X > thumb_pos.X + thumb_pos.Width) {
886                                                         thumb_moving = ThumbMoving.Forward;
887                                                         LargeIncrement ();                                                      
888                                                         Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
889                                                                                       ClientRectangle.Width -  (thumb_pos.X + thumb_pos.Width) -
890                                                                                       scrollbutton_width,
891                                                                                       ClientRectangle.Height));
892                                                 } else {
893                                                         thumb_moving = ThumbMoving.Backwards;
894                                                         LargeDecrement ();                                                      
895                                                         Dirty (new Rectangle (scrollbutton_width,  0,
896                                                                                       thumb_pos.X - scrollbutton_width,
897                                                                                       ClientRectangle.Height));
898                                                 }
899                                         }
900
901                                         SetHoldThumbAreaTimer ();
902                                         timer.Enabled = true;
903                                         InvalidateDirty ();
904                                 }
905                         }
906                 }
907                 
908                 private void OnMouseUpSB (object sender, MouseEventArgs e)
909                 {
910                         ClearDirty ();
911
912                         if (e.Button == MouseButtons.Right) {
913                                 if (MouseUp != null) {
914                                         MouseUp (this, e);
915                                 }
916                         }
917                         
918                         if (Enabled == false)
919                                 return;
920
921                         timer.Enabled = false;
922                         if (thumb_moving != ThumbMoving.None) {
923                                 DirtyThumbArea ();
924                                 thumb_moving = ThumbMoving.None;
925                         }                       
926                         this.Capture = false;
927
928                         if (firstbutton_pressed) {
929                                 firstbutton_state = ButtonState.Normal;
930                                 if (first_arrow_area.Contains (e.X, e.Y)) {
931                                         SmallDecrement ();
932                                 }
933                                 firstbutton_pressed = false;
934                                 Dirty (first_arrow_area);
935                         } else if (secondbutton_pressed) {
936                                 secondbutton_state = ButtonState.Normal;
937                                 if (second_arrow_area.Contains (e.X, e.Y)) {
938                                         SmallIncrement ();
939                                 }
940                                 Dirty (second_arrow_area);
941                                 secondbutton_pressed = false;
942                         } else if (thumb_pressed == true) {
943                                 OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
944                                 OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
945                                 thumb_pressed = false;
946                                 Refresh ();
947                                 return;
948                         }
949
950                         InvalidateDirty ();
951                 }
952
953                 private void OnKeyDownSB (Object o, KeyEventArgs key)
954                 {
955                         if (Enabled == false)
956                                 return;
957
958                         ClearDirty ();
959
960                         switch (key.KeyCode){
961                         case Keys.Up:
962                         {
963                                 SmallDecrement ();
964                                 break;
965                         }
966                         case Keys.Down:
967                         {
968                                 SmallIncrement ();
969                                 break;
970                         }
971                         case Keys.PageUp:
972                         {
973                                 LargeDecrement ();
974                                 break;
975                         }
976                         case Keys.PageDown:
977                         {
978                                 LargeIncrement ();
979                                 break;
980                         }
981                         case Keys.Home:
982                         {               
983                                 SetHomePosition ();                             
984                                 break;
985                         }                       
986                         case Keys.End:
987                         {       
988                                 SetEndPosition ();
989                                 break;
990                         }
991                         default:
992                                 break;
993                         }
994
995                         InvalidateDirty ();
996                 }               
997                 
998                 private void SetEndPosition () 
999                 {                       
1000                         ScrollEventArgs event_args;
1001                         int pos = Maximum;
1002                         
1003                         event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
1004                         OnScroll (event_args);
1005                         pos = event_args.NewValue;                      
1006                         
1007                         event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1008                         OnScroll (event_args);                  
1009                         pos = event_args.NewValue;                      
1010
1011                         SetValue (pos);
1012                 }
1013                 
1014                 private void SetHomePosition ()
1015                 {
1016                         ScrollEventArgs event_args;
1017                         int pos = Minimum;
1018                         
1019                         event_args = new ScrollEventArgs (ScrollEventType.First, pos);
1020                         OnScroll (event_args);
1021                         pos = event_args.NewValue;
1022                                                 
1023                         event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1024                         OnScroll (event_args);                  
1025                         pos = event_args.NewValue;                      
1026                         
1027                         SetValue (pos);
1028                 }               
1029
1030                 private void SmallIncrement ()
1031                 {
1032                         ScrollEventArgs event_args;
1033                         int pos = position + small_change;
1034                         
1035                         event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
1036                         OnScroll (event_args);                          
1037                         pos = event_args.NewValue;                      
1038                         
1039                         event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1040                         OnScroll (event_args);                  
1041                         pos = event_args.NewValue;                      
1042
1043                         UpdatePos (pos, true);
1044                 }
1045
1046                 private void SmallDecrement ()
1047                 {                       
1048                         ScrollEventArgs event_args;
1049                         int pos = position - small_change;
1050                         
1051                         event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
1052                         OnScroll (event_args);
1053                         pos = event_args.NewValue;
1054                                                 
1055                         event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
1056                         OnScroll (event_args);                  
1057                         pos = event_args.NewValue;                      
1058
1059                         UpdatePos (pos, true);
1060                 }
1061                 
1062                 private void SetHoldButtonClickTimer ()
1063                 {
1064                         timer.Enabled = false;
1065                         timer.Interval = 200;
1066                         timer_type = TimerType.HoldButton;
1067                         timer.Enabled = true;
1068                 }
1069
1070                 private void SetRepeatButtonTimer ()
1071                 {
1072                         timer.Enabled = false;
1073                         timer.Interval = 50;
1074                         timer_type = TimerType.RepeatButton;
1075                         timer.Enabled = true;
1076                 }
1077
1078                 private void SetHoldThumbAreaTimer ()
1079                 {
1080                         timer.Enabled = false;
1081                         timer.Interval = 200;
1082                         timer_type = TimerType.HoldThumbArea;
1083                         timer.Enabled = true;
1084                 }
1085
1086                 private void SetRepeatThumbAreaTimer ()
1087                 {
1088                         timer.Enabled = false;
1089                         timer.Interval = 50;
1090                         timer_type = TimerType.RepeatThumbArea;
1091                         timer.Enabled = true;
1092                 }                               
1093                 
1094                 private void UpdatePos (int newPos, bool update_thumbpos)
1095                 {
1096                         int old = position;
1097                         int pos;
1098
1099                         if (newPos < minimum)
1100                                 pos = minimum;
1101                         else
1102                                 if (newPos > maximum + 1 - large_change)
1103                                         pos = maximum + 1 - large_change;
1104                                         else
1105                                                 pos = newPos;
1106
1107                         // pos can't be less than minimum
1108                         if (pos < minimum)
1109                                 pos = minimum;
1110
1111                         if (update_thumbpos) {
1112                                 if (vert)
1113                                         UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), false);
1114                                 else
1115                                         UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), false);
1116                                 SetValue (pos);
1117                         }
1118                         else {
1119                                 position = pos; // Updates directly the value to avoid thumb pos update
1120                                 
1121                                 if (ValueChanged != null)
1122                                         ValueChanged (this, EventArgs.Empty);
1123                         }                       
1124                 }
1125
1126                 private void UpdateThumbPos (int pixel, bool update_value)
1127                 {
1128                         float new_pos = 0;
1129
1130                         if (vert) {
1131                                 Dirty (thumb_pos);
1132                                 if (pixel < thumb_area.Y)
1133                                         thumb_pos.Y = thumb_area.Y;
1134                                 else
1135                                         if (pixel > thumb_area.Y + thumb_area.Height - thumb_size)
1136                                                 thumb_pos.Y = thumb_area.Y +  thumb_area.Height - thumb_size;
1137                                         else
1138                                                 thumb_pos.Y = pixel;
1139
1140                                 thumb_pos.X = 0;
1141                                 thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
1142                                 thumb_pos.Height = thumb_size;
1143                                 new_pos = (float) (thumb_pos.Y - thumb_area.Y);
1144                                 new_pos = new_pos / pixel_per_pos;
1145
1146                                 Dirty (thumb_pos);
1147                         } else  {
1148                                 Dirty (thumb_pos);
1149                                 if (pixel < thumb_area.X)
1150                                         thumb_pos.X = thumb_area.X;
1151                                 else
1152                                         if (pixel > thumb_area.X + thumb_area.Width - thumb_size)
1153                                                 thumb_pos.X = thumb_area.X +  thumb_area.Width - thumb_size;
1154                                         else
1155                                                 thumb_pos.X = pixel;
1156
1157                                 thumb_pos.Y = 0;
1158                                 thumb_pos.Width =  thumb_size;
1159                                 thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
1160                                 new_pos = (float) (thumb_pos.X - thumb_area.X);
1161                                 new_pos = new_pos / pixel_per_pos;
1162
1163                                 Dirty (thumb_pos);
1164                         }
1165
1166                         if (update_value)
1167                                 UpdatePos ((int) new_pos + minimum, false);
1168                 }
1169
1170                 private void SetValue (int value)
1171                 {
1172                         if ( value < minimum || value > maximum )
1173                                 throw new ArgumentException(
1174                                         String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
1175
1176                         if (position != value){
1177                                 position = value;
1178
1179                                 OnValueChanged (EventArgs.Empty);
1180                                 UpdatePos (value, true);
1181                         }
1182                 }
1183
1184                 private void ClearDirty ()
1185                 {
1186                         dirty = Rectangle.Empty;
1187                 }
1188
1189                 private void Dirty (Rectangle r)
1190                 {
1191                         if (dirty == Rectangle.Empty) {
1192                                 dirty = r;
1193                                 return;
1194                         }
1195                         dirty = Rectangle.Union (dirty, r);
1196                 }
1197
1198                 private void DirtyThumbArea ()
1199                 {
1200                         if (thumb_moving == ThumbMoving.Forward) {
1201                                 if (vert) {
1202                                         Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
1203                                                                       ClientRectangle.Width,
1204                                                                       ClientRectangle.Height -  (thumb_pos.Y + thumb_pos.Height) -
1205                                                                       scrollbutton_height));
1206                                 } else {
1207                                         Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
1208                                                                       ClientRectangle.Width -  (thumb_pos.X + thumb_pos.Width) -
1209                                                                       scrollbutton_width,
1210                                                                       ClientRectangle.Height));
1211                                 }
1212                         } else if (thumb_moving == ThumbMoving.Backwards) {
1213                                 if (vert) {
1214                                         Dirty(new Rectangle (0,  scrollbutton_height,
1215                                                                       ClientRectangle.Width,
1216                                                                       thumb_pos.Y - scrollbutton_height));
1217                                 } else {
1218                                         Dirty (new Rectangle (scrollbutton_width,  0,
1219                                                                       thumb_pos.X - scrollbutton_width,
1220                                                                       ClientRectangle.Height));
1221                                 }
1222                         }
1223                 }
1224
1225                 private void InvalidateDirty ()
1226                 {
1227                         Invalidate (dirty);
1228                         dirty = Rectangle.Empty;
1229                 }
1230
1231                 #endregion //Private Methods
1232          }
1233 }
1234
1235