2008-11-24 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ScrollableControl.cs
index b0e425a1a01530a12fdcf74d856063702115b800..f901ffffe132077a3ee13cb0859d6d5ca8fc78fa 100644 (file)
@@ -47,7 +47,7 @@ namespace System.Windows.Forms {
                private SizeGrip                sizegrip;
                internal ImplicitHScrollBar     hscrollbar;
                internal ImplicitVScrollBar     vscrollbar;
-               private Size                    canvas_size;
+               internal Size                   canvas_size;
                private Rectangle               display_rectangle;
                private Control                 old_parent;
 
@@ -59,14 +59,93 @@ namespace System.Windows.Forms {
 
                [TypeConverter(typeof(ScrollableControl.DockPaddingEdgesConverter))]
                #region Subclass DockPaddingEdges
-               public class DockPaddingEdges : ICloneable {
+               public class DockPaddingEdges : ICloneable
+               {
+                       private Control owner;
+                       
+#if NET_2_0
+                       internal DockPaddingEdges (Control owner)
+                       {
+                               this.owner = owner;
+                       }
+
+                       #region DockPaddingEdges Public Instance Properties
+                       [RefreshProperties (RefreshProperties.All)]
+                       public int All {
+                               get { return owner.Padding.All; }
+                               set { owner.Padding = new Padding (value); }
+                       }
+
+                       [RefreshProperties (RefreshProperties.All)]
+                       public int Bottom {
+                               get { return owner.Padding.Bottom; }
+                               set { owner.Padding = new Padding (Left, Top, Right, value); }
+                       }
+
+                       [RefreshProperties (RefreshProperties.All)]
+                       public int Left {
+                               get { return owner.Padding.Left; }
+                               set { owner.Padding = new Padding (value, Top, Right, Bottom); }
+                       }
+
+                       [RefreshProperties (RefreshProperties.All)]
+                       public int Right {
+                               get { return owner.Padding.Right; }
+                               set { owner.Padding = new Padding (Left, Top, value, Bottom); }
+                       }
+
+                       [RefreshProperties (RefreshProperties.All)]
+                       public int Top {
+                               get { return owner.Padding.Top; }
+                               set { owner.Padding = new Padding (Left, value, Right, Bottom); }
+                       }
+                       #endregion      // DockPaddingEdges Public Instance Properties
+
+                       // Public Instance Methods
+                       public override bool Equals (object other)
+                       {
+                               if (!(other is DockPaddingEdges)) {
+                                       return false;
+                               }
+
+                               if ((this.All == ((DockPaddingEdges)other).All) && (this.Left == ((DockPaddingEdges)other).Left) &&
+                                       (this.Right == ((DockPaddingEdges)other).Right) && (this.Top == ((DockPaddingEdges)other).Top) &&
+                                       (this.Bottom == ((DockPaddingEdges)other).Bottom)) {
+                                       return true;
+                               }
+
+                               return false;
+                       }
+
+                       public override int GetHashCode ()
+                       {
+                               return All * Top * Bottom * Right * Left;
+                       }
+
+                       public override string ToString ()
+                       {
+                               return "All = " + All.ToString () + " Top = " + Top.ToString () + " Left = " + Left.ToString () + " Bottom = " + Bottom.ToString () + " Right = " + Right.ToString ();
+                       }
+
+                       internal void Scale (float dx, float dy)
+                       {
+                               Left = (int)(Left * dx);
+                               Right = (int)(Right * dx);
+                               Top = (int)(Top * dy);
+                               Bottom = (int)(Bottom * dy);
+                       }
+
+                       object ICloneable.Clone ()
+                       {
+                               return new DockPaddingEdges (owner);
+                       }
+#else
                        #region DockPaddingEdges Local Variables
                        private int     all;
                        private int     left;
                        private int     right;
                        private int     top;
                        private int     bottom;
-                       private Control owner;
                        #endregion      // DockPaddingEdges Local Variables
 
                        #region DockPaddingEdges Constructor
@@ -198,6 +277,7 @@ namespace System.Windows.Forms {
 
                                return padding_edge;
                        }
+#endif
                }
                #endregion      // Subclass DockPaddingEdges
 
@@ -231,7 +311,7 @@ namespace System.Windows.Forms {
                        scroll_position = new Point(0, 0);
                        dock_padding = new DockPaddingEdges(this);
                        SizeChanged +=new EventHandler(Recalculate);
-                       VisibleChanged += new EventHandler(Recalculate);
+                       VisibleChanged += new EventHandler (VisibleChangedHandler);
                        LocationChanged += new EventHandler (LocationChangedHandler);
                        ParentChanged += new EventHandler (ParentChangedHandler);
                        HandleCreated += new EventHandler (AddScrollbars);
@@ -244,6 +324,11 @@ namespace System.Windows.Forms {
 #endif
                }
 
+               void VisibleChangedHandler (object sender, EventArgs e)
+               {
+                       Recalculate (false);
+               }
+
                void LocationChangedHandler (object sender, EventArgs e)
                {
                        UpdateSizeGripVisible ();
@@ -301,11 +386,10 @@ namespace System.Windows.Forms {
                        }
 
                        set {
-                               if (auto_scroll == value) {
-                                       return;
+                               if (auto_scroll != value) {
+                                       auto_scroll = value;
+                                       PerformLayout (this, "AutoScroll");
                                }
-
-                               auto_scroll = value;
                        }
                }
 
@@ -329,6 +413,11 @@ namespace System.Windows.Forms {
                        }
                }
 
+               internal bool ShouldSerializeAutoScrollMargin ()
+               {
+                       return this.AutoScrollMargin != new Size (0, 0);
+               }
+
                [Localizable(true)]
                [MWFCategory("Layout")]
                public Size AutoScrollMinSize {
@@ -340,40 +429,54 @@ namespace System.Windows.Forms {
                                if (value != auto_scroll_min_size) {
                                        auto_scroll_min_size = value;
                                        AutoScroll = true;
+                                       PerformLayout (this, "AutoScrollMinSize");
                                }
                        }
                }
 
+               internal bool ShouldSerializeAutoScrollMinSize ()
+               {
+                       return this.AutoScrollMinSize != new Size (0, 0);
+               }
+
                [Browsable(false)]
                [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
                public Point AutoScrollPosition {
                        get {
-                               return new Point(-scroll_position.X, -scroll_position.Y);
+                               return DisplayRectangle.Location;
                        }
 
                        set {
-                               if ((value.X != scroll_position.X) || (value.Y != scroll_position.Y)) {
+                               if (value != AutoScrollPosition) {
                                        int     shift_x;
                                        int     shift_y;
 
                                        shift_x = 0;
                                        shift_y = 0;
                                        if (hscrollbar.VisibleInternal) {
+                                               int max = hscrollbar.Maximum - hscrollbar.LargeChange + 1;
+                                               value.X = value.X < hscrollbar.Minimum ? hscrollbar.Minimum : value.X;
+                                               value.X = value.X > max ? max : value.X;
                                                shift_x = value.X - scroll_position.X;
                                        }
 
                                        if (vscrollbar.VisibleInternal) {
+                                               int max = vscrollbar.Maximum - vscrollbar.LargeChange + 1;
+                                               value.Y = value.Y < vscrollbar.Minimum ? vscrollbar.Minimum : value.Y;
+                                               value.Y = value.Y > max ? max : value.Y;
                                                shift_y = value.Y - scroll_position.Y;
                                        }
 
                                        ScrollWindow(shift_x, shift_y);
 
                                        if (hscrollbar.VisibleInternal) {
-                                               hscrollbar.Value = scroll_position.X;
+                                               if (scroll_position.X >= hscrollbar.Minimum && scroll_position.X <= hscrollbar.Maximum)
+                                                       hscrollbar.Value = scroll_position.X;
                                        }
 
                                        if (vscrollbar.VisibleInternal) {
-                                               vscrollbar.Value = scroll_position.Y;
+                                               if (scroll_position.Y >= vscrollbar.Minimum && scroll_position.Y <= vscrollbar.Maximum)
+                                                       vscrollbar.Value = scroll_position.Y;
                                        }
 
                                }
@@ -467,7 +570,7 @@ namespace System.Windows.Forms {
                        set {
                                if (hscrollbar.VisibleInternal != value) {
                                        force_hscroll_visible = value;
-                                       Recalculate(this, EventArgs.Empty);
+                                       Recalculate (false);
                                }
                        }
                }
@@ -480,7 +583,7 @@ namespace System.Windows.Forms {
                        set {
                                if (vscrollbar.VisibleInternal != value) {
                                        force_vscroll_visible = value;
-                                       Recalculate(this, EventArgs.Empty);
+                                       Recalculate (false);
                                }
                        }
                }
@@ -565,14 +668,14 @@ namespace System.Windows.Forms {
                        }
 
                        auto_scroll_margin = new Size(x, y);
-                       Recalculate(this, EventArgs.Empty);
+                       Recalculate (false);
                }
                #endregion      // Public Instance Methods
 
                #region Protected Instance Methods
                [EditorBrowsable(EditorBrowsableState.Advanced)]
                protected virtual void AdjustFormScrollbars(bool displayScrollbars) {
-                       Recalculate(this, EventArgs.Empty);
+                       Recalculate (false);
                }
 
                [EditorBrowsable(EditorBrowsableState.Advanced)]
@@ -583,10 +686,20 @@ namespace System.Windows.Forms {
 
                [EditorBrowsable(EditorBrowsableState.Advanced)]
                protected override void OnLayout(LayoutEventArgs levent) {
-                       CalculateCanvasSize();
+                       CalculateCanvasSize (true);
 
                        AdjustFormScrollbars(AutoScroll);       // Dunno what the logic is. Passing AutoScroll seems to match MS behaviour
                        base.OnLayout(levent);
+
+#if NET_2_0
+                       // The first time through, we just set the canvas to clientsize
+                       // so we could re-layout everything according to the flow.
+                       // This time we want to actually calculate the canvas.
+                       if (this is FlowLayoutPanel) {
+                               CalculateCanvasSize (false);
+                               AdjustFormScrollbars (AutoScroll);
+                       }
+#endif
                }
 
                [EditorBrowsable(EditorBrowsableState.Advanced)]
@@ -627,6 +740,49 @@ namespace System.Windows.Forms {
                        base.ScaleCore(dx, dy);
                }
 
+#if NET_2_0
+               protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
+               {
+                       base.ScaleControl (factor, specified);
+               }
+               
+               protected virtual Point ScrollToControl (Control activeControl)
+               {
+                       int corner_x;
+                       int corner_y;
+
+                       Rectangle within = new Rectangle ();
+                       within.Size = ClientSize;
+
+                       if (vscrollbar.Visible)
+                               within.Width -= vscrollbar.Width;
+
+                       if (hscrollbar.Visible)
+                               within.Height -= hscrollbar.Height;
+
+                       // If the control is above the top or the left, move it down and right until it aligns 
+                       // with the top/left.
+                       // If the control is below the bottom or to the right, move it up/left until it aligns
+                       // with the bottom/right, but do never move it further than the top/left side.
+                       int x_diff = 0, y_diff = 0;
+                       
+                       if (activeControl.Top <= 0 || activeControl.Height >= within.Height)
+                               y_diff = -activeControl.Top;
+                       else if (activeControl.Bottom > within.Height)
+                               y_diff = within.Height - activeControl.Bottom;
+
+                       if (activeControl.Left <= 0 || activeControl.Width >= within.Width)
+                               x_diff = -activeControl.Left;
+                       else if (activeControl.Right > within.Width)
+                               x_diff = within.Width - activeControl.Right;
+
+                       corner_x = AutoScrollPosition.X + x_diff;
+                       corner_y = AutoScrollPosition.Y + y_diff;
+                       
+                       return new Point (corner_x, corner_y);
+               }
+#endif
+
                protected void SetDisplayRectLocation(int x, int y) {
                        // This method is weird. MS documents that the scrollbars are not
                        // updated. We need to move stuff, but leave the scrollbars as is
@@ -669,7 +825,7 @@ namespace System.Windows.Forms {
                        return base.AfterTopMostControl ();
                }
 
-               private void CalculateCanvasSize() {
+               internal virtual void CalculateCanvasSize (bool canOverride) {
                        Control         child;
                        int             num_of_children;
                        int             width;
@@ -747,6 +903,10 @@ namespace System.Windows.Forms {
                }
 
                private void Recalculate (object sender, EventArgs e) {
+                       Recalculate (true);
+               }
+                               
+               private void Recalculate (bool doLayout) {
                        if (!IsHandleCreated) {
                                return;
                        }
@@ -818,8 +978,10 @@ namespace System.Windows.Forms {
                                hscrollbar.Value = 0;
                        }
 
+                       /* Manually setting the size of the thumb should be done before
+                        * the other assignments */
                        if (hscroll_visible) {
-                               hscrollbar.LargeChange = right_edge;
+                               hscrollbar.SetManualLargeChange (right_edge);
                                hscrollbar.SmallChange = 5;
                                hscrollbar.Maximum = canvas.Width - 1;
                        } else {
@@ -830,7 +992,7 @@ namespace System.Windows.Forms {
                        }
 
                        if (vscroll_visible) {
-                               vscrollbar.LargeChange = bottom_edge;
+                               vscrollbar.SetManualLargeChange (bottom_edge);
                                vscrollbar.SmallChange = 5;
                                vscrollbar.Maximum = canvas.Height - 1;
                        } else {
@@ -852,19 +1014,19 @@ namespace System.Windows.Forms {
                        
                        SuspendLayout ();
 
-                       hscrollbar.Bounds = hscroll_bounds;
+                       hscrollbar.SetBoundsInternal (hscroll_bounds.X, hscroll_bounds.Y, hscroll_bounds.Width, hscroll_bounds.Height, BoundsSpecified.None);
                        hscrollbar.Visible = hscroll_visible;
                        if (hscrollbar.Visible)
                                XplatUI.SetZOrder (hscrollbar.Handle, IntPtr.Zero, true, false);
 
-                       vscrollbar.Bounds = vscroll_bounds;
+                       vscrollbar.SetBoundsInternal (vscroll_bounds.X, vscroll_bounds.Y, vscroll_bounds.Width, vscroll_bounds.Height, BoundsSpecified.None);
                        vscrollbar.Visible = vscroll_visible;
                        if (vscrollbar.Visible)
                                XplatUI.SetZOrder (vscrollbar.Handle, IntPtr.Zero, true, false);
 
                        UpdateSizeGripVisible ();
 
-                       ResumeLayout ();
+                       ResumeLayout (doLayout);
                        
                        // We should now scroll the active control into view, 
                        // the funny part is that ScrollableControl does not have 
@@ -909,6 +1071,13 @@ namespace System.Windows.Forms {
                        }
                }
 
+#if NET_2_0
+               private void HandleScrollEvent (object sender, ScrollEventArgs args)
+               {
+                       OnScroll (args);
+               }
+#endif
+
                private void AddScrollbars (object o, EventArgs e)
                {
                        Controls.AddRangeImplicit (new Control[] {hscrollbar, vscrollbar, sizegrip});
@@ -921,11 +1090,19 @@ namespace System.Windows.Forms {
                        hscrollbar.Visible = false;
                        hscrollbar.ValueChanged += new EventHandler (HandleScrollBar);
                        hscrollbar.Height = SystemInformation.HorizontalScrollBarHeight;
+                       hscrollbar.use_manual_thumb_size = true;
+#if NET_2_0
+                       hscrollbar.Scroll += new ScrollEventHandler (HandleScrollEvent);
+#endif
 
                        vscrollbar = new ImplicitVScrollBar ();
                        vscrollbar.Visible = false;
                        vscrollbar.ValueChanged += new EventHandler (HandleScrollBar);
                        vscrollbar.Width = SystemInformation.VerticalScrollBarWidth;
+                       vscrollbar.use_manual_thumb_size = true;
+#if NET_2_0
+                       vscrollbar.Scroll += new ScrollEventHandler (HandleScrollEvent);
+#endif
 
                        sizegrip = new SizeGrip (this);
                        sizegrip.Visible = false;
@@ -943,17 +1120,16 @@ namespace System.Windows.Forms {
                        num_of_children = Controls.Count;
 
                        for (int i = 0; i < num_of_children; i++) {
-                               Controls[i].Left -= XOffset;
-                               Controls[i].Top -= YOffset;
+                               Controls[i].Location = new Point (Controls[i].Left - XOffset, Controls[i].Top - YOffset);
+                               //Controls[i].Left -= XOffset;
+                               //Controls[i].Top -= YOffset;
                                // Is this faster? Controls[i].Location -= new Size(XOffset, YOffset);
                        }
 
                        scroll_position.X += XOffset;
                        scroll_position.Y += YOffset;
 
-                       // Should we call XplatUI.ScrollWindow??? If so, we need to position our windows by other means above
-                       // Since we're already causing a redraw above
-                       Invalidate(false);
+                       XplatUI.ScrollWindow (Handle, ClientRectangle, -XOffset, -YOffset, false);
                        ResumeLayout(false);
                }
                #endregion      // Internal & Private Methods
@@ -963,11 +1139,16 @@ namespace System.Windows.Forms {
                
                protected virtual void OnScroll (ScrollEventArgs se)
                {
-                       EventHandler eh = (EventHandler) (Events [OnScrollEvent]);
+                       ScrollEventHandler eh = (ScrollEventHandler) (Events [OnScrollEvent]);
                        if (eh != null)
                                eh (this, se);
                }
 
+               protected override void OnPaddingChanged (EventArgs e)
+               {
+                       base.OnPaddingChanged (e);
+               }
+               
                protected override void OnPaintBackground (PaintEventArgs e)
                {
                        base.OnPaintBackground (e);