Merge pull request #440 from mono-soc-2012/garyb/resources
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
index d6893a3653a35f2b651f2b37a008f22cea0770c3..44c3ea16d060023be0cec41c6c78278ddfce3406 100644 (file)
@@ -32,21 +32,21 @@ using System.Drawing;
 using System.Runtime.InteropServices;
 
 namespace System.Windows.Forms {
-#if NET_2_0
        [ClassInterface (ClassInterfaceType.AutoDispatch)]
        [ComVisible (true)]
-#endif
        public class ContainerControl : ScrollableControl, IContainerControl {
                private Control         active_control;
                private Control         unvalidated_control;
+               private ArrayList       pending_validation_chain;
 
                // This is an internal hack that allows some container controls
                // to not auto select their child when they are activated
                internal bool           auto_select_child = true;
-#if NET_2_0
                private SizeF           auto_scale_dimensions;
                private AutoScaleMode   auto_scale_mode;
-#endif
+               private bool            auto_scale_mode_set;
+               private bool            auto_scale_pending;
+               private bool            is_auto_scaling;
 
                internal bool validation_failed; //track whether validation was cancelled by a validating control
 
@@ -55,10 +55,8 @@ namespace System.Windows.Forms {
                        active_control = null;
                        unvalidated_control = null;
                        ControlRemoved += new ControlEventHandler(OnControlRemoved);
-#if NET_2_0
                        auto_scale_dimensions = SizeF.Empty;
-                       auto_scale_mode = AutoScaleMode.None;
-#endif
+                       auto_scale_mode = AutoScaleMode.Inherit;
                }
                #endregion      // Public Constructors
 
@@ -119,7 +117,7 @@ namespace System.Windows.Forms {
                                active_control = value;
 
                                // Generate the leave messages
-                               while (walk != common_ancestor) {
+                               while (walk != common_ancestor && walk != null) {
                                        if (walk == value) {
                                                root = value;
                                                fire_enter = false;
@@ -136,18 +134,36 @@ namespace System.Windows.Forms {
                                        walk = walk.Parent;
                                }
 
-                               validation_failed = false;
-                               for (int i = 0; i < validation_chain.Count; i ++) {
-                                       if (!ValidateControl ((Control)validation_chain[i])) {
-                                               active_control = value = (Control)validation_chain[i];
-                                               fire_enter = true;
-                                               validation_failed = true;
+                               // Validation can be postponed due to all the controls
+                               // in the enter chain not causing validation. If we don't have any
+                               // enter chain, it means that the selected control is a child and then
+                               // we need to validate the controls anyway
+                               bool postpone_validation;
+                               Control topmost_under_root = null; // topmost under root, in the *enter* chain
+                               if (value == root)
+                                       postpone_validation = false;
+                               else {
+                                       postpone_validation = true;
+                                       walk = value;
+                                       while (walk != root && walk != null) {
+                                               if (walk.CausesValidation)
+                                                       postpone_validation = false;
+
+                                               topmost_under_root = walk;
+                                               walk = walk.Parent;
                                        }
                                }
 
+                               Control failed_validation_control = PerformValidation (form == null ? this : form, postpone_validation, 
+                                               validation_chain, topmost_under_root);
+                               if (failed_validation_control != null) {
+                                       active_control = value = failed_validation_control;
+                                       fire_enter = true;
+                               }
+
                                if (fire_enter) {
                                        walk = value;
-                                       while (walk != root) {
+                                       while (walk != root && walk != null) {
                                                chain.Add (walk);
                                                walk = walk.Parent;
                                        }
@@ -176,12 +192,89 @@ namespace System.Windows.Forms {
 
                                // Scroll control into view
                                ScrollControlIntoView(active_control);
-
+                               
+                               
+                               walk = this;
+                               ctl = this;
+                               while (walk != null) {
+                                       if (walk.Parent is ContainerControl) {
+                                               ctl = walk.Parent;
+                                       }
+                                       walk = walk.Parent;
+                               }
+                               
                                // Let the control know it's selected
-                               SendControlFocus (active_control);
+                               if (ctl.InternalContainsFocus)
+                                       SendControlFocus (active_control);
+                       }
+               }
+
+               // Return the control where validation failed, and null otherwise
+               // @topmost_under_root is the control under the root in the enter chain, if any
+               //
+               // The process of validation happens as described:
+               //
+               //      1. Iterate over the nodes in the enter chain (walk down), looking for any node
+               //      causing validation. If we can't find any, don't validate the current validation chain, but postpone it,
+               //      saving it in the top_container.pending_validation_chain field, since we need to keep track of it later.
+               //      If we have a previous pending_validation_chain, add the new nodes, making sure they are not repeated
+               //      (this is computed in ActiveControl and we receive if as the postpone_validation parameter).
+               //
+               //      2. If we found at least one node causing validation in the enter chain, try to validate the elements
+               //      in pending_validation_chain, if any. Then continue with the ones receives as parameters.
+               //
+               //      3. Return null if all the validation performed successfully, and return the control where the validation
+               //      failed otherwise.
+               //
+               private Control PerformValidation (ContainerControl top_container, bool postpone_validation, ArrayList validation_chain, 
+                               Control topmost_under_root)
+               {
+                       validation_failed = false;
+
+                       if (postpone_validation) {
+                               AddValidationChain (top_container, validation_chain);
+                               return null;
+                       }
+
+                       // if not null, pending chain has always one element or more
+                       if (top_container.pending_validation_chain != null) {
+                               // if the topmost node in the enter chain is exactly the topmost
+                               // int the validation chain, remove it, as .net does
+                               int last_idx = top_container.pending_validation_chain.Count - 1;
+                               if (topmost_under_root == top_container.pending_validation_chain [last_idx])
+                                       top_container.pending_validation_chain.RemoveAt (last_idx);
+
+                               AddValidationChain (top_container, validation_chain);
+                               validation_chain = top_container.pending_validation_chain;
+                               top_container.pending_validation_chain = null;
+                       }
+
+                       for (int i = 0; i < validation_chain.Count; i ++) {
+                               if (!ValidateControl ((Control)validation_chain[i])) {
+                                       validation_failed = true;
+                                       return (Control)validation_chain[i];
+                               }
                        }
+
+                       return null;
                }
 
+               // Add the elements in validation_chain to the pending validation chain stored in top_container
+               private void AddValidationChain (ContainerControl top_container, ArrayList validation_chain)
+               {
+                       if (validation_chain.Count == 0)
+                               return;
+
+                       if (top_container.pending_validation_chain == null || top_container.pending_validation_chain.Count == 0) {
+                               top_container.pending_validation_chain = validation_chain;
+                               return;
+                       }
+
+                       foreach (Control c in validation_chain)
+                               if (!top_container.pending_validation_chain.Contains (c))
+                                       top_container.pending_validation_chain.Add (c);
+               }       
+
                private bool ValidateControl (Control c)
                {
                        CancelEventArgs e = new CancelEventArgs ();
@@ -234,7 +327,6 @@ namespace System.Windows.Forms {
                        }
                }
 
-#if NET_2_0
                [Browsable (false)]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [EditorBrowsable (EditorBrowsableState.Advanced)]
@@ -248,17 +340,16 @@ namespace System.Windows.Forms {
                                if (auto_scale_dimensions != value) {
                                        auto_scale_dimensions = value;
                                        
-                                       if (AutoScaleFactor != new SizeF (1, 1))
-                                               PerformAutoScale ();
+                                       PerformAutoScale ();
                                }
                        }
                }
 
                protected SizeF AutoScaleFactor {
                        get {
-                               if (auto_scale_dimensions.IsEmpty) {
-                                       return new SizeF(1f, 1f);
-                               }
+                               if (auto_scale_dimensions.IsEmpty)
+                                       return new SizeF (1f, 1f);
+
                                return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
                                        CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
                        }
@@ -273,16 +364,21 @@ namespace System.Windows.Forms {
                                return auto_scale_mode;
                        }
                        set {
+                               if (this is Form)
+                                       (this as Form).AutoScale = false;
+
                                if (auto_scale_mode != value) {
                                        auto_scale_mode = value;
-                                       auto_scale_dimensions = CurrentAutoScaleDimensions;
+
+                                       if (auto_scale_mode_set)
+                                               auto_scale_dimensions = SizeF.Empty;
+
+                                       auto_scale_mode_set = true;
+
+                                       PerformAutoScale ();
                                }
-                                       
-                               if (this is Form)
-                                       (this as Form).AutoScale = false;
                        }
                }
-#endif // NET_2_0
 
                [Browsable (false)]
                public override BindingContext BindingContext {
@@ -298,7 +394,6 @@ namespace System.Windows.Forms {
                        }
                }
 
-#if NET_2_0
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                public SizeF CurrentAutoScaleDimensions {
@@ -309,7 +404,7 @@ namespace System.Windows.Forms {
 
                                        case AutoScaleMode.Font:
                                                Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font);
-                                               int width = (int)((float)s.Width / 62f);
+                                               int width = (int)Math.Round ((float)s.Width / 62f);
                                                
                                                return new SizeF (width, s.Height);
                                }
@@ -317,7 +412,6 @@ namespace System.Windows.Forms {
                                return auto_scale_dimensions;
                        }
                }
-#endif
 
                [Browsable (false)]
                [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
@@ -340,6 +434,9 @@ namespace System.Windows.Forms {
                #endregion      // Public Instance Properties
 
                #region Protected Instance Methods
+               protected override bool CanEnableIme {
+                       get { return false; }
+               }
                protected override CreateParams CreateParams {
                        get {
                                return base.CreateParams;
@@ -348,18 +445,52 @@ namespace System.Windows.Forms {
                #endregion      // Public Instance Methods
 
                #region Public Instance Methods
-#if NET_2_0
-               public void PerformAutoScale ()
+               internal void PerformAutoScale (bool called_by_scale)
                {
-                       SuspendLayout ();
-                       Scale (AutoScaleFactor);
-                       ResumeLayout (false);
-                       
+                       if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale)
+                               return;
+
+                       if ((layout_suspended > 0) && !called_by_scale) {
+                               auto_scale_pending = true;
+                               return;
+                       }
+                       // Set this first so we don't get called again from
+                       // PerformDelayedAutoScale after ResumeLayout
+                       auto_scale_pending = false;
+
+                       SizeF factor = AutoScaleFactor;
+                       if (AutoScaleMode == AutoScaleMode.Inherit) {
+                               ContainerControl cc = FindContainer (this.Parent);
+                               if (cc != null)
+                                       factor = cc.AutoScaleFactor;
+                       }
+                       if (factor != new SizeF (1F, 1F)) {
+                               is_auto_scaling = true;
+                               SuspendLayout ();
+                               Scale (factor);
+                               ResumeLayout (false);
+                               is_auto_scaling = false;
+                       }
+
                        auto_scale_dimensions = CurrentAutoScaleDimensions;
                }
-#endif
 
-               [MonoTODO]
+               public void PerformAutoScale ()
+               {
+                       PerformAutoScale (false);
+               }
+
+               internal void PerformDelayedAutoScale ()
+               {
+                       if (auto_scale_pending)
+                               PerformAutoScale ();
+               }
+
+               internal bool IsAutoScaling {
+                       get { return is_auto_scaling; }
+               }
+
+               [MonoTODO ("Stub, not implemented")]
                static bool ValidateWarned;
                public bool Validate() {
                        //throw new NotImplementedException();
@@ -370,7 +501,6 @@ namespace System.Windows.Forms {
                        return true;
                }
 
-#if NET_2_0
                public bool Validate (bool checkAutoValidate)
                {
                        if ((checkAutoValidate && (AutoValidate != AutoValidate.Disable)) || !checkAutoValidate)
@@ -398,7 +528,6 @@ namespace System.Windows.Forms {
 
                        return true;
                }
-#endif
 
                bool IContainerControl.ActivateControl(Control control) {
                        return Select(control);
@@ -437,7 +566,6 @@ namespace System.Windows.Forms {
                        OnBindingContextChanged (EventArgs.Empty);
                }
 
-#if NET_2_0
                protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
                {
                        if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
@@ -445,7 +573,6 @@ namespace System.Windows.Forms {
                                
                        return base.ProcessCmdKey (ref msg, keyData);
                }
-#endif
 
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                protected override bool ProcessDialogChar(char charCode) {
@@ -556,10 +683,6 @@ namespace System.Windows.Forms {
                                                Select (active_control);
                                        else
                                                base.WndProc (ref m);
-#if false
-                                       else
-                                               SelectNextControl (null, true, true, true, false);
-#endif
                                break;
 
                                default:
@@ -572,6 +695,19 @@ namespace System.Windows.Forms {
                #region Internal Methods
                internal void ChildControlRemoved (Control control)
                {
+                       ContainerControl top_container = FindForm ();
+                       if (top_container == null)
+                               top_container = this;
+
+                       // Remove controls -as well as any sub control- that was in the pending validation chain
+                       ArrayList pending_validation_chain = top_container.pending_validation_chain;
+                       if (pending_validation_chain != null) {
+                               RemoveChildrenFromValidation (pending_validation_chain, control);
+
+                               if (pending_validation_chain.Count == 0)
+                                       top_container.pending_validation_chain = null;
+                       }
+
                        if (control == active_control || control.Contains (active_control)) {
                                SelectNextControl (this, true, true, true, true);
                                if (control == active_control || control.Contains (active_control)) {
@@ -579,12 +715,38 @@ namespace System.Windows.Forms {
                                }
                        }
                }
+
+               // Check that this control (or any child) is included in the pending validation chain
+               bool RemoveChildrenFromValidation (ArrayList validation_chain, Control c)
+               {
+                       if (RemoveFromValidationChain (validation_chain, c))
+                               return true;
+
+                       foreach (Control child in c.Controls)
+                               if (RemoveChildrenFromValidation (validation_chain, child))
+                                       return true;
+
+                       return false;
+               }
+
+               // Remove the top most control in the pending validation chain, as well as any children there,
+               // taking advantage of the fact that the chain is in reverse order of the control's hierarchy
+               bool RemoveFromValidationChain (ArrayList validation_chain, Control c)
+               {
+                       int idx = validation_chain.IndexOf (c);
+                       if (idx > -1) {
+                               pending_validation_chain.RemoveAt (idx--);
+                               return true;
+                       }
+
+                       return false;
+               }
+
                internal virtual void CheckAcceptButton()
                {
                        // do nothing here, only called if it is a Form
                }
 
-#if NET_2_0
                private bool ValidateNestedControls (Control c, ValidationConstraints constraints, bool recurse)
                {
                        bool validate_result = true;
@@ -623,10 +785,8 @@ namespace System.Windows.Forms {
 
                        return true;
                }
-#endif
                #endregion      // Internal Methods
 
-#if NET_2_0
                protected override void OnParentChanged (EventArgs e)
                {
                        base.OnParentChanged (e);
@@ -641,9 +801,9 @@ namespace System.Windows.Forms {
                                PerformAutoScale ();
                }
 
-               protected override void OnLayout (LayoutEventArgs levent)
+               protected override void OnLayout (LayoutEventArgs e)
                {
-                       base.OnLayout (levent);
+                       base.OnLayout (e);
                }
                
                AutoValidate auto_validate = AutoValidate.Inherit;
@@ -685,6 +845,5 @@ namespace System.Windows.Forms {
                        add { Events.AddHandler (OnValidateChanged, value); }
                        remove { Events.RemoveHandler (OnValidateChanged, value); }
                }
-#endif
        }
 }