2007-01-02 Miguel de Icaza <miguel@novell.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
27 // NOT COMPLETE
28
29 using System;
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
32 using System.Drawing;
33
34 namespace System.Windows.Forms {
35         [Designer ("System.Windows.Forms.Design.ScrollableControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
36         public class ScrollableControl : Control {
37                 #region Local Variables
38                 private bool                    hscroll_visible;
39                 private bool                    vscroll_visible;
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                 private ImplicitHScrollBar      hscrollbar;
49                 private ImplicitVScrollBar      vscrollbar;
50                 private Size                    canvas_size;
51                 private Rectangle               display_rectangle;
52                 #endregion      // Local Variables
53
54                 [TypeConverter(typeof(ScrollableControl.DockPaddingEdgesConverter))]
55                 #region Subclass DockPaddingEdges
56                 public class DockPaddingEdges : ICloneable {
57                         #region DockPaddingEdges Local Variables
58                         private int     all;
59                         private int     left;
60                         private int     right;
61                         private int     top;
62                         private int     bottom;
63                         private Control owner;
64                         #endregion      // DockPaddingEdges Local Variables
65
66                         #region DockPaddingEdges Constructor
67                         internal DockPaddingEdges(Control owner) {
68                                 all = 0;
69                                 left = 0;
70                                 right = 0;
71                                 top = 0;
72                                 bottom = 0;
73                                 this.owner = owner;
74                         }
75                         #endregion      // DockPaddingEdges Constructor
76
77                         #region DockPaddingEdges Public Instance Properties
78                         [RefreshProperties(RefreshProperties.All)]
79                         public int All {
80                                 get {
81                                         return all;
82                                 }
83
84                                 set {
85                                         all = value;
86                                         left = value;
87                                         right = value;
88                                         top = value;
89                                         bottom = value;
90
91                                         owner.PerformLayout();
92                                 }
93                         }
94
95                         [RefreshProperties(RefreshProperties.All)]
96                         public int Bottom {
97                                 get {
98                                         return bottom;
99                                 }
100
101                                 set {
102                                         bottom = value;
103                                         all = 0;
104
105                                         owner.PerformLayout();
106                                 }
107                         }
108
109                         [RefreshProperties(RefreshProperties.All)]
110                         public int Left {
111                                 get {
112                                         return left;
113                                 }
114
115                                 set {
116                                         left=value;
117                                         all = 0;
118
119                                         owner.PerformLayout();
120                                 }
121                         }
122
123                         [RefreshProperties(RefreshProperties.All)]
124                         public int Right {
125                                 get {
126                                         return right;
127                                 }
128
129                                 set {
130                                         right=value;
131                                         all = 0;
132
133                                         owner.PerformLayout();
134                                 }
135                         }
136
137                         [RefreshProperties(RefreshProperties.All)]
138                         public int Top {
139                                 get {
140                                         return top;
141                                 }
142
143                                 set {
144                                         top=value;
145                                         all = 0;
146
147                                         owner.PerformLayout();
148                                 }
149                         }
150                         #endregion      // DockPaddingEdges Public Instance Properties
151
152                         // Public Instance Methods
153                         public override bool Equals(object other) {
154                                 if (! (other is DockPaddingEdges)) {
155                                         return false;
156                                 }
157
158                                 if (    (this.all == ((DockPaddingEdges)other).all) && (this.left == ((DockPaddingEdges)other).left) &&
159                                         (this.right == ((DockPaddingEdges)other).right) && (this.top == ((DockPaddingEdges)other).top) && 
160                                         (this.bottom == ((DockPaddingEdges)other).bottom)) {
161                                         return true;
162                                 }
163
164                                 return false;
165                         }
166
167                         public override int GetHashCode() {
168                                 return all*top*bottom*right*left;
169                         }
170
171                         public override string ToString() {
172                                 return "All = "+all.ToString()+" Top = "+top.ToString()+" Left = "+left.ToString()+" Bottom = "+bottom.ToString()+" Right = "+right.ToString();
173                         }
174
175                         internal void Scale(float dx, float dy) {
176                                 left = (int) (left * dx);
177                                 right = (int) (right * dx);
178                                 top = (int) (top * dy);
179                                 bottom = (int) (bottom * dy);
180                         }
181
182                         object ICloneable.Clone() {
183                                 DockPaddingEdges padding_edge;
184
185                                 padding_edge=new DockPaddingEdges(owner);
186
187                                 padding_edge.all=all;
188                                 padding_edge.left=left;
189                                 padding_edge.right=right;
190                                 padding_edge.top=top;
191                                 padding_edge.bottom=bottom;
192
193                                 return padding_edge;
194                         }
195                 }
196                 #endregion      // Subclass DockPaddingEdges
197
198                 #region Subclass DockPaddingEdgesConverter
199                 public class DockPaddingEdgesConverter : System.ComponentModel.TypeConverter {
200                         // Public Constructors
201                         public DockPaddingEdgesConverter() {
202                         }
203
204                         // Public Instance Methods
205                         public override PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, Attribute[] attributes) {
206                                 return TypeDescriptor.GetProperties(typeof(DockPaddingEdges), attributes);
207                         }
208
209                         public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) {
210                                 return true;
211                         }
212                 }
213                 #endregion      // Subclass DockPaddingEdgesConverter
214
215                 #region Public Constructors
216                 public ScrollableControl() {
217                         SetStyle(ControlStyles.ContainerControl, true);
218                         SetStyle(ControlStyles.AllPaintingInWmPaint, false);
219                         auto_scroll = false;
220                         hscroll_visible = false;
221                         vscroll_visible = false;
222                         force_hscroll_visible = false;
223                         force_vscroll_visible = false;
224                         auto_scroll_margin = new Size(0, 0);
225                         auto_scroll_min_size = new Size(0, 0);
226                         scroll_position = new Point(0, 0);
227                         dock_padding = new DockPaddingEdges(this);
228                         SizeChanged +=new EventHandler(Recalculate);
229                         VisibleChanged += new EventHandler(Recalculate);
230                 }
231                 #endregion      // Public Constructors
232
233                 #region Protected Static Fields
234                 protected const int ScrollStateAutoScrolling    = 1;
235                 protected const int ScrollStateFullDrag         = 16;
236                 protected const int ScrollStateHScrollVisible   = 2;
237                 protected const int ScrollStateUserHasScrolled  = 8;
238                 protected const int ScrollStateVScrollVisible   = 4;
239                 #endregion      // Protected Static Fields
240
241                 #region Public Instance Properties
242                 [DefaultValue(false)]
243                 [Localizable(true)]
244                 [MWFCategory("Layout")]
245                 public virtual bool AutoScroll {
246                         get {
247                                 return  auto_scroll;
248                         }
249
250                         set {
251                                 if (auto_scroll == value) {
252                                         return;
253                                 }
254
255                                 auto_scroll = value;
256                                 if (!auto_scroll) {
257                                         SuspendLayout ();
258
259                                         Controls.RemoveImplicit (hscrollbar);
260                                         hscrollbar.Dispose();
261                                         hscrollbar = null;
262                                         hscroll_visible = false;
263
264                                         Controls.RemoveImplicit (vscrollbar);
265                                         vscrollbar.Dispose();
266                                         vscrollbar = null;
267                                         vscroll_visible = false;
268
269                                         Controls.RemoveImplicit (sizegrip);
270                                         sizegrip.Dispose();
271                                         sizegrip = null;
272
273                                         ResumeLayout ();
274                                 } else {
275                                         SuspendLayout ();
276
277                                         hscrollbar = new ImplicitHScrollBar();
278                                         hscrollbar.Visible = false;
279                                         hscrollbar.ValueChanged += new EventHandler(HandleScrollBar);
280                                         hscrollbar.Height = SystemInformation.HorizontalScrollBarHeight;
281                                         this.Controls.AddImplicit (hscrollbar);
282
283                                         vscrollbar = new ImplicitVScrollBar();
284                                         vscrollbar.Visible = false;
285                                         vscrollbar.ValueChanged += new EventHandler(HandleScrollBar);
286                                         vscrollbar.Width = SystemInformation.VerticalScrollBarWidth;
287                                         this.Controls.AddImplicit (vscrollbar);
288
289                                         sizegrip = new SizeGrip();
290                                         sizegrip.Visible = false;
291                                         this.Controls.AddImplicit (sizegrip);
292
293                                         ResumeLayout ();
294                                 }
295                         }
296                 }
297
298                 [Localizable(true)]
299                 [MWFCategory("Layout")]
300                 public Size AutoScrollMargin {
301                         get {
302                                 return auto_scroll_margin;
303                         }
304
305                         set {
306                                 if (value.Width < 0) {
307                                         throw new ArgumentException("Width is assigned less than 0", "value.Width");
308                                 }
309
310                                 if (value.Height < 0) {
311                                         throw new ArgumentException("Height is assigned less than 0", "value.Height");
312                                 }
313
314                                 auto_scroll_margin = value;
315                         }
316                 }
317
318                 [Localizable(true)]
319                 [MWFCategory("Layout")]
320                 public Size AutoScrollMinSize {
321                         get {
322                                 return auto_scroll_min_size;
323                         }
324
325                         set {
326                                 if (value != auto_scroll_min_size) {
327                                         auto_scroll_min_size = value;
328                                         AutoScroll = true;
329                                 }
330                         }
331                 }
332
333                 [Browsable(false)]
334                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
335                 public Point AutoScrollPosition {
336                         get {
337                                 return new Point(-scroll_position.X, -scroll_position.Y);
338                         }
339
340                         set {
341                                 if ((value.X != scroll_position.X) || (value.Y != scroll_position.Y)) {
342                                         int     shift_x;
343                                         int     shift_y;
344
345                                         shift_x = 0;
346                                         shift_y = 0;
347                                         if (hscroll_visible) {
348                                                 shift_x = value.X - scroll_position.X;
349                                         }
350
351                                         if (vscroll_visible) {
352                                                 shift_y = value.Y - scroll_position.Y;
353                                         }
354
355                                         ScrollWindow(shift_x, shift_y);
356
357                                         if (hscroll_visible) {
358                                                 hscrollbar.Value = scroll_position.X;
359                                         }
360
361                                         if (vscroll_visible) {
362                                                 vscrollbar.Value = scroll_position.Y;
363                                         }
364
365                                 }
366                         }
367                 }
368
369                 public override Rectangle DisplayRectangle {
370                         get {
371                                 if (auto_scroll) {
372                                         int             width;
373                                         int             height;
374
375                                         if (canvas_size.Width <= base.DisplayRectangle.Width) {
376                                                 width = base.DisplayRectangle.Width;
377                                                 if (vscroll_visible) {
378                                                         width -= vscrollbar.Width;
379                                                 }
380                                         } else {
381                                                 width = canvas_size.Width;
382                                         }
383
384                                         if (canvas_size.Height <= base.DisplayRectangle.Height) {
385                                                 height = base.DisplayRectangle.Height;
386                                                 if (hscroll_visible) {
387                                                         height -= hscrollbar.Height;
388                                                 }
389                                         } else {
390                                                 height = canvas_size.Height;
391                                         }
392
393                                         display_rectangle.X = -scroll_position.X;
394                                         display_rectangle.Y = -scroll_position.Y;
395                                         display_rectangle.Width = Math.Max(auto_scroll_min_size.Width, width);
396                                         display_rectangle.Height = Math.Max(auto_scroll_min_size.Height, height);
397                                 }
398                                 else {
399                                         display_rectangle = base.DisplayRectangle;
400                                 }
401
402                                 display_rectangle.X += dock_padding.Left;
403                                 display_rectangle.Y += dock_padding.Top;
404                                 display_rectangle.Width -= dock_padding.Left + dock_padding.Right;
405                                 display_rectangle.Height -= dock_padding.Top + dock_padding.Bottom;
406
407                                 return display_rectangle;
408                         }
409                 }
410
411                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
412                 [Localizable(true)]
413                 [MWFCategory("Layout")]
414                 public DockPaddingEdges DockPadding {
415                         get {
416                                 return dock_padding;
417                         }
418                 }
419                 #endregion      // Public Instance Properties
420
421                 #region Protected Instance Methods
422                 protected override CreateParams CreateParams {
423                         get {
424                                 return base.CreateParams;
425                         }
426                 }
427
428                 protected bool HScroll {
429                         get {
430                                 return hscroll_visible;
431                         }
432
433                         set {
434                                 if (hscroll_visible != value) {
435                                         force_hscroll_visible = value;
436                                         Recalculate(this, EventArgs.Empty);
437                                 }
438                         }
439                 }
440
441                 protected bool VScroll {
442                         get {
443                                 return vscroll_visible;
444                         }
445
446                         set {
447                                 if (vscroll_visible != value) {
448                                         force_vscroll_visible = value;
449                                         Recalculate(this, EventArgs.Empty);
450                                 }
451                         }
452                 }
453                 #endregion      // Protected Instance Methods
454
455                 #region Public Instance Methods
456                 public void ScrollControlIntoView(Control activeControl) {
457                         int     x;
458                         int     y;
459                         int     corner_x;
460                         int     corner_y;
461
462                         if (!AutoScroll || (!hscroll_visible && !vscroll_visible)) {
463                                 return;
464                         }
465
466                         if (!Contains(activeControl)) {
467                                 return;
468                         }
469
470                         x = activeControl.Left;
471                         y = activeControl.Top;
472
473                         // Translate into coords relative to us
474                         if (activeControl.Parent != this) {
475                                 activeControl.PointToScreen(ref x, ref y);
476                                 PointToClient(ref x, ref y);
477                         }
478
479                         x += scroll_position.X;
480                         y += scroll_position.Y;
481
482                         // Don't scroll if already visible
483                         if ((activeControl.Left >= scroll_position.X) && (activeControl.Left < (scroll_position.X + ClientSize.Width)) &&
484                             (activeControl.Top >= scroll_position.Y) && (activeControl.Top < (scroll_position.Y + ClientSize.Height))) {
485                                 return;
486                         }
487
488                         // try to center
489                         corner_x = Math.Max(0, x + activeControl.Width / 2 - ClientSize.Width / 2);
490                         corner_y = Math.Max(0, y + activeControl.Height / 2 - ClientSize.Height / 2);
491
492                         if (hscroll_visible && (corner_x > hscrollbar.Maximum)) {
493                                 corner_x = Math.Max(0, hscrollbar.Maximum - ClientSize.Width);
494                         }
495
496                         if (vscroll_visible && (corner_y > vscrollbar.Maximum)) {
497                                 corner_y = Math.Max(0, vscrollbar.Maximum - ClientSize.Height);
498                         }
499                         if ((corner_x == scroll_position.X) && (corner_y == scroll_position.Y)) {
500                                 return;
501                         }
502
503                         //this.SetDisplayRectLocation(-corner_x, -corner_y);
504                         hscrollbar.Value = corner_x;
505                         vscrollbar.Value = corner_y;
506                 }
507
508                 public void SetAutoScrollMargin(int x, int y) {
509                         if (x < 0) {
510                                 x = 0;
511                         }
512
513                         if (y < 0) {
514                                 y = 0;
515                         }
516
517                         auto_scroll_margin = new Size(x, y);
518                         Recalculate(this, EventArgs.Empty);
519                 }
520                 #endregion      // Public Instance Methods
521
522                 #region Protected Instance Methods
523                 [EditorBrowsable(EditorBrowsableState.Advanced)]
524                 protected virtual void AdjustFormScrollbars(bool displayScrollbars) {
525                         Recalculate(this, EventArgs.Empty);
526                 }
527
528                 [EditorBrowsable(EditorBrowsableState.Advanced)]
529                 protected bool GetScrollState(int bit) {
530                         // Internal MS
531                         return false;
532                 }
533
534                 [EditorBrowsable(EditorBrowsableState.Advanced)]
535                 protected override void OnLayout(LayoutEventArgs levent) {
536                         CalculateCanvasSize();
537
538                         AdjustFormScrollbars(AutoScroll);       // Dunno what the logic is. Passing AutoScroll seems to match MS behaviour
539                         base.OnLayout(levent);
540                 }
541
542                 [EditorBrowsable(EditorBrowsableState.Advanced)]
543                 protected override void OnMouseWheel(MouseEventArgs e) {
544                         if (vscroll_visible) {
545                                 if (e.Delta > 0) {
546                                         if (vscrollbar.Minimum < (vscrollbar.Value - vscrollbar.LargeChange)) {
547                                                 vscrollbar.Value -= vscrollbar.LargeChange;
548                                         } else {
549                                                 vscrollbar.Value = vscrollbar.Minimum;
550                                         }
551                                 } else {
552                                         if (vscrollbar.Maximum > (vscrollbar.Value + vscrollbar.LargeChange)) {
553                                                 vscrollbar.Value += vscrollbar.LargeChange;
554                                         } else {
555                                                 vscrollbar.Value = vscrollbar.Maximum;
556                                         }
557                                 }
558                         }
559                         base.OnMouseWheel(e);
560                 }
561
562                 [EditorBrowsable(EditorBrowsableState.Advanced)]
563                 protected override void OnVisibleChanged(EventArgs e) {
564                         if (Visible) {
565                                 PerformLayout();
566                         }
567                         base.OnVisibleChanged(e);
568                 }
569
570                 protected override void ScaleCore(float dx, float dy) {
571                         dock_padding.Scale(dx, dy);
572                         base.ScaleCore(dx, dy);
573                 }
574
575                 protected void SetDisplayRectLocation(int x, int y) {
576                         // This method is weird. MS documents that the scrollbars are not
577                         // updated. We need to move stuff, but leave the scrollbars as is
578
579                         if (x > 0) {
580                                 x = 0;
581                         }
582
583                         if (y > 0) {
584                                 y = 0;
585                         }
586
587                         ScrollWindow(scroll_position.X - x , scroll_position.Y - y);
588                 }
589
590                 protected void SetScrollState(int bit, bool value) {
591                         //throw new NotImplementedException();
592                 }
593
594                 [EditorBrowsable(EditorBrowsableState.Advanced)]
595                 protected override void WndProc(ref Message m) {
596                         base.WndProc(ref m);
597                 }
598                 #endregion      // Protected Instance Methods
599
600                 #region Internal & Private Methods
601                 private void CalculateCanvasSize() {
602                         Control         child;
603                         int             num_of_children;
604                         int             width;
605                         int             height;
606                         int             extra_width;
607                         int             extra_height;
608
609                         num_of_children = Controls.Count;
610                         width = 0;
611                         height = 0;
612                         extra_width = dock_padding.Right;
613                         extra_height = dock_padding.Bottom;
614
615                         for (int i = 0; i < num_of_children; i++) {
616                                 child = Controls[i];
617                                 if (child.Dock == DockStyle.Right) {
618                                         extra_width += child.Width;
619                                 } else if (child.Dock == DockStyle.Bottom) {
620                                         extra_height += child.Height;
621                                 }
622                         }
623
624                         if (!auto_scroll_min_size.IsEmpty) {
625                                 width = auto_scroll_min_size.Width;
626                                 height = auto_scroll_min_size.Height;
627                         }
628
629                         for (int i = 0; i < num_of_children; i++) {
630                                 child = Controls[i];
631
632                                 switch(child.Dock) {
633                                         case DockStyle.Left: {
634                                                 if ((child.Right + extra_width) > width) {
635                                                         width = child.Right + extra_width;
636                                                 }
637                                                 continue;
638                                         }
639
640                                         case DockStyle.Top: {
641                                                 if ((child.Bottom + extra_height) > height) {
642                                                         height = child.Bottom + extra_height;
643                                                 }
644                                                 continue;
645                                         }
646
647                                         case DockStyle.Fill:
648                                         case DockStyle.Right:
649                                         case DockStyle.Bottom: {
650                                                 continue;
651                                         }
652
653                                         default: {
654                                                 AnchorStyles    anchor;
655
656                                                 anchor = child.Anchor;
657
658                                                 if (((anchor & AnchorStyles.Left) != 0) && ((anchor & AnchorStyles.Right) == 0)) {
659                                                         if ((child.Right + extra_width) > width) {
660                                                                 width = child.Right + extra_width;
661                                                         }
662                                                 }
663
664                                                 if (((anchor & AnchorStyles.Top) != 0) || ((anchor & AnchorStyles.Bottom) == 0)) {
665                                                         if ((child.Bottom + extra_height) > height) {
666                                                                 height = child.Bottom + extra_height;
667                                                         }
668                                                 }
669                                                 continue;
670                                         }
671                                 }
672                         }
673                         width += scroll_position.X;
674                         height += scroll_position.Y;
675
676                         canvas_size.Width = width;
677                         canvas_size.Height = height;
678                 }
679
680                 private void Recalculate (object sender, EventArgs e) {
681                         if (!auto_scroll && !force_hscroll_visible && !force_vscroll_visible) {
682                                 return;
683                         }
684
685                         Size canvas = canvas_size;
686                         Size client = ClientSize;
687
688                         canvas.Width += auto_scroll_margin.Width;
689                         canvas.Height += auto_scroll_margin.Height;
690
691                         int right_edge = client.Width;
692                         int bottom_edge = client.Height;
693                         int prev_right_edge;
694                         int prev_bottom_edge;
695
696                         do {
697                                 prev_right_edge = right_edge;
698                                 prev_bottom_edge = bottom_edge;
699
700                                 if ((force_hscroll_visible || canvas.Width > right_edge) && client.Width > 0) {
701                                         hscroll_visible = true;
702                                         bottom_edge = client.Height - SystemInformation.HorizontalScrollBarHeight;
703                                 } else {
704                                         hscroll_visible = false;
705                                         bottom_edge = client.Height;
706                                 }
707
708                                 if ((force_vscroll_visible || canvas.Height > bottom_edge) && client.Height > 0) {
709                                         vscroll_visible = true;
710                                         right_edge = client.Width - SystemInformation.VerticalScrollBarWidth;
711                                 } else {
712                                         vscroll_visible = false;
713                                         right_edge = client.Width;
714                                 }
715
716                         } while (right_edge != prev_right_edge || bottom_edge != prev_bottom_edge);
717
718                         if (right_edge < 0) right_edge = 0;
719                         if (bottom_edge < 0) bottom_edge = 0;
720
721                         Rectangle hscroll_bounds;
722                         Rectangle vscroll_bounds;
723
724                         hscroll_bounds = new Rectangle (0, client.Height - SystemInformation.HorizontalScrollBarHeight,
725                                                         ClientRectangle.Width, SystemInformation.HorizontalScrollBarHeight);
726                         vscroll_bounds = new Rectangle (client.Width - SystemInformation.VerticalScrollBarWidth, 0,
727                                                         SystemInformation.VerticalScrollBarWidth, ClientRectangle.Height);
728
729                         /* the ScrollWindow calls here are needed
730                          * because (this explanation sucks):
731                          * 
732                          * when we transition from having a scrollbar to
733                          * not having one, we won't receive a scrollbar
734                          * moved (value changed) event, so we need to
735                          * manually scroll the canvas.
736                          * 
737                          * if you can fix this without requiring the
738                          * ScrollWindow calls, pdb and toshok will each
739                          * pay you $5.
740                         */
741                         if (hscroll_visible) {
742                                 hscrollbar.LargeChange = right_edge;
743                                 hscrollbar.SmallChange = 5;
744                                 hscrollbar.Maximum = canvas.Width - 1;
745                         } else {
746                                 if (hscrollbar.Visible) {
747                                         ScrollWindow (- scroll_position.X, 0);
748                                 }
749                                 scroll_position.X = 0;
750                         }
751
752                         if (vscroll_visible) {
753                                 vscrollbar.LargeChange = bottom_edge;
754                                 vscrollbar.SmallChange = 5;
755                                 vscrollbar.Maximum = canvas.Height - 1;
756                         } else {
757                                 if (vscrollbar.Visible) {
758                                         ScrollWindow (0, - scroll_position.Y);
759                                 }
760                                 scroll_position.Y = 0;
761                         }
762
763                         if (hscroll_visible && vscroll_visible) {
764                                 hscroll_bounds.Width -= SystemInformation.VerticalScrollBarWidth;
765                                 vscroll_bounds.Height -= SystemInformation.HorizontalScrollBarHeight;
766
767                                 sizegrip.Bounds = new Rectangle (hscroll_bounds.Right,
768                                                                  vscroll_bounds.Bottom,
769                                                                  SystemInformation.VerticalScrollBarWidth,
770                                                                  SystemInformation.HorizontalScrollBarHeight);
771                         }
772
773                         hscrollbar.Bounds = hscroll_bounds;
774                         vscrollbar.Bounds = vscroll_bounds;
775                         hscrollbar.Visible = hscroll_visible;
776                         vscrollbar.Visible = vscroll_visible;
777                         sizegrip.Visible = hscroll_visible && vscroll_visible;
778                 }
779
780                 private void HandleScrollBar(object sender, EventArgs e) {
781                         if (sender == vscrollbar) {
782                                 ScrollWindow(0, vscrollbar.Value- scroll_position.Y);
783                         } else {
784                                 ScrollWindow(hscrollbar.Value - scroll_position.X, 0);
785                         }
786                 }
787
788                 private void ScrollWindow(int XOffset, int YOffset) {
789                         int     num_of_children;
790
791                         if (XOffset == 0 && YOffset == 0) {
792                                 return;
793                         }
794
795                         SuspendLayout();
796
797                         num_of_children = Controls.Count;
798
799                         for (int i = 0; i < num_of_children; i++) {
800                                 Controls[i].Left -= XOffset;
801                                 Controls[i].Top -= YOffset;
802                                 // Is this faster? Controls[i].Location -= new Size(XOffset, YOffset);
803                         }
804
805                         scroll_position.X += XOffset;
806                         scroll_position.Y += YOffset;
807
808                         // Should we call XplatUI.ScrollWindow??? If so, we need to position our windows by other means above
809                         // Since we're already causing a redraw above
810                         Invalidate(false);
811                         ResumeLayout(false);
812                 }
813                 #endregion      // Internal & Private Methods
814
815 #if NET_2_0
816                 static object OnScrollEvent = new object ();
817                 
818                 protected virtual void OnScroll (ScrollEventArgs se)
819                 {
820                         EventHandler eh = (EventHandler) (Events [OnScrollEvent]);
821                         if (eh != null)
822                                 eh (this, se);
823                 }
824
825                 protected override void OnPaintBackground (PaintEventArgs e)
826                 {
827                         base.OnPaintBackground (e);
828                 }
829
830                 protected override void OnRightToLeftChanged (EventArgs e)
831                 {
832                         base.OnRightToLeftChanged (e);
833                 }
834
835                 public event ScrollEventHandler Scroll {
836                         add { Events.AddHandler (OnScrollEvent, value); }
837                         remove { Events.RemoveHandler (OnScrollEvent, value); }
838                 }
839 #endif
840         }
841 }