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