In System.Windows.Forms.VisualStyles:
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
index 2eb0828fa539143bfac4618404c1a146438b1244..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         focused_control;
                private Control         unvalidated_control;
+
+               // 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;
@@ -45,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
@@ -64,48 +69,100 @@ namespace System.Windows.Forms {
                        }
 
                        set {
-                               if ((active_control==value) || (value==null)) {
+                               if (value==null || (active_control == value)) {
                                        return;
                                }
 
-                               
-                               if (!Contains(value) && this != value) {
-                                       throw new ArgumentException("Not a child control");
-                               }
-
-                               if (value == this) {
-                                       
+                               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 ();
+                                               /* 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;
                                        }
                                }
 
                                active_control = value;
 
-
                                if (this is Form)
                                        CheckAcceptButton();
 
@@ -113,13 +170,32 @@ namespace System.Windows.Forms {
                                ScrollControlIntoView(active_control);
 
                                // Let the control know it's selected
-//                             value.is_selected = true;
-//                             if (value.IsHandleCreated) {
-//                                     XplatUI.SetFocus (value.window.Handle);
-//                             }
+                               SendControlFocus (value);
+                       }
+               }
 
-                               SelectChild (value);
+               private bool ValidateControl (Control c)
+               {
+                       CancelEventArgs e = new CancelEventArgs ();
+
+                       c.FireValidating (e);
+
+                       if (e.Cancel)
+                               return false;
+
+                       c.FireValidated ();
+                       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,
@@ -143,7 +219,18 @@ namespace System.Windows.Forms {
                        return null;
                }
 
+               internal void SendControlFocus (Control c)
+               {
+                       if (c.IsHandleCreated) {
+                               XplatUI.SetFocus (c.window.Handle);
+                       }
+               }
+
 #if NET_2_0
+               [Browsable (false)]
+               [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
+               [Localizable (true)]
                public SizeF AutoScaleDimensions {
                        get {
                                return auto_scale_dimensions;
@@ -154,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;
                        }
@@ -196,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) {
@@ -230,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;
@@ -279,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) {
@@ -344,7 +436,7 @@ namespace System.Windows.Forms {
                                                return true;
                                        }
                                        break;
-                               } 
+                               }
 
 
                        }
@@ -362,7 +454,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;
@@ -373,7 +465,7 @@ namespace System.Windows.Forms {
                                        wrapped = true;
                                }
                        } while (c != active_control);
-                       
+
                        return false;
                }
 
@@ -381,42 +473,18 @@ namespace System.Windows.Forms {
                        return SelectNextControl(active_control, forward, true, true, false);
                }
 
-               protected override void Select(bool directed, bool forward) {
-
-                       int     index;
-                       bool    result;
-
-                       if (!directed) {
-                               // Select this control
-                               Select(this);
-                               return;
+               protected override void Select(bool directed, bool forward)
+               {
+                       if (Parent != null) {
+                               IContainerControl parent = Parent.GetContainerControl ();
+                               if (parent != null) {
+                                       parent.ActiveControl = this;
+                               }
                        }
 
-                       if (parent == null) {
-                               return;
+                       if (directed && auto_select_child) {
+                               SelectNextControl (null, forward, true, true, false);
                        }
-
-                       // FIXME - this thing is doing the wrong stuff, needs to be similar to SelectNextControl
-
-                       index = parent.child_controls.IndexOf(this);
-                       result = false;
-
-                       do {
-                               if (forward) {
-                                       if ((index+1) < parent.child_controls.Count) {
-                                               index++;
-                                       } else {
-                                               index = 0;
-                                       }
-                               } else {
-                                       if (index>0) {
-                                               index++;
-                                       } else {
-                                               index = parent.child_controls.Count-1;
-                                       }
-                               }
-                               result = Select(parent.child_controls[index]);
-                       } while (!result && parent.child_controls[index] != this);
                }
 
                protected virtual void UpdateDefaultButton() {
@@ -426,19 +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)
-                                       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
 
@@ -448,5 +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
        }
 }