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