X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FManaged.Windows.Forms%2FSystem.Windows.Forms%2FContainerControl.cs;h=20a9425d41da1f448c7e3870dc8541734e28bc77;hb=ab2cc8b39bec26c7b300ad72cd705437a9ec057b;hp=b454df3c4ede858463bda833822099c612235d49;hpb=5f58f7e4d92128db927f7918b1d25cf8c6b70893;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 b454df3c4ed..20a9425d41d 100644 --- a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ContainerControl.cs +++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ContainerControl.cs @@ -39,6 +39,7 @@ namespace System.Windows.Forms { public class ContainerControl : ScrollableControl, IContainerControl { private Control active_control; private Control unvalidated_control; + private ArrayList pending_validation_chain; // This is an internal hack that allows some container controls // to not auto select their child when they are activated @@ -46,8 +47,13 @@ namespace System.Windows.Forms { #if NET_2_0 private SizeF auto_scale_dimensions; private AutoScaleMode auto_scale_mode; + private bool auto_scale_mode_set; + private bool auto_scale_pending; + private bool is_auto_scaling; #endif + internal bool validation_failed; //track whether validation was cancelled by a validating control + #region Public Constructors public ContainerControl() { active_control = null; @@ -55,7 +61,7 @@ namespace System.Windows.Forms { ControlRemoved += new ControlEventHandler(OnControlRemoved); #if NET_2_0 auto_scale_dimensions = SizeF.Empty; - auto_scale_mode = AutoScaleMode.None; + auto_scale_mode = AutoScaleMode.Inherit; #endif } #endregion // Public Constructors @@ -69,7 +75,7 @@ namespace System.Windows.Forms { } set { - if (value==null || (active_control == value)) { + if (value==null || (active_control == value && active_control.Focused)) { return; } @@ -79,101 +85,202 @@ namespace System.Windows.Forms { // 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; + Control active = GetMostDeeplyNestedActiveControl (form == null ? this : form); + Control common_ancestor = 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 ancestor 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_ancestor; + + active_control = value; + + // Generate the leave messages + while (walk != common_ancestor && walk != null) { + 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; + } + + // Validation can be postponed due to all the controls + // in the enter chain not causing validation. If we don't have any + // enter chain, it means that the selected control is a child and then + // we need to validate the controls anyway + bool postpone_validation; + Control topmost_under_root = null; // topmost under root, in the *enter* chain + if (value == root) + postpone_validation = false; + else { + postpone_validation = true; + walk = value; + while (walk != root && walk != null) { if (walk.CausesValidation) - validation_chain.Add (walk); + postpone_validation = false; + topmost_under_root = 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; - } + Control failed_validation_control = PerformValidation (form == null ? this : form, postpone_validation, + validation_chain, topmost_under_root); + if (failed_validation_control != null) { + active_control = value = failed_validation_control; + fire_enter = true; + } + + if (fire_enter) { + walk = value; + while (walk != root && walk != null) { + chain.Add (walk); + walk = walk.Parent; } - if (fire_enter) { - walk = value; - while (walk != root) { - chain.Add (walk); - walk = walk.Parent; - } + if (root != null && walk == root && !(root is ContainerControl)) + chain.Add (walk); - 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; + walk = this; + Control ctl = this; + while (walk != null) { + if (walk.Parent is ContainerControl) { + ((ContainerControl) walk.Parent).active_control = ctl; + ctl = walk.Parent; } + walk = walk.Parent; } - active_control = value; - if (this is Form) CheckAcceptButton(); // Scroll control into view ScrollControlIntoView(active_control); - + + + walk = this; + ctl = this; + while (walk != null) { + if (walk.Parent is ContainerControl) { + ctl = walk.Parent; + } + walk = walk.Parent; + } + // Let the control know it's selected - SendControlFocus (value); + if (ctl.InternalContainsFocus) + SendControlFocus (active_control); + } + } + + // Return the control where validation failed, and null otherwise + // @topmost_under_root is the control under the root in the enter chain, if any + // + // The process of validation happens as described: + // + // 1. Iterate over the nodes in the enter chain (walk down), looking for any node + // causing validation. If we can't find any, don't validate the current validation chain, but postpone it, + // saving it in the top_container.pending_validation_chain field, since we need to keep track of it later. + // If we have a previous pending_validation_chain, add the new nodes, making sure they are not repeated + // (this is computed in ActiveControl and we receive if as the postpone_validation parameter). + // + // 2. If we found at least one node causing validation in the enter chain, try to validate the elements + // in pending_validation_chain, if any. Then continue with the ones receives as parameters. + // + // 3. Return null if all the validation performed successfully, and return the control where the validation + // failed otherwise. + // + private Control PerformValidation (ContainerControl top_container, bool postpone_validation, ArrayList validation_chain, + Control topmost_under_root) + { + validation_failed = false; + + if (postpone_validation) { + AddValidationChain (top_container, validation_chain); + return null; + } + + // if not null, pending chain has always one element or more + if (top_container.pending_validation_chain != null) { + // if the topmost node in the enter chain is exactly the topmost + // int the validation chain, remove it, as .net does + int last_idx = top_container.pending_validation_chain.Count - 1; + if (topmost_under_root == top_container.pending_validation_chain [last_idx]) + top_container.pending_validation_chain.RemoveAt (last_idx); + + AddValidationChain (top_container, validation_chain); + validation_chain = top_container.pending_validation_chain; + top_container.pending_validation_chain = null; } + + for (int i = 0; i < validation_chain.Count; i ++) { + if (!ValidateControl ((Control)validation_chain[i])) { + validation_failed = true; + return (Control)validation_chain[i]; + } + } + + return null; } + // Add the elements in validation_chain to the pending validation chain stored in top_container + private void AddValidationChain (ContainerControl top_container, ArrayList validation_chain) + { + if (validation_chain.Count == 0) + return; + + if (top_container.pending_validation_chain == null || top_container.pending_validation_chain.Count == 0) { + top_container.pending_validation_chain = validation_chain; + return; + } + + foreach (Control c in validation_chain) + if (!top_container.pending_validation_chain.Contains (c)) + top_container.pending_validation_chain.Add (c); + } + private bool ValidateControl (Control c) { CancelEventArgs e = new CancelEventArgs (); @@ -237,22 +344,25 @@ namespace System.Windows.Forms { } set { - auto_scale_dimensions = value; + if (auto_scale_dimensions != value) { + auto_scale_dimensions = value; + + PerformAutoScale (); + } } } protected SizeF AutoScaleFactor { get { - if (auto_scale_dimensions.IsEmpty) { - return new SizeF(1f, 1f); - } + if (auto_scale_dimensions.IsEmpty) + return new SizeF (1f, 1f); + return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width, CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height); } } - [MonoTODO("Call scaling method")] [Browsable (false)] [EditorBrowsable (EditorBrowsableState.Advanced)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] @@ -261,10 +371,18 @@ namespace System.Windows.Forms { return auto_scale_mode; } set { + if (this is Form) + (this as Form).AutoScale = false; + if (auto_scale_mode != value) { auto_scale_mode = value; - // Trigger scaling + if (auto_scale_mode_set) + auto_scale_dimensions = SizeF.Empty; + + auto_scale_mode_set = true; + + PerformAutoScale (); } } } @@ -285,30 +403,19 @@ 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) { - case AutoScaleMode.Dpi: { - Bitmap bmp; - Graphics g; - SizeF size; - - bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - g = Graphics.FromImage(bmp); - size = new SizeF(g.DpiX, g.DpiY); - g.Dispose(); - bmp.Dispose(); - return size; - } - - case AutoScaleMode.Font: { - // http://msdn2.microsoft.com/en-us/library/system.windows.forms.containercontrol.currentautoscaledimensions(VS.80).aspx - // Implement System.Drawing.GDI.WindowsGraphics.GetTextMetrics first... - break; - } + case AutoScaleMode.Dpi: + return TextRenderer.GetDpi (); + + case AutoScaleMode.Font: + Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font); + int width = (int)Math.Round ((float)s.Width / 62f); + + return new SizeF (width, s.Height); } return auto_scale_dimensions; @@ -337,6 +444,11 @@ namespace System.Windows.Forms { #endregion // Public Instance Properties #region Protected Instance Methods +#if NET_2_0 + protected override bool CanEnableIme { + get { return false; } + } +#endif protected override CreateParams CreateParams { get { return base.CreateParams; @@ -345,7 +457,54 @@ namespace System.Windows.Forms { #endregion // Public Instance Methods #region Public Instance Methods - [MonoTODO] +#if NET_2_0 + internal void PerformAutoScale (bool called_by_scale) + { + if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale) + return; + + if ((layout_suspended > 0) && !called_by_scale) { + auto_scale_pending = true; + return; + } + // Set this first so we don't get called again from + // PerformDelayedAutoScale after ResumeLayout + auto_scale_pending = false; + + SizeF factor = AutoScaleFactor; + if (AutoScaleMode == AutoScaleMode.Inherit) { + ContainerControl cc = FindContainer (this.Parent); + if (cc != null) + factor = cc.AutoScaleFactor; + } + if (factor != new SizeF (1F, 1F)) { + is_auto_scaling = true; + SuspendLayout (); + Scale (factor); + ResumeLayout (false); + is_auto_scaling = false; + } + + auto_scale_dimensions = CurrentAutoScaleDimensions; + } + + public void PerformAutoScale () + { + PerformAutoScale (false); + } + + internal void PerformDelayedAutoScale () + { + if (auto_scale_pending) + PerformAutoScale (); + } + + internal bool IsAutoScaling { + get { return is_auto_scaling; } + } +#endif + + [MonoTODO ("Stub, not implemented")] static bool ValidateWarned; public bool Validate() { //throw new NotImplementedException(); @@ -356,6 +515,36 @@ namespace System.Windows.Forms { return true; } +#if NET_2_0 + public bool Validate (bool checkAutoValidate) + { + if ((checkAutoValidate && (AutoValidate != AutoValidate.Disable)) || !checkAutoValidate) + return Validate (); + + return true; + } + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual bool ValidateChildren () + { + return ValidateChildren (ValidationConstraints.Selectable); + } + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public virtual bool ValidateChildren (ValidationConstraints validationConstraints) + { + bool recurse = !((validationConstraints & ValidationConstraints.ImmediateChildren) == ValidationConstraints.ImmediateChildren); + + foreach (Control control in Controls) + if (!ValidateNestedControls (control, validationConstraints, recurse)) + return false; + + return true; + } +#endif + bool IContainerControl.ActivateControl(Control control) { return Select(control); } @@ -393,6 +582,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()) { @@ -412,8 +611,10 @@ namespace System.Windows.Forms { switch (key) { case Keys.Tab: { - if (ProcessTabKey((Control.ModifierKeys & Keys.Shift) == 0)) { - return true; + if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) { + if (ProcessTabKey ((Control.ModifierKeys & Keys.Shift) == 0)) { + return true; + } } break; } @@ -454,7 +655,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.CanSelect && c.ProcessControlMnemonic(charCode)) { + if (c.ProcessControlMnemonic(charCode)) { return(true); } continue; @@ -495,12 +696,6 @@ namespace System.Windows.Forms { 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)); - break; - case Msg.WM_SETFOCUS: if (active_control != null) Select (active_control); @@ -520,10 +715,100 @@ namespace System.Windows.Forms { #endregion // Protected Instance Methods #region Internal Methods + internal void ChildControlRemoved (Control control) + { + ContainerControl top_container = FindForm (); + if (top_container == null) + top_container = this; + + // Remove controls -as well as any sub control- that was in the pending validation chain + ArrayList pending_validation_chain = top_container.pending_validation_chain; + if (pending_validation_chain != null) { + RemoveChildrenFromValidation (pending_validation_chain, control); + + if (pending_validation_chain.Count == 0) + top_container.pending_validation_chain = null; + } + + if (control == active_control || control.Contains (active_control)) { + SelectNextControl (this, true, true, true, true); + if (control == active_control || control.Contains (active_control)) { + active_control = null; + } + } + } + + // Check that this control (or any child) is included in the pending validation chain + bool RemoveChildrenFromValidation (ArrayList validation_chain, Control c) + { + if (RemoveFromValidationChain (validation_chain, c)) + return true; + + foreach (Control child in c.Controls) + if (RemoveChildrenFromValidation (validation_chain, child)) + return true; + + return false; + } + + // Remove the top most control in the pending validation chain, as well as any children there, + // taking advantage of the fact that the chain is in reverse order of the control's hierarchy + bool RemoveFromValidationChain (ArrayList validation_chain, Control c) + { + int idx = validation_chain.IndexOf (c); + if (idx > -1) { + pending_validation_chain.RemoveAt (idx--); + return true; + } + + return false; + } + internal virtual void CheckAcceptButton() { // do nothing here, only called if it is a Form } + +#if NET_2_0 + private bool ValidateNestedControls (Control c, ValidationConstraints constraints, bool recurse) + { + bool validate_result = true; + + if (!c.CausesValidation) + validate_result = true; + else if (!ValidateThisControl (c, constraints)) + validate_result = true; + else if (!ValidateControl (c)) + validate_result = false; + + if (recurse) + foreach (Control control in c.Controls) + if (!ValidateNestedControls (control, constraints, recurse)) + return false; + + return validate_result; + } + + private bool ValidateThisControl (Control c, ValidationConstraints constraints) + { + if (constraints == ValidationConstraints.None) + return true; + + if ((constraints & ValidationConstraints.Enabled) == ValidationConstraints.Enabled && !c.Enabled) + return false; + + if ((constraints & ValidationConstraints.Selectable) == ValidationConstraints.Selectable && !c.GetStyle (ControlStyles.Selectable)) + return false; + + if ((constraints & ValidationConstraints.TabStop) == ValidationConstraints.TabStop && !c.TabStop) + return false; + + if ((constraints & ValidationConstraints.Visible) == ValidationConstraints.Visible && !c.Visible) + return false; + + return true; + } +#endif #endregion // Internal Methods #if NET_2_0 @@ -536,9 +821,21 @@ namespace System.Windows.Forms { protected override void OnFontChanged (EventArgs e) { base.OnFontChanged (e); + + if (AutoScaleMode == AutoScaleMode.Font) + PerformAutoScale (); } + protected override void OnLayout (LayoutEventArgs e) + { + base.OnLayout (e); + } + AutoValidate auto_validate = AutoValidate.Inherit; + + [Browsable (false)] + [AmbientValue (AutoValidate.Inherit)] + [EditorBrowsable (EditorBrowsableState.Never)] public virtual AutoValidate AutoValidate { get { return auto_validate; @@ -553,6 +850,11 @@ namespace System.Windows.Forms { } } + internal bool ShouldSerializeAutoValidate () + { + return this.AutoValidate != AutoValidate.Inherit; + } + static object OnValidateChanged = new object (); protected virtual void OnAutoValidateChanged (EventArgs e) @@ -562,6 +864,8 @@ namespace System.Windows.Forms { eh (this, e); } + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] public event EventHandler AutoValidateChanged { add { Events.AddHandler (OnValidateChanged, value); } remove { Events.RemoveHandler (OnValidateChanged, value); }