Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ContainerControl.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 Novell, Inc.
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27
28 using System.Collections;
29 using System.ComponentModel;
30 using System.ComponentModel.Design;
31 using System.Drawing;
32 using System.Runtime.InteropServices;
33
34 namespace System.Windows.Forms {
35         [ClassInterface (ClassInterfaceType.AutoDispatch)]
36         [ComVisible (true)]
37         public class ContainerControl : ScrollableControl, IContainerControl {
38                 private Control         active_control;
39                 private Control         unvalidated_control;
40                 private ArrayList       pending_validation_chain;
41
42                 // This is an internal hack that allows some container controls
43                 // to not auto select their child when they are activated
44                 internal bool           auto_select_child = true;
45                 private SizeF           auto_scale_dimensions;
46                 private AutoScaleMode   auto_scale_mode;
47                 private bool            auto_scale_mode_set;
48                 private bool            auto_scale_pending;
49                 private bool            is_auto_scaling;
50
51                 internal bool validation_failed; //track whether validation was cancelled by a validating control
52
53                 #region Public Constructors
54                 public ContainerControl() {
55                         active_control = null;
56                         unvalidated_control = null;
57                         ControlRemoved += new ControlEventHandler(OnControlRemoved);
58                         auto_scale_dimensions = SizeF.Empty;
59                         auto_scale_mode = AutoScaleMode.Inherit;
60                 }
61                 #endregion      // Public Constructors
62
63                 #region Public Instance Properties
64                 [Browsable (false)]
65                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
66                 public Control ActiveControl {
67                         get {
68                                 return active_control;
69                         }
70
71                         set {
72                                 if (value==null || (active_control == value && active_control.Focused)) {
73                                         return;
74                                 }
75
76                                 if (!Contains(value)) {
77                                         throw new ArgumentException("Cannot activate invisible or disabled control.");
78                                 }
79
80                                 // Fire the enter and leave events if possible
81                                 Form form = FindForm ();
82                                 Control active = GetMostDeeplyNestedActiveControl (form == null ? this : form);
83                                 Control common_ancestor = GetCommonContainer (active, value);
84                                 ArrayList chain = new ArrayList ();
85                                 ArrayList validation_chain = new ArrayList ();
86                                 Control walk = active;
87
88                                 // we split this up into three steps:
89                                 //    1. walk up the tree (if we need to) to our root, firing leave events.
90                                 //    2. validate.
91                                 //    3. walk down the tree (if we need to), firing enter events.
92
93                                 // "our root" is either the common ancestor of the current active
94                                 // control and the new active control, or the current active control,
95                                 // or the new active control.  That is, it's either one of these three
96                                 // configurations:
97
98                                 //  (1)     root            (2)  new          (3)  current
99                                 //          /  \                /   \               /   \
100                                 //        ...   ...           ...   ...           ...   ...
101                                 //        /      \            /                           \
102                                 //     current   new       current                        new
103
104
105                                 // note (3) doesn't require any upward walking, and no leave events are generated.
106                                 //      (2) doesn't require any downward walking, and no enter events are generated.
107
108                                 // as we walk up the tree, we generate a list of all controls which cause
109                                 // validation.  After firing the leave events, we invoke (in order starting from
110                                 // the most deeply nested) their Validating event.  If one sets CancelEventArgs.Cancel
111                                 // to true, we ignore the control the user wanted to set ActiveControl to, and use
112                                 // the Validating control instead.
113
114                                 bool fire_enter = true;
115                                 Control root = common_ancestor;
116
117                                 active_control = value;
118
119                                 // Generate the leave messages
120                                 while (walk != common_ancestor && walk != null) {
121                                         if (walk == value) {
122                                                 root = value;
123                                                 fire_enter = false;
124                                                 break;
125                                         }
126                                         walk.FireLeave ();
127                                         /* clear our idea of the active control as we go back up */
128                                         if (walk is ContainerControl)
129                                                 ((ContainerControl)walk).active_control = null;
130
131                                         if (walk.CausesValidation)
132                                                 validation_chain.Add (walk);
133
134                                         walk = walk.Parent;
135                                 }
136
137                                 // Validation can be postponed due to all the controls
138                                 // in the enter chain not causing validation. If we don't have any
139                                 // enter chain, it means that the selected control is a child and then
140                                 // we need to validate the controls anyway
141                                 bool postpone_validation;
142                                 Control topmost_under_root = null; // topmost under root, in the *enter* chain
143                                 if (value == root)
144                                         postpone_validation = false;
145                                 else {
146                                         postpone_validation = true;
147                                         walk = value;
148                                         while (walk != root && walk != null) {
149                                                 if (walk.CausesValidation)
150                                                         postpone_validation = false;
151
152                                                 topmost_under_root = walk;
153                                                 walk = walk.Parent;
154                                         }
155                                 }
156
157                                 Control failed_validation_control = PerformValidation (form == null ? this : form, postpone_validation, 
158                                                 validation_chain, topmost_under_root);
159                                 if (failed_validation_control != null) {
160                                         active_control = value = failed_validation_control;
161                                         fire_enter = true;
162                                 }
163
164                                 if (fire_enter) {
165                                         walk = value;
166                                         while (walk != root && walk != null) {
167                                                 chain.Add (walk);
168                                                 walk = walk.Parent;
169                                         }
170
171                                         if (root != null && walk == root && !(root is ContainerControl))
172                                                 chain.Add (walk);
173
174                                         for (int i = chain.Count - 1; i >= 0; i--) {
175                                                 walk = (Control) chain [i];
176                                                 walk.FireEnter ();
177                                         }
178                                 }
179
180                                 walk = this;
181                                 Control ctl = this;
182                                 while (walk != null) {
183                                         if (walk.Parent is ContainerControl) {
184                                                 ((ContainerControl) walk.Parent).active_control = ctl;
185                                                 ctl = walk.Parent;
186                                         }
187                                         walk = walk.Parent;
188                                 }
189
190                                 if (this is Form)
191                                         CheckAcceptButton();
192
193                                 // Scroll control into view
194                                 ScrollControlIntoView(active_control);
195                                 
196                                 
197                                 walk = this;
198                                 ctl = this;
199                                 while (walk != null) {
200                                         if (walk.Parent is ContainerControl) {
201                                                 ctl = walk.Parent;
202                                         }
203                                         walk = walk.Parent;
204                                 }
205                                 
206                                 // Let the control know it's selected
207                                 if (ctl.InternalContainsFocus)
208                                         SendControlFocus (active_control);
209                         }
210                 }
211
212                 // Return the control where validation failed, and null otherwise
213                 // @topmost_under_root is the control under the root in the enter chain, if any
214                 //
215                 // The process of validation happens as described:
216                 //
217                 //      1. Iterate over the nodes in the enter chain (walk down), looking for any node
218                 //      causing validation. If we can't find any, don't validate the current validation chain, but postpone it,
219                 //      saving it in the top_container.pending_validation_chain field, since we need to keep track of it later.
220                 //      If we have a previous pending_validation_chain, add the new nodes, making sure they are not repeated
221                 //      (this is computed in ActiveControl and we receive if as the postpone_validation parameter).
222                 //
223                 //      2. If we found at least one node causing validation in the enter chain, try to validate the elements
224                 //      in pending_validation_chain, if any. Then continue with the ones receives as parameters.
225                 //
226                 //      3. Return null if all the validation performed successfully, and return the control where the validation
227                 //      failed otherwise.
228                 //
229                 private Control PerformValidation (ContainerControl top_container, bool postpone_validation, ArrayList validation_chain, 
230                                 Control topmost_under_root)
231                 {
232                         validation_failed = false;
233
234                         if (postpone_validation) {
235                                 AddValidationChain (top_container, validation_chain);
236                                 return null;
237                         }
238
239                         // if not null, pending chain has always one element or more
240                         if (top_container.pending_validation_chain != null) {
241                                 // if the topmost node in the enter chain is exactly the topmost
242                                 // int the validation chain, remove it, as .net does
243                                 int last_idx = top_container.pending_validation_chain.Count - 1;
244                                 if (topmost_under_root == top_container.pending_validation_chain [last_idx])
245                                         top_container.pending_validation_chain.RemoveAt (last_idx);
246
247                                 AddValidationChain (top_container, validation_chain);
248                                 validation_chain = top_container.pending_validation_chain;
249                                 top_container.pending_validation_chain = null;
250                         }
251
252                         for (int i = 0; i < validation_chain.Count; i ++) {
253                                 if (!ValidateControl ((Control)validation_chain[i])) {
254                                         validation_failed = true;
255                                         return (Control)validation_chain[i];
256                                 }
257                         }
258
259                         return null;
260                 }
261
262                 // Add the elements in validation_chain to the pending validation chain stored in top_container
263                 private void AddValidationChain (ContainerControl top_container, ArrayList validation_chain)
264                 {
265                         if (validation_chain.Count == 0)
266                                 return;
267
268                         if (top_container.pending_validation_chain == null || top_container.pending_validation_chain.Count == 0) {
269                                 top_container.pending_validation_chain = validation_chain;
270                                 return;
271                         }
272
273                         foreach (Control c in validation_chain)
274                                 if (!top_container.pending_validation_chain.Contains (c))
275                                         top_container.pending_validation_chain.Add (c);
276                 }       
277
278                 private bool ValidateControl (Control c)
279                 {
280                         CancelEventArgs e = new CancelEventArgs ();
281
282                         c.FireValidating (e);
283
284                         if (e.Cancel)
285                                 return false;
286
287                         c.FireValidated ();
288                         return true;
289                 }
290
291                 private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
292                 {
293                         Control active = container.ActiveControl;
294                         while (active is ContainerControl) {
295                                 if (((ContainerControl)active).ActiveControl == null)
296                                         break;
297                                 active = ((ContainerControl)active).ActiveControl;
298                         }
299                         return active;
300                 }
301
302                 // Just in a separate method to make debugging a little easier,
303                 // should eventually be rolled into ActiveControl setter
304                 private Control GetCommonContainer (Control active_control, Control value)
305                 {
306                         Control new_container = null;
307                         Control prev_container = active_control;
308
309                         while (prev_container != null) {
310                                 new_container = value.Parent;
311                                 while (new_container != null) {
312                                         if (new_container == prev_container)
313                                                 return new_container;
314                                         new_container = new_container.Parent;
315                                 }
316
317                                 prev_container = prev_container.Parent;
318                         }
319
320                         return null;
321                 }
322
323                 internal void SendControlFocus (Control c)
324                 {
325                         if (c.IsHandleCreated) {
326                                 XplatUI.SetFocus (c.window.Handle);
327                         }
328                 }
329
330                 [Browsable (false)]
331                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
332                 [EditorBrowsable (EditorBrowsableState.Advanced)]
333                 [Localizable (true)]
334                 public SizeF AutoScaleDimensions {
335                         get {
336                                 return auto_scale_dimensions;
337                         }
338
339                         set {
340                                 if (auto_scale_dimensions != value) {
341                                         auto_scale_dimensions = value;
342                                         
343                                         PerformAutoScale ();
344                                 }
345                         }
346                 }
347
348                 protected SizeF AutoScaleFactor {
349                         get {
350                                 if (auto_scale_dimensions.IsEmpty)
351                                         return new SizeF (1f, 1f);
352
353                                 return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
354                                         CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
355                         }
356                 }
357
358
359                 [Browsable (false)]
360                 [EditorBrowsable (EditorBrowsableState.Advanced)]
361                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
362                 public AutoScaleMode AutoScaleMode {
363                         get {
364                                 return auto_scale_mode;
365                         }
366                         set {
367                                 if (this is Form)
368                                         (this as Form).AutoScale = false;
369
370                                 if (auto_scale_mode != value) {
371                                         auto_scale_mode = value;
372
373                                         if (auto_scale_mode_set)
374                                                 auto_scale_dimensions = SizeF.Empty;
375
376                                         auto_scale_mode_set = true;
377
378                                         PerformAutoScale ();
379                                 }
380                         }
381                 }
382
383                 [Browsable (false)]
384                 public override BindingContext BindingContext {
385                         get {
386                                 if (base.BindingContext == null) {
387                                         base.BindingContext = new BindingContext();
388                                 }
389                                 return base.BindingContext;
390                         }
391
392                         set {
393                                 base.BindingContext = value;
394                         }
395                 }
396
397                 [Browsable (false)]
398                 [EditorBrowsable (EditorBrowsableState.Advanced)]
399                 public SizeF CurrentAutoScaleDimensions {
400                         get {
401                                 switch(auto_scale_mode) {
402                                         case AutoScaleMode.Dpi:
403                                                 return TextRenderer.GetDpi ();
404
405                                         case AutoScaleMode.Font:
406                                                 Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font);
407                                                 int width = (int)Math.Round ((float)s.Width / 62f);
408                                                 
409                                                 return new SizeF (width, s.Height);
410                                 }
411
412                                 return auto_scale_dimensions;
413                         }
414                 }
415
416                 [Browsable (false)]
417                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
418                 public Form ParentForm {
419                         get {
420                                 Control parent;
421
422                                 parent = this.Parent;
423
424                                 while (parent != null) {
425                                         if (parent is Form) {
426                                                 return (Form)parent;
427                                         }
428                                         parent = parent.Parent;
429                                 }
430
431                                 return null;
432                         }
433                 }
434                 #endregion      // Public Instance Properties
435
436                 #region Protected Instance Methods
437                 protected override bool CanEnableIme {
438                         get { return false; }
439                 }
440                 protected override CreateParams CreateParams {
441                         get {
442                                 return base.CreateParams;
443                         }
444                 }
445                 #endregion      // Public Instance Methods
446
447                 #region Public Instance Methods
448                 internal void PerformAutoScale (bool called_by_scale)
449                 {
450                         if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale)
451                                 return;
452
453                         if ((layout_suspended > 0) && !called_by_scale) {
454                                 auto_scale_pending = true;
455                                 return;
456                         }
457                         // Set this first so we don't get called again from
458                         // PerformDelayedAutoScale after ResumeLayout
459                         auto_scale_pending = false;
460
461                         SizeF factor = AutoScaleFactor;
462                         if (AutoScaleMode == AutoScaleMode.Inherit) {
463                                 ContainerControl cc = FindContainer (this.Parent);
464                                 if (cc != null)
465                                         factor = cc.AutoScaleFactor;
466                         }
467                         if (factor != new SizeF (1F, 1F)) {
468                                 is_auto_scaling = true;
469                                 SuspendLayout ();
470                                 Scale (factor);
471                                 ResumeLayout (false);
472                                 is_auto_scaling = false;
473                         }
474
475                         auto_scale_dimensions = CurrentAutoScaleDimensions;
476                 }
477
478                 public void PerformAutoScale ()
479                 {
480                         PerformAutoScale (false);
481                 }
482
483                 internal void PerformDelayedAutoScale ()
484                 {
485                         if (auto_scale_pending)
486                                 PerformAutoScale ();
487                 }
488
489                 internal bool IsAutoScaling {
490                         get { return is_auto_scaling; }
491                 }
492
493                 [MonoTODO ("Stub, not implemented")]
494                 static bool ValidateWarned;
495                 public bool Validate() {
496                         //throw new NotImplementedException();
497                         if (!ValidateWarned) {
498                                 Console.WriteLine("ContainerControl.Validate is not yet implemented");
499                                 ValidateWarned = true;
500                         }
501                         return true;
502                 }
503
504                 public bool Validate (bool checkAutoValidate)
505                 {
506                         if ((checkAutoValidate && (AutoValidate != AutoValidate.Disable)) || !checkAutoValidate)
507                                 return Validate ();
508                                 
509                         return true;
510                 }
511                 
512                 [Browsable (false)]
513                 [EditorBrowsable (EditorBrowsableState.Never)]
514                 public virtual bool ValidateChildren ()
515                 {
516                         return ValidateChildren (ValidationConstraints.Selectable);
517                 }
518
519                 [Browsable (false)]
520                 [EditorBrowsable (EditorBrowsableState.Never)]
521                 public virtual bool ValidateChildren (ValidationConstraints validationConstraints)
522                 {
523                         bool recurse = !((validationConstraints & ValidationConstraints.ImmediateChildren) == ValidationConstraints.ImmediateChildren);
524                         
525                         foreach (Control control in Controls)
526                                 if (!ValidateNestedControls (control, validationConstraints, recurse))
527                                         return false;
528
529                         return true;
530                 }
531
532                 bool IContainerControl.ActivateControl(Control control) {
533                         return Select(control);
534                 }
535                 #endregion      // Public Instance Methods
536
537                 #region Protected Instance Methods
538                 [EditorBrowsable (EditorBrowsableState.Advanced)]
539                 protected override void AdjustFormScrollbars(bool displayScrollbars) {
540                         base.AdjustFormScrollbars(displayScrollbars);
541                 }
542
543                 protected override void Dispose(bool disposing) {
544                         base.Dispose(disposing);
545                 }
546
547                 // LAMESPEC This used to be documented, but it's not in code
548                 // and no longer listed in MSDN2
549                 // [EditorBrowsable (EditorBrowsableState.Advanced)]
550                 // protected override void OnControlRemoved(ControlEventArgs e) {
551                 private void OnControlRemoved(object sender, ControlEventArgs e) {
552                         if (e.Control == this.unvalidated_control) {
553                                 this.unvalidated_control = null;
554                         }
555
556                         if (e.Control == this.active_control) {
557                                 this.unvalidated_control = null;
558                         }
559
560                         // base.OnControlRemoved(e);
561                 }
562
563                 protected override void OnCreateControl() {
564                         base.OnCreateControl();
565                         // MS seems to call this here, it gets the whole databinding process started
566                         OnBindingContextChanged (EventArgs.Empty);
567                 }
568
569                 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
570                 {
571                         if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
572                                 return true;
573                                 
574                         return base.ProcessCmdKey (ref msg, keyData);
575                 }
576
577                 [EditorBrowsable (EditorBrowsableState.Advanced)]
578                 protected override bool ProcessDialogChar(char charCode) {
579                         if (GetTopLevel()) {
580                                 if (ProcessMnemonic(charCode)) {
581                                         return true;
582                                 }
583                         }
584                         return base.ProcessDialogChar(charCode);
585                 }
586
587                 protected override bool ProcessDialogKey(Keys keyData) {
588                         Keys    key;
589                         bool    forward;
590
591                         key = keyData & Keys.KeyCode;
592                         forward = true;
593
594                         switch (key) {
595                                 case Keys.Tab: {
596                                         if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) {
597                                                 if (ProcessTabKey ((Control.ModifierKeys & Keys.Shift) == 0)) {
598                                                         return true;
599                                                 }
600                                         }
601                                         break;
602                                 }
603
604                                 case Keys.Left: {
605                                         forward = false;
606                                         goto case Keys.Down;
607                                 }
608
609                                 case Keys.Up: {
610                                         forward = false;
611                                         goto case Keys.Down;
612                                 }
613
614                                 case Keys.Right: {
615                                         goto case Keys.Down;
616                                 }
617                                 case Keys.Down: {
618                                         if (SelectNextControl(active_control, forward, false, false, true)) {
619                                                 return true;
620                                         }
621                                         break;
622                                 }
623
624
625                         }
626                         return base.ProcessDialogKey(keyData);
627                 }
628
629                 protected override bool ProcessMnemonic(char charCode) {
630                         bool    wrapped;
631                         Control c;
632
633                         wrapped = false;
634                         c = active_control;
635
636                         do {
637                                 c = GetNextControl(c, true);
638                                 if (c != null) {
639                                         // This is stupid. I want to be able to call c.ProcessMnemonic directly
640                                         if (c.ProcessControlMnemonic(charCode)) {
641                                                 return(true);
642                                         }
643                                         continue;
644                                 } else {
645                                         if (wrapped) {
646                                                 break;
647                                         }
648                                         wrapped = true;
649                                 }
650                         } while (c != active_control);
651
652                         return false;
653                 }
654
655                 protected virtual bool ProcessTabKey(bool forward) {
656                         return SelectNextControl(active_control, forward, true, true, false);
657                 }
658
659                 protected override void Select(bool directed, bool forward)
660                 {
661                         if (Parent != null) {
662                                 IContainerControl parent = Parent.GetContainerControl ();
663                                 if (parent != null) {
664                                         parent.ActiveControl = this;
665                                 }
666                         }
667
668                         if (directed && auto_select_child) {
669                                 SelectNextControl (null, forward, true, true, false);
670                         }
671                 }
672
673                 protected virtual void UpdateDefaultButton() {
674                         // MS Internal
675                 }
676
677                 [EditorBrowsable (EditorBrowsableState.Advanced)]
678                 protected override void WndProc(ref Message m) {
679                         switch ((Msg) m.Msg) {
680
681                                 case Msg.WM_SETFOCUS:
682                                         if (active_control != null)
683                                                 Select (active_control);
684                                         else
685                                                 base.WndProc (ref m);
686                                 break;
687
688                                 default:
689                                         base.WndProc(ref m);
690                                         break;
691                         }
692                 }
693                 #endregion      // Protected Instance Methods
694
695                 #region Internal Methods
696                 internal void ChildControlRemoved (Control control)
697                 {
698                         ContainerControl top_container = FindForm ();
699                         if (top_container == null)
700                                 top_container = this;
701
702                         // Remove controls -as well as any sub control- that was in the pending validation chain
703                         ArrayList pending_validation_chain = top_container.pending_validation_chain;
704                         if (pending_validation_chain != null) {
705                                 RemoveChildrenFromValidation (pending_validation_chain, control);
706
707                                 if (pending_validation_chain.Count == 0)
708                                         top_container.pending_validation_chain = null;
709                         }
710
711                         if (control == active_control || control.Contains (active_control)) {
712                                 SelectNextControl (this, true, true, true, true);
713                                 if (control == active_control || control.Contains (active_control)) {
714                                         active_control = null;
715                                 }
716                         }
717                 }
718
719                 // Check that this control (or any child) is included in the pending validation chain
720                 bool RemoveChildrenFromValidation (ArrayList validation_chain, Control c)
721                 {
722                         if (RemoveFromValidationChain (validation_chain, c))
723                                 return true;
724
725                         foreach (Control child in c.Controls)
726                                 if (RemoveChildrenFromValidation (validation_chain, child))
727                                         return true;
728
729                         return false;
730                 }
731
732                 // Remove the top most control in the pending validation chain, as well as any children there,
733                 // taking advantage of the fact that the chain is in reverse order of the control's hierarchy
734                 bool RemoveFromValidationChain (ArrayList validation_chain, Control c)
735                 {
736                         int idx = validation_chain.IndexOf (c);
737                         if (idx > -1) {
738                                 pending_validation_chain.RemoveAt (idx--);
739                                 return true;
740                         }
741
742                         return false;
743                 }
744
745                 internal virtual void CheckAcceptButton()
746                 {
747                         // do nothing here, only called if it is a Form
748                 }
749
750                 private bool ValidateNestedControls (Control c, ValidationConstraints constraints, bool recurse)
751                 {
752                         bool validate_result = true;
753
754                         if (!c.CausesValidation)
755                                 validate_result = true;
756                         else if (!ValidateThisControl (c, constraints))
757                                 validate_result = true;
758                         else if (!ValidateControl (c))
759                                 validate_result = false;
760
761                         if (recurse)
762                                 foreach (Control control in c.Controls)
763                                         if (!ValidateNestedControls (control, constraints, recurse))
764                                                 return false;
765
766                         return validate_result;
767                 }
768
769                 private bool ValidateThisControl (Control c, ValidationConstraints constraints)
770                 {
771                         if (constraints == ValidationConstraints.None)
772                                 return true;
773
774                         if ((constraints & ValidationConstraints.Enabled) == ValidationConstraints.Enabled && !c.Enabled)
775                                 return false;
776
777                         if ((constraints & ValidationConstraints.Selectable) == ValidationConstraints.Selectable && !c.GetStyle (ControlStyles.Selectable))
778                                 return false;
779
780                         if ((constraints & ValidationConstraints.TabStop) == ValidationConstraints.TabStop && !c.TabStop)
781                                 return false;
782
783                         if ((constraints & ValidationConstraints.Visible) == ValidationConstraints.Visible && !c.Visible)
784                                 return false;
785
786                         return true;
787                 }
788                 #endregion      // Internal Methods
789
790                 protected override void OnParentChanged (EventArgs e)
791                 {
792                         base.OnParentChanged (e);
793                 }
794
795                 [EditorBrowsable (EditorBrowsableState.Advanced)]
796                 protected override void OnFontChanged (EventArgs e)
797                 {
798                         base.OnFontChanged (e);
799                         
800                         if (AutoScaleMode == AutoScaleMode.Font)
801                                 PerformAutoScale ();
802                 }
803
804                 protected override void OnLayout (LayoutEventArgs e)
805                 {
806                         base.OnLayout (e);
807                 }
808                 
809                 AutoValidate auto_validate = AutoValidate.Inherit;
810
811                 [Browsable (false)]
812                 [AmbientValue (AutoValidate.Inherit)]
813                 [EditorBrowsable (EditorBrowsableState.Never)]
814                 public virtual AutoValidate AutoValidate {
815                         get {
816                                 return auto_validate;
817                         }
818
819                         [MonoTODO("Currently does nothing with the setting")]
820                         set {
821                                 if (auto_validate != value){
822                                         auto_validate = value;
823                                         OnAutoValidateChanged (new EventArgs ());
824                                 }
825                         }
826                 }
827
828                 internal bool ShouldSerializeAutoValidate ()
829                 {
830                         return this.AutoValidate != AutoValidate.Inherit;
831                 }
832
833                 static object OnValidateChanged = new object ();
834
835                 protected virtual void OnAutoValidateChanged (EventArgs e)
836                 {
837                         EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
838                         if (eh != null)
839                                 eh (this, e);
840                 }
841
842                 [Browsable (false)]
843                 [EditorBrowsable (EditorBrowsableState.Never)]
844                 public event EventHandler AutoValidateChanged {
845                         add { Events.AddHandler (OnValidateChanged, value); }
846                         remove { Events.RemoveHandler (OnValidateChanged, value); }
847                 }
848         }
849 }