In System.Windows.Forms.VisualStyles:
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
index 276f9baf6f894482157fed955b0d5ea7764b79cd..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;
@@ -44,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
@@ -63,27 +69,168 @@ namespace System.Windows.Forms {
                        }
 
                        set {
-                               if ((active_control==value) || (value==null)) {
+                               if (value==null || (active_control == value)) {
                                        return;
                                }
 
-                               active_control = value;
+                               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 = GetMostDeeplyNestedActiveControl (form);
+                                       Control common_container = GetCommonContainer (active, value);
+                                       ArrayList chain = new ArrayList ();
+                                       ArrayList validation_chain = new ArrayList ();
+                                       Control walk = active;
+
+                                       // 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;
+                                       }
+
+                                       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 ();
+                                               }
+                                       }
 
-                               if (!Contains(value) && this != value) {
-                                       throw new ArgumentException("Not a child control");
+                                       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();
-                               
+
                                // Scroll control into view
+                               ScrollControlIntoView(active_control);
 
                                // Let the control know it's selected
-                               Select(value);
+                               SendControlFocus (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,
+               // should eventually be rolled into ActiveControl setter
+               private Control GetCommonContainer (Control active_control, Control value)
+               {
+                       Control new_container = null;
+                       Control prev_container = active_control;
+
+                       while (prev_container != null) {
+                               new_container = value.Parent;
+                               while (new_container != null) {
+                                       if (new_container == prev_container)
+                                               return new_container;
+                                       new_container = new_container.Parent;
+                               }
+
+                               prev_container = prev_container.Parent;
+                       }
+
+                       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;
@@ -94,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;
                        }
@@ -136,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) {
@@ -170,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;
@@ -219,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) {
@@ -284,7 +436,7 @@ namespace System.Windows.Forms {
                                                return true;
                                        }
                                        break;
-                               } 
+                               }
 
 
                        }
@@ -302,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;
@@ -313,16 +465,26 @@ namespace System.Windows.Forms {
                                        wrapped = true;
                                }
                        } while (c != active_control);
-                       
+
                        return false;
                }
 
                protected virtual bool ProcessTabKey(bool forward) {
-                       return SelectNextControl(active_control, forward, true, true, true);
+                       return SelectNextControl(active_control, forward, true, true, false);
                }
 
-               protected override void Select(bool directed, bool forward) {
-                       base.Select(directed, forward);
+               protected override void Select(bool directed, bool forward)
+               {
+                       if (Parent != null) {
+                               IContainerControl parent = Parent.GetContainerControl ();
+                               if (parent != null) {
+                                       parent.ActiveControl = this;
+                               }
+                       }
+
+                       if (directed && auto_select_child) {
+                               SelectNextControl (null, forward, true, true, false);
+                       }
                }
 
                protected virtual void UpdateDefaultButton() {
@@ -331,13 +493,84 @@ namespace System.Windows.Forms {
 
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                protected override void WndProc(ref Message m) {
-                       base.WndProc(ref m);
+                       switch ((Msg) m.Msg) {
+
+                               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;
+                       }
                }
                #endregion      // Protected Instance Methods
-               
+
+               #region Internal Methods
                internal virtual void CheckAcceptButton()
                {
                        // 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
        }
 }