Merge pull request #440 from mono-soc-2012/garyb/resources
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
index 7251abc20210bfb2dcaf431d390ac2df6ca970c0..44c3ea16d060023be0cec41c6c78278ddfce3406 100644 (file)
@@ -32,24 +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;
                private bool            auto_scale_mode_set;
                private bool            auto_scale_pending;
                private bool            is_auto_scaling;
-#endif
 
                internal bool validation_failed; //track whether validation was cancelled by a validating control
 
@@ -58,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.Inherit;
-#endif
                }
                #endregion      // Public Constructors
 
@@ -122,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;
@@ -139,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;
                                        }
@@ -179,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 ();
@@ -237,7 +327,6 @@ namespace System.Windows.Forms {
                        }
                }
 
-#if NET_2_0
                [Browsable (false)]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [EditorBrowsable (EditorBrowsableState.Advanced)]
@@ -290,7 +379,6 @@ namespace System.Windows.Forms {
                                }
                        }
                }
-#endif // NET_2_0
 
                [Browsable (false)]
                public override BindingContext BindingContext {
@@ -306,7 +394,6 @@ namespace System.Windows.Forms {
                        }
                }
 
-#if NET_2_0
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                public SizeF CurrentAutoScaleDimensions {
@@ -325,7 +412,6 @@ namespace System.Windows.Forms {
                                return auto_scale_dimensions;
                        }
                }
-#endif
 
                [Browsable (false)]
                [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
@@ -348,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;
@@ -356,7 +445,6 @@ namespace System.Windows.Forms {
                #endregion      // Public Instance Methods
 
                #region Public Instance Methods
-#if NET_2_0
                internal void PerformAutoScale (bool called_by_scale)
                {
                        if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale)
@@ -401,9 +489,8 @@ namespace System.Windows.Forms {
                internal bool IsAutoScaling {
                        get { return is_auto_scaling; }
                }
-#endif
 
-               [MonoTODO]
+               [MonoTODO ("Stub, not implemented")]
                static bool ValidateWarned;
                public bool Validate() {
                        //throw new NotImplementedException();
@@ -414,7 +501,6 @@ namespace System.Windows.Forms {
                        return true;
                }
 
-#if NET_2_0
                public bool Validate (bool checkAutoValidate)
                {
                        if ((checkAutoValidate && (AutoValidate != AutoValidate.Disable)) || !checkAutoValidate)
@@ -442,7 +528,6 @@ namespace System.Windows.Forms {
 
                        return true;
                }
-#endif
 
                bool IContainerControl.ActivateControl(Control control) {
                        return Select(control);
@@ -481,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)
@@ -489,7 +573,6 @@ namespace System.Windows.Forms {
                                
                        return base.ProcessCmdKey (ref msg, keyData);
                }
-#endif
 
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                protected override bool ProcessDialogChar(char charCode) {
@@ -550,16 +633,8 @@ namespace System.Windows.Forms {
                        wrapped = false;
                        c = active_control;
 
-#if NET_2_0
-                       System.Collections.Generic.List<MenuStrip> strips = new System.Collections.Generic.List<MenuStrip> ();
-#endif
-
                        do {
                                c = GetNextControl(c, true);
-#if NET_2_0
-                               if (c is MenuStrip)
-                                       strips.Add ((MenuStrip)c);
-#endif
                                if (c != null) {
                                        // This is stupid. I want to be able to call c.ProcessMnemonic directly
                                        if (c.ProcessControlMnemonic(charCode)) {
@@ -574,14 +649,6 @@ namespace System.Windows.Forms {
                                }
                        } while (c != active_control);
 
-#if NET_2_0
-                       // No one has an explicit mnemonic for this key.
-                       // Let MenuStrips have a chance at implicit mnemonics.
-                       foreach (MenuStrip ms in strips)
-                               if (ms.ProcessImplicitMnemonic (charCode))
-                                       return true;
-#endif
-
                        return false;
                }
 
@@ -616,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:
@@ -632,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)) {
@@ -639,44 +715,57 @@ 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)
-
-#else
-               // Used by Form to validate its children on the 1.1 Profile
-               internal bool ValidateNestedControls (Control c, bool recurse)
-#endif
                {
                        bool validate_result = true;
 
                        if (!c.CausesValidation)
                                validate_result = true;
-#if NET_2_0
                        else if (!ValidateThisControl (c, constraints))
                                validate_result = true;
-#endif
                        else if (!ValidateControl (c))
                                validate_result = false;
 
                        if (recurse)
-                               foreach (Control control in c.Controls) {
-                                               if (!ValidateNestedControls (control, 
-#if NET_2_0
-                                                                            constraints, 
-#endif
-                                                                            recurse))
-                                                       return false;
-                               }
+                               foreach (Control control in c.Controls)
+                                       if (!ValidateNestedControls (control, constraints, recurse))
+                                               return false;
 
                        return validate_result;
                }
-#if NET_2_0
+
                private bool ValidateThisControl (Control c, ValidationConstraints constraints)
                {
                        if (constraints == ValidationConstraints.None)
@@ -696,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);
@@ -758,6 +845,5 @@ namespace System.Windows.Forms {
                        add { Events.AddHandler (OnValidateChanged, value); }
                        remove { Events.RemoveHandler (OnValidateChanged, value); }
                }
-#endif
        }
 }