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