2007-06-13 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ScrollableControl.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 Novell, Inc.
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25
26 using System;
27 using System.ComponentModel;
28 using System.ComponentModel.Design;
29 using System.Drawing;
30 using System.Runtime.InteropServices;
31
32 namespace System.Windows.Forms {
33         [Designer ("System.Windows.Forms.Design.ScrollableControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
34 #if NET_2_0
35         [ClassInterface (ClassInterfaceType.AutoDispatch)]
36         [ComVisible (true)]
37 #endif
38         public class ScrollableControl : Control {
39                 #region Local Variables
40                 private bool                    force_hscroll_visible;
41                 private bool                    force_vscroll_visible;
42                 private bool                    auto_scroll;
43                 private Size                    auto_scroll_margin;
44                 private Size                    auto_scroll_min_size;
45                 private Point                   scroll_position;
46                 private DockPaddingEdges        dock_padding;
47                 private SizeGrip                sizegrip;
48                 internal ImplicitHScrollBar     hscrollbar;
49                 internal ImplicitVScrollBar     vscrollbar;
50                 private Size                    canvas_size;
51                 private Rectangle               display_rectangle;
52                 private Control                 old_parent;
53
54 #if NET_2_0
55                 private HScrollProperties       horizontalScroll;
56                 private VScrollProperties       verticalScroll;
57 #endif
58                 #endregion      // Local Variables
59
60                 [TypeConverter(typeof(ScrollableControl.DockPaddingEdgesConverter))]
61                 #region Subclass DockPaddingEdges
62                 public class DockPaddingEdges : ICloneable {
63                         #region DockPaddingEdges Local Variables
64                         private int     all;
65                         private int     left;
66                         private int     right;
67                         private int     top;
68                         private int     bottom;
69                         private Control owner;
70                         #endregion      // DockPaddingEdges Local Variables
71
72                         #region DockPaddingEdges Constructor
73                         internal DockPaddingEdges(Control owner) {
74                                 all = 0;
75                                 left = 0;
76                                 right = 0;
77                                 top = 0;
78                                 bottom = 0;
79                                 this.owner = owner;
80                         }
81                         #endregion      // DockPaddingEdges Constructor
82
83                         #region DockPaddingEdges Public Instance Properties
84                         [RefreshProperties(RefreshProperties.All)]
85                         public int All {
86                                 get {
87                                         return all;
88                                 }
89
90                                 set {
91                                         all = value;
92                                         left = value;
93                                         right = value;
94                                         top = value;
95                                         bottom = value;
96
97                                         owner.PerformLayout();
98                                 }
99                         }
100
101                         [RefreshProperties(RefreshProperties.All)]
102                         public int Bottom {
103                                 get {
104                                         return bottom;
105                                 }
106
107                                 set {
108                                         bottom = value;
109                                         all = 0;
110
111                                         owner.PerformLayout();
112                                 }
113                         }
114
115                         [RefreshProperties(RefreshProperties.All)]
116                         public int Left {
117                                 get {
118                                         return left;
119                                 }
120
121                                 set {
122                                         left=value;
123                                         all = 0;
124
125                                         owner.PerformLayout();
126                                 }
127                         }
128
129                         [RefreshProperties(RefreshProperties.All)]
130                         public int Right {
131                                 get {
132                                         return right;
133                                 }
134
135                                 set {
136                                         right=value;
137                                         all = 0;
138
139                                         owner.PerformLayout();
140                                 }
141                         }
142
143                         [RefreshProperties(RefreshProperties.All)]
144                         public int Top {
145                                 get {
146                                         return top;
147                                 }
148
149                                 set {
150                                         top=value;
151                                         all = 0;
152
153                                         owner.PerformLayout();
154                                 }
155                         }
156                         #endregion      // DockPaddingEdges Public Instance Properties
157
158                         // Public Instance Methods
159                         public override bool Equals(object other) {
160                                 if (! (other is DockPaddingEdges)) {
161                                         return false;
162                                 }
163
164                                 if (    (this.all == ((DockPaddingEdges)other).all) && (this.left == ((DockPaddingEdges)other).left) &&
165                                         (this.right == ((DockPaddingEdges)other).right) && (this.top == ((DockPaddingEdges)other).top) && 
166                                         (this.bottom == ((DockPaddingEdges)other).bottom)) {
167                                         return true;
168                                 }
169
170                                 return false;
171                         }
172
173                         public override int GetHashCode() {
174                                 return all*top*bottom*right*left;
175                         }
176
177                         public override string ToString() {
178                                 return "All = "+all.ToString()+" Top = "+top.ToString()+" Left = "+left.ToString()+" Bottom = "+bottom.ToString()+" Right = "+right.ToString();
179                         }
180
181                         internal void Scale(float dx, float dy) {
182                                 left = (int) (left * dx);
183                                 right = (int) (right * dx);
184                                 top = (int) (top * dy);
185                                 bottom = (int) (bottom * dy);
186                         }
187
188                         object ICloneable.Clone() {
189                                 DockPaddingEdges padding_edge;
190
191                                 padding_edge=new DockPaddingEdges(owner);
192
193                                 padding_edge.all=all;
194                                 padding_edge.left=left;
195                                 padding_edge.right=right;
196                                 padding_edge.top=top;
197                                 padding_edge.bottom=bottom;
198
199                                 return padding_edge;
200                         }
201                 }
202                 #endregion      // Subclass DockPaddingEdges
203
204                 #region Subclass DockPaddingEdgesConverter
205                 public class DockPaddingEdgesConverter : System.ComponentModel.TypeConverter {
206                         // Public Constructors
207                         public DockPaddingEdgesConverter() {
208                         }
209
210                         // Public Instance Methods
211                         public override PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, Attribute[] attributes) {
212                                 return TypeDescriptor.GetProperties(typeof(DockPaddingEdges), attributes);
213                         }
214
215                         public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) {
216                                 return true;
217                         }
218                 }
219                 #endregion      // Subclass DockPaddingEdgesConverter
220
221                 #region Public Constructors
222                 public ScrollableControl() {
223                         SetStyle(ControlStyles.ContainerControl, true);
224                         SetStyle(ControlStyles.AllPaintingInWmPaint, false);
225
226                         auto_scroll = false;
227                         force_hscroll_visible = false;
228                         force_vscroll_visible = false;
229                         auto_scroll_margin = new Size(0, 0);
230                         auto_scroll_min_size = new Size(0, 0);
231                         scroll_position = new Point(0, 0);
232                         dock_padding = new DockPaddingEdges(this);
233                         SizeChanged +=new EventHandler(Recalculate);
234                         VisibleChanged += new EventHandler (VisibleChangedHandler);
235                         LocationChanged += new EventHandler (LocationChangedHandler);
236                         ParentChanged += new EventHandler (ParentChangedHandler);
237                         HandleCreated += new EventHandler (AddScrollbars);
238
239                         CreateScrollbars ();
240                         
241 #if NET_2_0
242                         horizontalScroll = new HScrollProperties (this);
243                         verticalScroll = new VScrollProperties (this);
244 #endif
245                 }
246
247                 void VisibleChangedHandler (object sender, EventArgs e)
248                 {
249                         Recalculate (false);
250                 }
251
252                 void LocationChangedHandler (object sender, EventArgs e)
253                 {
254                         UpdateSizeGripVisible ();
255                 }
256
257                 void ParentChangedHandler (object sender, EventArgs e)
258                 {
259                         
260                         if (old_parent == Parent)
261                                 return;
262                                 
263                         if (old_parent != null) {
264                                 old_parent.SizeChanged -= new EventHandler (Parent_SizeChanged);
265 #if NET_2_0                             
266                                 old_parent.PaddingChanged -= new EventHandler (Parent_PaddingChanged);
267 #endif
268                         }
269                         
270                         if (Parent != null) {
271                                 Parent.SizeChanged += new EventHandler (Parent_SizeChanged);
272 #if NET_2_0
273                                 Parent.PaddingChanged += new EventHandler (Parent_PaddingChanged);
274 #endif
275                         }
276                         
277                         old_parent = Parent;
278                 }
279 #if NET_2_0
280                 void Parent_PaddingChanged (object sender, EventArgs e)
281                 {
282                         UpdateSizeGripVisible ();
283                 }
284 #endif
285                 void Parent_SizeChanged (object sender, EventArgs e)
286                 {
287                         UpdateSizeGripVisible ();
288                 }
289                 #endregion      // Public Constructors
290
291                 #region Protected Static Fields
292                 protected const int ScrollStateAutoScrolling    = 1;
293                 protected const int ScrollStateFullDrag         = 16;
294                 protected const int ScrollStateHScrollVisible   = 2;
295                 protected const int ScrollStateUserHasScrolled  = 8;
296                 protected const int ScrollStateVScrollVisible   = 4;
297                 #endregion      // Protected Static Fields
298
299                 #region Public Instance Properties
300                 [DefaultValue(false)]
301                 [Localizable(true)]
302                 [MWFCategory("Layout")]
303                 public virtual bool AutoScroll {
304                         get {
305                                 return  auto_scroll;
306                         }
307
308                         set {
309                                 if (auto_scroll == value) {
310                                         return;
311                                 }
312
313                                 auto_scroll = value;
314                         }
315                 }
316
317                 [Localizable(true)]
318                 [MWFCategory("Layout")]
319                 public Size AutoScrollMargin {
320                         get {
321                                 return auto_scroll_margin;
322                         }
323
324                         set {
325                                 if (value.Width < 0) {
326                                         throw new ArgumentException("Width is assigned less than 0", "value.Width");
327                                 }
328
329                                 if (value.Height < 0) {
330                                         throw new ArgumentException("Height is assigned less than 0", "value.Height");
331                                 }
332
333                                 auto_scroll_margin = value;
334                         }
335                 }
336
337                 [Localizable(true)]
338                 [MWFCategory("Layout")]
339                 public Size AutoScrollMinSize {
340                         get {
341                                 return auto_scroll_min_size;
342                         }
343
344                         set {
345                                 if (value != auto_scroll_min_size) {
346                                         auto_scroll_min_size = value;
347                                         AutoScroll = true;
348                                 }
349                         }
350                 }
351
352                 [Browsable(false)]
353                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
354                 public Point AutoScrollPosition {
355                         get {
356                                 return new Point(-scroll_position.X, -scroll_position.Y);
357                         }
358
359                         set {
360                                 if ((value.X != scroll_position.X) || (value.Y != scroll_position.Y)) {
361                                         int     shift_x;
362                                         int     shift_y;
363
364                                         shift_x = 0;
365                                         shift_y = 0;
366                                         if (hscrollbar.VisibleInternal) {
367                                                 shift_x = value.X - scroll_position.X;
368                                         }
369
370                                         if (vscrollbar.VisibleInternal) {
371                                                 shift_y = value.Y - scroll_position.Y;
372                                         }
373
374                                         ScrollWindow(shift_x, shift_y);
375
376                                         if (hscrollbar.VisibleInternal) {
377                                                 hscrollbar.Value = scroll_position.X;
378                                         }
379
380                                         if (vscrollbar.VisibleInternal) {
381                                                 vscrollbar.Value = scroll_position.Y;
382                                         }
383
384                                 }
385                         }
386                 }
387
388                 public override Rectangle DisplayRectangle {
389                         get {
390                                 if (auto_scroll) {
391                                         int             width;
392                                         int             height;
393
394                                         if (canvas_size.Width <= base.DisplayRectangle.Width) {
395                                                 width = base.DisplayRectangle.Width;
396                                                 if (vscrollbar.VisibleInternal) {
397                                                         width -= vscrollbar.Width;
398                                                 }
399                                         } else {
400                                                 width = canvas_size.Width;
401                                         }
402
403                                         if (canvas_size.Height <= base.DisplayRectangle.Height) {
404                                                 height = base.DisplayRectangle.Height;
405                                                 if (hscrollbar.VisibleInternal) {
406                                                         height -= hscrollbar.Height;
407                                                 }
408                                         } else {
409                                                 height = canvas_size.Height;
410                                         }
411
412                                         display_rectangle.X = -scroll_position.X;
413                                         display_rectangle.Y = -scroll_position.Y;
414                                         display_rectangle.Width = Math.Max(auto_scroll_min_size.Width, width);
415                                         display_rectangle.Height = Math.Max(auto_scroll_min_size.Height, height);
416                                 }
417                                 else {
418                                         display_rectangle = base.DisplayRectangle;
419                                 }
420
421                                 display_rectangle.X += dock_padding.Left;
422                                 display_rectangle.Y += dock_padding.Top;
423                                 display_rectangle.Width -= dock_padding.Left + dock_padding.Right;
424                                 display_rectangle.Height -= dock_padding.Top + dock_padding.Bottom;
425
426                                 return display_rectangle;
427                         }
428                 }
429
430                 [MWFCategory("Layout")]
431 #if NET_2_0
432                 [Browsable (false)]
433                 [EditorBrowsable (EditorBrowsableState.Never)]
434                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
435 #else
436                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
437                 [Localizable(true)]
438 #endif
439                 public DockPaddingEdges DockPadding {
440                         get {
441                                 return dock_padding;
442                         }
443                 }
444
445 #if NET_2_0
446                 [Browsable (false)]
447                 [EditorBrowsable (EditorBrowsableState.Always)]
448                 public HScrollProperties HorizontalScroll {
449                         get { return horizontalScroll; }
450                 }
451
452                 [Browsable (false)]
453                 [EditorBrowsable (EditorBrowsableState.Always)]
454                 public VScrollProperties VerticalScroll {
455                         get { return verticalScroll; }
456                 }
457 #endif
458                 #endregion      // Public Instance Properties
459
460                 #region Protected Instance Methods
461                 protected override CreateParams CreateParams {
462                         get {
463                                 return base.CreateParams;
464                         }
465                 }
466
467                 protected bool HScroll {
468                         get {
469                                 return hscrollbar.VisibleInternal;
470                         }
471
472                         set {
473                                 if (hscrollbar.VisibleInternal != value) {
474                                         force_hscroll_visible = value;
475                                         Recalculate (false);
476                                 }
477                         }
478                 }
479
480                 protected bool VScroll {
481                         get {
482                                 return vscrollbar.VisibleInternal;
483                         }
484
485                         set {
486                                 if (vscrollbar.VisibleInternal != value) {
487                                         force_vscroll_visible = value;
488                                         Recalculate (false);
489                                 }
490                         }
491                 }
492                 #endregion      // Protected Instance Methods
493
494                 #region Public Instance Methods
495                 public void ScrollControlIntoView(Control activeControl) {
496                         int     corner_x;
497                         int     corner_y;
498
499                         Rectangle within = new Rectangle ();
500                         within.Size = ClientSize;
501                         
502                         if (!AutoScroll || (!hscrollbar.VisibleInternal && !vscrollbar.VisibleInternal)) {
503                                 return;
504                         }
505
506                         if (!Contains(activeControl)) {
507                                 return;
508                         }
509
510                         if (vscrollbar.Visible) {
511                                 within.Width -= vscrollbar.Width;
512                         }
513                         if (hscrollbar.Visible) {
514                                 within.Height -= hscrollbar.Height;
515                         }
516
517                         // Don't scroll if already visible
518                         if (within.Contains (activeControl.Location) && within.Contains (activeControl.Right, activeControl.Bottom)) {
519                                 return;
520                         }
521
522                         // If the control is above the top or the left, move it down and right until it aligns 
523                         // with the top/left.
524                         // If the control is below the bottom or to the right, move it up/left until it aligns
525                         // with the bottom/right, but do never move it further than the top/left side.
526                         int x_diff = 0, y_diff = 0;
527                         if (activeControl.Top <= 0 || activeControl.Height >= within.Height) {
528                                 y_diff = -activeControl.Top;
529                         } else if (activeControl.Bottom > within.Height) {
530                                 y_diff = within.Height - activeControl.Bottom;
531                         }
532                         if (activeControl.Left <= 0 || activeControl.Width >= within.Width) {
533                                 x_diff = -activeControl.Left;
534                         } else if (activeControl.Right > within.Width) {
535                                 x_diff = within.Width - activeControl.Right;
536                         }
537                         corner_x = hscrollbar.Value - x_diff;
538                         corner_y = vscrollbar.Value - y_diff;
539
540                         if (hscrollbar.VisibleInternal) {
541                                 if (corner_x > hscrollbar.Maximum) {
542                                         corner_x = hscrollbar.Maximum;
543                                 } else if (corner_x < hscrollbar.Minimum) {
544                                         corner_x = hscrollbar.Minimum;
545                                 }
546                                 if (corner_x != hscrollbar.Value) {
547                                         hscrollbar.Value = corner_x;
548                                 }
549                         }
550
551                         if (vscrollbar.VisibleInternal) {
552                                 if (corner_y > vscrollbar.Maximum) {
553                                         corner_y = vscrollbar.Maximum;
554                                 } else if (corner_y < vscrollbar.Minimum) {
555                                         corner_y = vscrollbar.Minimum;
556                                 }
557                                 if (corner_y != vscrollbar.Value) {
558                                         vscrollbar.Value = corner_y;
559                                 }
560                         }
561                 }
562
563                 public void SetAutoScrollMargin(int x, int y) {
564                         if (x < 0) {
565                                 x = 0;
566                         }
567
568                         if (y < 0) {
569                                 y = 0;
570                         }
571
572                         auto_scroll_margin = new Size(x, y);
573                         Recalculate (false);
574                 }
575                 #endregion      // Public Instance Methods
576
577                 #region Protected Instance Methods
578                 [EditorBrowsable(EditorBrowsableState.Advanced)]
579                 protected virtual void AdjustFormScrollbars(bool displayScrollbars) {
580                         Recalculate (false);
581                 }
582
583                 [EditorBrowsable(EditorBrowsableState.Advanced)]
584                 protected bool GetScrollState(int bit) {
585                         // Internal MS
586                         return false;
587                 }
588
589                 [EditorBrowsable(EditorBrowsableState.Advanced)]
590                 protected override void OnLayout(LayoutEventArgs levent) {
591                         CalculateCanvasSize();
592
593                         AdjustFormScrollbars(AutoScroll);       // Dunno what the logic is. Passing AutoScroll seems to match MS behaviour
594                         base.OnLayout(levent);
595                 }
596
597                 [EditorBrowsable(EditorBrowsableState.Advanced)]
598                 protected override void OnMouseWheel(MouseEventArgs e) {
599                         if (vscrollbar.VisibleInternal) {
600                                 if (e.Delta > 0) {
601                                         if (vscrollbar.Minimum < (vscrollbar.Value - vscrollbar.LargeChange)) {
602                                                 vscrollbar.Value -= vscrollbar.LargeChange;
603                                         } else {
604                                                 vscrollbar.Value = vscrollbar.Minimum;
605                                         }
606                                 } else {
607                                         int maximum_scrollbar_value = vscrollbar.Maximum - vscrollbar.LargeChange + 1;
608                                         if (maximum_scrollbar_value > (vscrollbar.Value + vscrollbar.LargeChange)) {
609                                                 vscrollbar.Value += vscrollbar.LargeChange;
610                                         } else {
611                                                 vscrollbar.Value = maximum_scrollbar_value;
612                                         }
613                                 }
614                         }
615                         base.OnMouseWheel(e);
616                 }
617
618                 [EditorBrowsable(EditorBrowsableState.Advanced)]
619                 protected override void OnVisibleChanged(EventArgs e) {
620                         if (Visible) {
621                                 UpdateChildrenZOrder ();
622                                 PerformLayout(this, "Visible");
623                         }
624                         base.OnVisibleChanged(e);
625                 }
626
627 #if NET_2_0
628                 [EditorBrowsable (EditorBrowsableState.Never)]
629 #endif
630                 protected override void ScaleCore(float dx, float dy) {
631                         dock_padding.Scale(dx, dy);
632                         base.ScaleCore(dx, dy);
633                 }
634
635 #if NET_2_0
636                 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
637                 {
638                         base.ScaleControl (factor, specified);
639                 }
640                 
641                 protected virtual Point ScrollToControl (Control activeControl)
642                 {
643                         int corner_x;
644                         int corner_y;
645
646                         Rectangle within = new Rectangle ();
647                         within.Size = ClientSize;
648
649                         if (vscrollbar.Visible)
650                                 within.Width -= vscrollbar.Width;
651
652                         if (hscrollbar.Visible)
653                                 within.Height -= hscrollbar.Height;
654
655                         // If the control is above the top or the left, move it down and right until it aligns 
656                         // with the top/left.
657                         // If the control is below the bottom or to the right, move it up/left until it aligns
658                         // with the bottom/right, but do never move it further than the top/left side.
659                         int x_diff = 0, y_diff = 0;
660                         
661                         if (activeControl.Top <= 0 || activeControl.Height >= within.Height)
662                                 y_diff = -activeControl.Top;
663                         else if (activeControl.Bottom > within.Height)
664                                 y_diff = within.Height - activeControl.Bottom;
665
666                         if (activeControl.Left <= 0 || activeControl.Width >= within.Width)
667                                 x_diff = -activeControl.Left;
668                         else if (activeControl.Right > within.Width)
669                                 x_diff = within.Width - activeControl.Right;
670
671                         corner_x = AutoScrollPosition.X + x_diff;
672                         corner_y = AutoScrollPosition.Y + y_diff;
673                         
674                         return new Point (corner_x, corner_y);
675                 }
676 #endif
677
678                 protected void SetDisplayRectLocation(int x, int y) {
679                         // This method is weird. MS documents that the scrollbars are not
680                         // updated. We need to move stuff, but leave the scrollbars as is
681
682                         if (x > 0) {
683                                 x = 0;
684                         }
685
686                         if (y > 0) {
687                                 y = 0;
688                         }
689
690                         ScrollWindow(scroll_position.X - x , scroll_position.Y - y);
691                 }
692
693                 protected void SetScrollState(int bit, bool value) {
694                         //throw new NotImplementedException();
695                 }
696
697                 [EditorBrowsable(EditorBrowsableState.Advanced)]
698                 protected override void WndProc(ref Message m) {
699                         base.WndProc(ref m);
700                 }
701                 #endregion      // Protected Instance Methods
702
703                 #region Internal & Private Methods
704                 internal override IntPtr AfterTopMostControl ()
705                 {
706                         // order of scrollbars:
707                         // top = vertical
708                         //       sizegrid
709                         // bottom = horizontal
710                         if (hscrollbar != null && hscrollbar.Visible)
711                                 return hscrollbar.Handle;
712                         // no need to check for sizegrip since it will only
713                         // be visible if hbar is visible.
714                         if (vscrollbar != null && vscrollbar.Visible)
715                                 return hscrollbar.Handle;
716
717                         return base.AfterTopMostControl ();
718                 }
719
720                 private void CalculateCanvasSize() {
721                         Control         child;
722                         int             num_of_children;
723                         int             width;
724                         int             height;
725                         int             extra_width;
726                         int             extra_height;
727
728                         num_of_children = Controls.Count;
729                         width = 0;
730                         height = 0;
731                         extra_width = dock_padding.Right + hscrollbar.Value;
732                         extra_height = dock_padding.Bottom + vscrollbar.Value;
733
734                         for (int i = 0; i < num_of_children; i++) {
735                                 child = Controls[i];
736                                 if (child.Dock == DockStyle.Right) {
737                                         extra_width += child.Width;
738                                 } else if (child.Dock == DockStyle.Bottom) {
739                                         extra_height += child.Height;
740                                 }
741                         }
742
743                         if (!auto_scroll_min_size.IsEmpty) {
744                                 width = auto_scroll_min_size.Width;
745                                 height = auto_scroll_min_size.Height;
746                         }
747
748                         for (int i = 0; i < num_of_children; i++) {
749                                 child = Controls[i];
750
751                                 switch(child.Dock) {
752                                         case DockStyle.Left: {
753                                                 if ((child.Right + extra_width) > width) {
754                                                         width = child.Right + extra_width;
755                                                 }
756                                                 continue;
757                                         }
758
759                                         case DockStyle.Top: {
760                                                 if ((child.Bottom + extra_height) > height) {
761                                                         height = child.Bottom + extra_height;
762                                                 }
763                                                 continue;
764                                         }
765
766                                         case DockStyle.Fill:
767                                         case DockStyle.Right:
768                                         case DockStyle.Bottom: {
769                                                 continue;
770                                         }
771
772                                         default: {
773                                                 AnchorStyles    anchor;
774
775                                                 anchor = child.Anchor;
776
777                                                 if (((anchor & AnchorStyles.Left) != 0) && ((anchor & AnchorStyles.Right) == 0)) {
778                                                         if ((child.Right + extra_width) > width) {
779                                                                 width = child.Right + extra_width;
780                                                         }
781                                                 }
782
783                                                 if (((anchor & AnchorStyles.Top) != 0) || ((anchor & AnchorStyles.Bottom) == 0)) {
784                                                         if ((child.Bottom + extra_height) > height) {
785                                                                 height = child.Bottom + extra_height;
786                                                         }
787                                                 }
788                                                 continue;
789                                         }
790                                 }
791                         }
792
793                         canvas_size.Width = width;
794                         canvas_size.Height = height;
795                 }
796
797                 private void Recalculate (object sender, EventArgs e) {
798                         Recalculate (true);
799                 }
800                                 
801                 private void Recalculate (bool doLayout) {
802                         if (!IsHandleCreated) {
803                                 return;
804                         }
805
806                         Size canvas = canvas_size;
807                         Size client = ClientSize;
808
809                         canvas.Width += auto_scroll_margin.Width;
810                         canvas.Height += auto_scroll_margin.Height;
811
812                         int right_edge = client.Width;
813                         int bottom_edge = client.Height;
814                         int prev_right_edge;
815                         int prev_bottom_edge;
816
817                         bool hscroll_visible;
818                         bool vscroll_visible;
819
820                         do {
821                                 prev_right_edge = right_edge;
822                                 prev_bottom_edge = bottom_edge;
823
824                                 if ((force_hscroll_visible || (canvas.Width > right_edge && auto_scroll)) && client.Width > 0) {
825                                         hscroll_visible = true;
826                                         bottom_edge = client.Height - SystemInformation.HorizontalScrollBarHeight;
827                                 } else {
828                                         hscroll_visible = false;
829                                         bottom_edge = client.Height;
830                                 }
831
832                                 if ((force_vscroll_visible || (canvas.Height > bottom_edge && auto_scroll)) && client.Height > 0) {
833                                         vscroll_visible = true;
834                                         right_edge = client.Width - SystemInformation.VerticalScrollBarWidth;
835                                 } else {
836                                         vscroll_visible = false;
837                                         right_edge = client.Width;
838                                 }
839
840                         } while (right_edge != prev_right_edge || bottom_edge != prev_bottom_edge);
841
842                         if (right_edge < 0) right_edge = 0;
843                         if (bottom_edge < 0) bottom_edge = 0;
844
845                         Rectangle hscroll_bounds;
846                         Rectangle vscroll_bounds;
847
848                         hscroll_bounds = new Rectangle (0, client.Height - SystemInformation.HorizontalScrollBarHeight,
849                                                         ClientRectangle.Width, SystemInformation.HorizontalScrollBarHeight);
850                         vscroll_bounds = new Rectangle (client.Width - SystemInformation.VerticalScrollBarWidth, 0,
851                                                         SystemInformation.VerticalScrollBarWidth, ClientRectangle.Height);
852
853                         /* the ScrollWindow calls here are needed
854                          * because (this explanation sucks):
855                          * 
856                          * when we transition from having a scrollbar to
857                          * not having one, we won't receive a scrollbar
858                          * moved (value changed) event, so we need to
859                          * manually scroll the canvas.
860                          * 
861                          * if you can fix this without requiring the
862                          * ScrollWindow calls, pdb and toshok will each
863                          * pay you $5.
864                         */
865
866                         if (!vscrollbar.Visible) {
867                                 vscrollbar.Value = 0;
868                         }
869                         if (!hscrollbar.Visible) {
870                                 hscrollbar.Value = 0;
871                         }
872
873                         if (hscroll_visible) {
874                                 hscrollbar.LargeChange = right_edge;
875                                 hscrollbar.SmallChange = 5;
876                                 hscrollbar.Maximum = canvas.Width - 1;
877                         } else {
878                                 if (hscrollbar != null && hscrollbar.VisibleInternal) {
879                                         ScrollWindow (- scroll_position.X, 0);
880                                 }
881                                 scroll_position.X = 0;
882                         }
883
884                         if (vscroll_visible) {
885                                 vscrollbar.LargeChange = bottom_edge;
886                                 vscrollbar.SmallChange = 5;
887                                 vscrollbar.Maximum = canvas.Height - 1;
888                         } else {
889                                 if (vscrollbar != null && vscrollbar.VisibleInternal) {
890                                         ScrollWindow (0, - scroll_position.Y);
891                                 }
892                                 scroll_position.Y = 0;
893                         }
894
895                         if (hscroll_visible && vscroll_visible) {
896                                 hscroll_bounds.Width -= SystemInformation.VerticalScrollBarWidth;
897                                 vscroll_bounds.Height -= SystemInformation.HorizontalScrollBarHeight;
898
899                                 sizegrip.Bounds = new Rectangle (hscroll_bounds.Right,
900                                                                  vscroll_bounds.Bottom,
901                                                                  SystemInformation.VerticalScrollBarWidth,
902                                                                  SystemInformation.HorizontalScrollBarHeight);
903                         }
904                         
905                         SuspendLayout ();
906
907                         hscrollbar.Bounds = hscroll_bounds;
908                         hscrollbar.Visible = hscroll_visible;
909                         if (hscrollbar.Visible)
910                                 XplatUI.SetZOrder (hscrollbar.Handle, IntPtr.Zero, true, false);
911
912                         vscrollbar.Bounds = vscroll_bounds;
913                         vscrollbar.Visible = vscroll_visible;
914                         if (vscrollbar.Visible)
915                                 XplatUI.SetZOrder (vscrollbar.Handle, IntPtr.Zero, true, false);
916
917                         UpdateSizeGripVisible ();
918
919                         ResumeLayout (doLayout);
920                         
921                         // We should now scroll the active control into view, 
922                         // the funny part is that ScrollableControl does not have 
923                         // the concept of active control.
924                         ContainerControl container = this as ContainerControl;
925                         if (container != null && container.ActiveControl != null) {
926                                 ScrollControlIntoView (container.ActiveControl);
927                         }
928                 }
929
930                 internal void UpdateSizeGripVisible ()
931                 {
932                         if (!IsHandleCreated) {
933                                 return;
934                         }
935
936                         sizegrip.CapturedControl = Parent;
937                         // This is really wierd, the size grip is only showing up 
938                         // if the bottom right corner of the scrollable control is within
939                         // two pixels from the bottom right corner of its parent.
940                         bool show_sizegrip = hscrollbar.VisibleInternal && vscrollbar.VisibleInternal;
941                         bool enable_sizegrip = false;
942                         if (show_sizegrip && Parent != null) {
943                                 Point diff = new Point (Parent.ClientRectangle.Bottom - Bottom, Parent.ClientRectangle.Right - Right);
944                                 enable_sizegrip = diff.X <= 2 && diff.X >= 0 && diff.Y <= 2 && diff.Y >= 0;
945                         }
946                         sizegrip.Visible = show_sizegrip;
947                         sizegrip.Enabled = enable_sizegrip || sizegrip.Capture;
948                         if (sizegrip.Visible)
949                                 XplatUI.SetZOrder (sizegrip.Handle, vscrollbar.Handle, false, false);
950                 }
951
952                 private void HandleScrollBar(object sender, EventArgs e) {
953                         if (sender == vscrollbar) {
954                                 if (!vscrollbar.Visible)
955                                         return;
956                                 ScrollWindow(0, vscrollbar.Value- scroll_position.Y);
957                         } else {
958                                 if (!hscrollbar.Visible)
959                                         return;
960                                 ScrollWindow(hscrollbar.Value - scroll_position.X, 0);
961                         }
962                 }
963
964                 private void AddScrollbars (object o, EventArgs e)
965                 {
966                         Controls.AddRangeImplicit (new Control[] {hscrollbar, vscrollbar, sizegrip});
967                         HandleCreated -= new EventHandler (AddScrollbars);
968                 }
969
970                 private void CreateScrollbars ()
971                 {
972                         hscrollbar = new ImplicitHScrollBar ();
973                         hscrollbar.Visible = false;
974                         hscrollbar.ValueChanged += new EventHandler (HandleScrollBar);
975                         hscrollbar.Height = SystemInformation.HorizontalScrollBarHeight;
976
977                         vscrollbar = new ImplicitVScrollBar ();
978                         vscrollbar.Visible = false;
979                         vscrollbar.ValueChanged += new EventHandler (HandleScrollBar);
980                         vscrollbar.Width = SystemInformation.VerticalScrollBarWidth;
981
982                         sizegrip = new SizeGrip (this);
983                         sizegrip.Visible = false;
984                 }
985
986                 private void ScrollWindow(int XOffset, int YOffset) {
987                         int     num_of_children;
988
989                         if (XOffset == 0 && YOffset == 0) {
990                                 return;
991                         }
992
993                         SuspendLayout();
994
995                         num_of_children = Controls.Count;
996
997                         for (int i = 0; i < num_of_children; i++) {
998                                 Controls[i].Location = new Point (Controls[i].Left - XOffset, Controls[i].Top - YOffset);
999                                 //Controls[i].Left -= XOffset;
1000                                 //Controls[i].Top -= YOffset;
1001                                 // Is this faster? Controls[i].Location -= new Size(XOffset, YOffset);
1002                         }
1003
1004                         scroll_position.X += XOffset;
1005                         scroll_position.Y += YOffset;
1006
1007                         // Should we call XplatUI.ScrollWindow??? If so, we need to position our windows by other means above
1008                         // Since we're already causing a redraw above
1009                         Invalidate(false);
1010                         ResumeLayout(false);
1011                 }
1012                 #endregion      // Internal & Private Methods
1013
1014 #if NET_2_0
1015                 static object OnScrollEvent = new object ();
1016                 
1017                 protected virtual void OnScroll (ScrollEventArgs se)
1018                 {
1019                         EventHandler eh = (EventHandler) (Events [OnScrollEvent]);
1020                         if (eh != null)
1021                                 eh (this, se);
1022                 }
1023
1024                 protected override void OnPaddingChanged (EventArgs e)
1025                 {
1026                         base.OnPaddingChanged (e);
1027                 }
1028                 
1029                 protected override void OnPaintBackground (PaintEventArgs e)
1030                 {
1031                         base.OnPaintBackground (e);
1032                 }
1033
1034                 [EditorBrowsable (EditorBrowsableState.Advanced)]
1035                 protected override void OnRightToLeftChanged (EventArgs e)
1036                 {
1037                         base.OnRightToLeftChanged (e);
1038                 }
1039
1040                 public event ScrollEventHandler Scroll {
1041                         add { Events.AddHandler (OnScrollEvent, value); }
1042                         remove { Events.RemoveHandler (OnScrollEvent, value); }
1043                 }
1044 #endif
1045         }
1046 }