X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FManaged.Windows.Forms%2FSystem.Windows.Forms%2FContainerControl.cs;h=7483eeda8efa70ace9daeb4793cf662b6964fb14;hb=426893c54bdcc14cc32022c3cb9258340b1d150d;hp=276f9baf6f894482157fed955b0d5ea7764b79cd;hpb=948dbf8d4581ac17f5420cc4f7dc375e3c502576;p=mono.git diff --git a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ContainerControl.cs b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ContainerControl.cs index 276f9baf6f8..7483eeda8ef 100644 --- a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ContainerControl.cs +++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ContainerControl.cs @@ -25,17 +25,24 @@ // -// 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 && active_control.Focused)) { 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) { @@ -241,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()) { @@ -284,7 +446,7 @@ namespace System.Windows.Forms { return true; } break; - } + } } @@ -302,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; @@ -313,16 +475,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 +503,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 } }