In System.Windows.Forms.VisualStyles:
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
index b937d24b0d852ebdec11fd51bbe1c87568895725..669adfda0acc40178cead253d0abb8b5d7ea2acf 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         unvalidated_control;
@@ -66,39 +69,95 @@ namespace System.Windows.Forms {
                        }
 
                        set {
-                               if (value==null || (active_control == value && active_control.has_focus) || !Contains(value)) {
+                               if (value==null || (active_control == value)) {
                                        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;
+                                               }
                                        }
 
-                                       for (int i = chain.Count - 1; i >= 0; i--) {
-                                               walk = (Control) chain [i];
-                                               walk.FireEnter ();
+                                       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 ();
+                                               }
+                                       }
+
+                                       walk = this;
+                                       while (walk != null) {
+                                               if (walk.Parent is ContainerControl)
+                                                       ((ContainerControl)walk.Parent).active_control = walk;
+                                               walk = walk.Parent;
                                        }
                                }
 
@@ -128,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)
@@ -157,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;
@@ -167,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;
                        }
@@ -209,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) {
@@ -292,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) {
@@ -357,7 +436,7 @@ namespace System.Windows.Forms {
                                                return true;
                                        }
                                        break;
-                               } 
+                               }
 
 
                        }
@@ -386,7 +465,7 @@ namespace System.Windows.Forms {
                                        wrapped = true;
                                }
                        } while (c != active_control);
-                       
+
                        return false;
                }
 
@@ -415,21 +494,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
 
@@ -439,17 +519,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
        }
 }