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