2007-04-02 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
index dc084caa5b09bd57c33522d9e71cf5386cdf3b6c..7483eeda8efa70ace9daeb4793cf662b6964fb14 100644 (file)
 //
 
 
-// NOT COMPLETE
-
 using System.Collections;
 using System.ComponentModel;
 using System.ComponentModel.Design;
 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         focused_control;
                private Control         unvalidated_control;
 
                // This is an internal hack that allows some container controls
@@ -49,7 +51,6 @@ namespace System.Windows.Forms {
                #region Public Constructors
                public ContainerControl() {
                        active_control = null;
-                       focused_control = null;
                        unvalidated_control = null;
                        ControlRemoved += new ControlEventHandler(OnControlRemoved);
 #if NET_2_0
@@ -68,39 +69,95 @@ namespace System.Windows.Forms {
                        }
 
                        set {
-                               if ((active_control==value) || (value==null)) {
+                               if (value==null || (active_control == value && active_control.Focused)) {
                                        return;
                                }
 
-                               if (!Contains(value) && this != value) {
-                                       throw new ArgumentException("Not a child control");
+                               if (!Contains(value)) {
+                                       throw new ArgumentException("Cannot activate invisible or disabled control.");
                                }
 
                                // Fire the enter and leave events if possible
                                Form form = FindForm ();
                                if (form != null) {
-                                       Control active = form.ActiveControl;
-                                       Control common_container = GetCommonContainer (form.ActiveControl, value);
+                                       Control active = GetMostDeeplyNestedActiveControl (form);
+                                       Control common_container = GetCommonContainer (active, value);
                                        ArrayList chain = new ArrayList ();
+                                       ArrayList validation_chain = new ArrayList ();
                                        Control walk = active;
 
-                                       // Generate the leave messages  
+                                       // we split this up into three steps:
+                                       //    1. walk up the tree (if we need to) to our root, firing leave events.
+                                       //    2. validate.
+                                       //    3. walk down the tree (if we need to), firing enter events.
+
+                                       // "our root" is either the common container of the current active
+                                       // control and the new active control, or the current active control,
+                                       // or the new active control.  That is, it's either one of these three
+                                       // configurations:
+
+                                       //  (1)     root            (2)  new          (3)  current
+                                       //          /  \                /   \               /   \
+                                       //        ...   ...           ...   ...           ...   ...
+                                       //        /      \            /                           \
+                                       //     current   new       current                        new
+
+
+                                       // note (3) doesn't require any upward walking, and no leave events are generated.
+                                       //      (2) doesn't require any downward walking, and no enter events are generated.
+
+                                       // as we walk up the tree, we generate a list of all controls which cause
+                                       // validation.  After firing the leave events, we invoke (in order starting from
+                                       // the most deeply nested) their Validating event.  If one sets CancelEventArgs.Cancel
+                                       // to true, we ignore the control the user wanted to set ActiveControl to, and use
+                                       // the Validating control instead.
+
+                                       bool fire_enter = true;
+                                       Control root = common_container;
+
+                                       // Generate the leave messages
                                        while (walk != common_container) {
+                                               if (walk == value) {
+                                                       root = value;
+                                                       fire_enter = false;
+                                                       break;
+                                               }
                                                walk.FireLeave ();
-                                               if (walk.CausesValidation && !ValidateControl (walk))
-                                                       return;
+                                               /* clear our idea of the active control as we go back up */
+                                               if (walk is ContainerControl)
+                                                       ((ContainerControl)walk).active_control = null;
+
+                                               if (walk.CausesValidation)
+                                                       validation_chain.Add (walk);
+
                                                walk = walk.Parent;
                                        }
 
-                                       walk = value;
-                                       while (walk != common_container) {
-                                               chain.Add (walk);
-                                               walk = walk.Parent;
+                                       for (int i = 0; i < validation_chain.Count; i ++) {
+                                               if (!ValidateControl ((Control)validation_chain[i])) {
+                                                       value = (Control)validation_chain[i];
+                                                       fire_enter = true;
+                                               }
+                                       }
+
+                                       if (fire_enter) {
+                                               walk = value;
+                                               while (walk != root) {
+                                                       chain.Add (walk);
+                                                       walk = walk.Parent;
+                                               }
+
+                                               for (int i = chain.Count - 1; i >= 0; i--) {
+                                                       walk = (Control) chain [i];
+                                                       walk.FireEnter ();
+                                               }
                                        }
 
-                                       for (int i = chain.Count - 1; i >= 0; i--) {
-                                               walk = (Control) chain [i];
-                                               walk.FireEnter ();
+                                       walk = this;
+                                       while (walk != null) {
+                                               if (walk.Parent is ContainerControl)
+                                                       ((ContainerControl)walk.Parent).active_control = walk;
+                                               walk = walk.Parent;
                                        }
                                }
 
@@ -130,6 +187,17 @@ namespace System.Windows.Forms {
                        return true;
                }
 
+               private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
+               {
+                       Control active = container.ActiveControl;
+                       while (active is ContainerControl) {
+                               if (((ContainerControl)active).ActiveControl == null)
+                                       break;
+                               active = ((ContainerControl)active).ActiveControl;
+                       }
+                       return active;
+               }
+
                // Just in a separate method to make debugging a little easier,
                // should eventually be rolled into ActiveControl setter
                private Control GetCommonContainer (Control active_control, Control value)
@@ -151,7 +219,7 @@ namespace System.Windows.Forms {
                        return null;
                }
 
-               private void SendControlFocus (Control c)
+               internal void SendControlFocus (Control c)
                {
                        if (c.IsHandleCreated) {
                                XplatUI.SetFocus (c.window.Handle);
@@ -159,6 +227,10 @@ namespace System.Windows.Forms {
                }
 
 #if NET_2_0
+               [Browsable (false)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
+               [Localizable (true)]
                public SizeF AutoScaleDimensions {
                        get {
                                return auto_scale_dimensions;
@@ -169,19 +241,22 @@ namespace System.Windows.Forms {
                        }
                }
 
-               public SizeF AutoScaleFactor {
+               protected SizeF AutoScaleFactor {
                        get {
                                if (auto_scale_dimensions.IsEmpty) {
                                        return new SizeF(1f, 1f);
                                }
-                               return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width, 
+                               return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
                                        CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
                        }
                }
 
 
                [MonoTODO("Call scaling method")]
-               public virtual AutoScaleMode AutoScaleMode {
+               [Browsable (false)]
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               public AutoScaleMode AutoScaleMode {
                        get {
                                return auto_scale_mode;
                        }
@@ -211,6 +286,8 @@ namespace System.Windows.Forms {
 
 #if NET_2_0
                [MonoTODO("Revisit when System.Drawing.GDI.WindowsGraphics.GetTextMetrics is done or come up with other cross-plat avg. font width calc method")]
+               [Browsable (false)]
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
                public SizeF CurrentAutoScaleDimensions {
                        get {
                                switch(auto_scale_mode) {
@@ -245,13 +322,13 @@ namespace System.Windows.Forms {
                        get {
                                Control parent;
 
-                               parent = this.parent;
+                               parent = this.Parent;
 
                                while (parent != null) {
                                        if (parent is Form) {
                                                return (Form)parent;
                                        }
-                                       parent = parent.parent;
+                                       parent = parent.Parent;
                                }
 
                                return null;
@@ -294,7 +371,7 @@ namespace System.Windows.Forms {
                        base.Dispose(disposing);
                }
 
-               // LAMESPEC This used to be documented, but it's not in code 
+               // LAMESPEC This used to be documented, but it's not in code
                // and no longer listed in MSDN2
                // [EditorBrowsable (EditorBrowsableState.Advanced)]
                // protected override void OnControlRemoved(ControlEventArgs e) {
@@ -316,6 +393,16 @@ 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)
+                               return true;
+                               
+                       return base.ProcessCmdKey (ref msg, keyData);
+               }
+#endif
+
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                protected override bool ProcessDialogChar(char charCode) {
                        if (GetTopLevel()) {
@@ -359,7 +446,7 @@ namespace System.Windows.Forms {
                                                return true;
                                        }
                                        break;
-                               } 
+                               }
 
 
                        }
@@ -377,7 +464,7 @@ namespace System.Windows.Forms {
                                c = GetNextControl(c, true);
                                if (c != null) {
                                        // This is stupid. I want to be able to call c.ProcessMnemonic directly
-                                       if (c.ProcessControlMnemonic(charCode)) {
+                                       if (c.CanSelect && c.ProcessControlMnemonic(charCode)) {
                                                return(true);
                                        }
                                        continue;
@@ -388,7 +475,7 @@ namespace System.Windows.Forms {
                                        wrapped = true;
                                }
                        } while (c != active_control);
-                       
+
                        return false;
                }
 
@@ -417,23 +504,22 @@ namespace System.Windows.Forms {
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                protected override void WndProc(ref Message m) {
                        switch ((Msg) m.Msg) {
-                       case Msg.WM_LBUTTONDOWN:
-                               OnMouseDown (new MouseEventArgs (FromParamToMouseButtons ((int) m.WParam.ToInt32()), 
-                                       mouse_clicks, LowOrder ((int) m.LParam.ToInt32 ()),
-                                       HighOrder ((int) m.LParam.ToInt32 ()), 0));
-                               return;
-                               /*
-                       case Msg.WM_SETFOCUS:
-                               if (active_control != null)
-                                       Select (active_control);
-                               else
-                                       SelectNextControl (null, true, true, true, false);
-                               break;
-                               */
-                       case Msg.WM_KILLFOCUS:
+
+                               case Msg.WM_SETFOCUS:
+                                       if (active_control != null)
+                                               Select (active_control);
+                                       else
+                                               base.WndProc (ref m);
+#if false
+                                       else
+                                               SelectNextControl (null, true, true, true, false);
+#endif
                                break;
+
+                               default:
+                                       base.WndProc(ref m);
+                                       break;
                        }
-                       base.WndProc(ref m);
                }
                #endregion      // Protected Instance Methods
 
@@ -443,5 +529,58 @@ namespace System.Windows.Forms {
                        // do nothing here, only called if it is a Form
                }
                #endregion      // Internal Methods
+
+#if NET_2_0
+               protected override void OnParentChanged (EventArgs e)
+               {
+                       base.OnParentChanged (e);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
+               protected override void OnFontChanged (EventArgs e)
+               {
+                       base.OnFontChanged (e);
+               }
+
+               protected override void OnLayout (LayoutEventArgs levent)
+               {
+                       base.OnLayout (levent);
+               }
+               
+               AutoValidate auto_validate = AutoValidate.Inherit;
+
+               [Browsable (false)]
+               [AmbientValue (AutoValidate.Inherit)]
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public virtual AutoValidate AutoValidate {
+                       get {
+                               return auto_validate;
+                       }
+
+                       [MonoTODO("Currently does nothing with the setting")]
+                       set {
+                               if (auto_validate != value){
+                                       auto_validate = value;
+                                       OnAutoValidateChanged (new EventArgs ());
+                               }
+                       }
+               }
+
+               static object OnValidateChanged = new object ();
+
+               protected virtual void OnAutoValidateChanged (EventArgs e)
+               {
+                       EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
+                       if (eh != null)
+                               eh (this, e);
+               }
+
+               [Browsable (false)]
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public event EventHandler AutoValidateChanged {
+                       add { Events.AddHandler (OnValidateChanged, value); }
+                       remove { Events.RemoveHandler (OnValidateChanged, value); }
+               }
+#endif
        }
 }