* ListView.cs: When doing layout calculations don't use a ref
[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 #if NET_2_0
36         [ClassInterface (ClassInterfaceType.AutoDispatch)]
37         [ComVisible (true)]
38 #endif
39         public class ContainerControl : ScrollableControl, IContainerControl {
40                 private Control         active_control;
41                 private Control         unvalidated_control;
42
43                 // This is an internal hack that allows some container controls
44                 // to not auto select their child when they are activated
45                 internal bool           auto_select_child = true;
46 #if NET_2_0
47                 private SizeF           auto_scale_dimensions;
48                 private AutoScaleMode   auto_scale_mode;
49 #endif
50
51                 #region Public Constructors
52                 public ContainerControl() {
53                         active_control = null;
54                         unvalidated_control = null;
55                         ControlRemoved += new ControlEventHandler(OnControlRemoved);
56 #if NET_2_0
57                         auto_scale_dimensions = SizeF.Empty;
58                         auto_scale_mode = AutoScaleMode.None;
59 #endif
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) {
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                                 for (int i = 0; i < validation_chain.Count; i ++) {
138                                         if (!ValidateControl ((Control)validation_chain[i])) {
139                                                 active_control = value = (Control)validation_chain[i];
140                                                 fire_enter = true;
141                                         }
142                                 }
143
144                                 if (fire_enter) {
145                                         walk = value;
146                                         while (walk != root) {
147                                                 chain.Add (walk);
148                                                 walk = walk.Parent;
149                                         }
150
151                                         if (root != null && walk == root && !(root is ContainerControl))
152                                                 chain.Add (walk);
153
154                                         for (int i = chain.Count - 1; i >= 0; i--) {
155                                                 walk = (Control) chain [i];
156                                                 walk.FireEnter ();
157                                         }
158                                 }
159
160                                 walk = this;
161                                 Control ctl = this;
162                                 while (walk != null) {
163                                         if (walk.Parent is ContainerControl) {
164                                                 ((ContainerControl) walk.Parent).active_control = ctl;
165                                                 ctl = walk.Parent;
166                                         }
167                                         walk = walk.Parent;
168                                 }
169
170                                 if (this is Form)
171                                         CheckAcceptButton();
172
173                                 // Scroll control into view
174                                 ScrollControlIntoView(active_control);
175
176                                 // Let the control know it's selected
177                                 SendControlFocus (active_control);
178                         }
179                 }
180
181                 private bool ValidateControl (Control c)
182                 {
183                         CancelEventArgs e = new CancelEventArgs ();
184
185                         c.FireValidating (e);
186
187                         if (e.Cancel)
188                                 return false;
189
190                         c.FireValidated ();
191                         return true;
192                 }
193
194                 private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
195                 {
196                         Control active = container.ActiveControl;
197                         while (active is ContainerControl) {
198                                 if (((ContainerControl)active).ActiveControl == null)
199                                         break;
200                                 active = ((ContainerControl)active).ActiveControl;
201                         }
202                         return active;
203                 }
204
205                 // Just in a separate method to make debugging a little easier,
206                 // should eventually be rolled into ActiveControl setter
207                 private Control GetCommonContainer (Control active_control, Control value)
208                 {
209                         Control new_container = null;
210                         Control prev_container = active_control;
211
212                         while (prev_container != null) {
213                                 new_container = value.Parent;
214                                 while (new_container != null) {
215                                         if (new_container == prev_container)
216                                                 return new_container;
217                                         new_container = new_container.Parent;
218                                 }
219
220                                 prev_container = prev_container.Parent;
221                         }
222
223                         return null;
224                 }
225
226                 internal void SendControlFocus (Control c)
227                 {
228                         if (c.IsHandleCreated) {
229                                 XplatUI.SetFocus (c.window.Handle);
230                         }
231                 }
232
233 #if NET_2_0
234                 [Browsable (false)]
235                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
236                 [EditorBrowsable (EditorBrowsableState.Advanced)]
237                 [Localizable (true)]
238                 public SizeF AutoScaleDimensions {
239                         get {
240                                 return auto_scale_dimensions;
241                         }
242
243                         set {
244                                 if (auto_scale_dimensions != value) {
245                                         auto_scale_dimensions = value;
246                                         
247                                         if (AutoScaleFactor != new SizeF (1, 1))
248                                                 PerformAutoScale ();
249                                 }
250                         }
251                 }
252
253                 protected SizeF AutoScaleFactor {
254                         get {
255                                 if (auto_scale_dimensions.IsEmpty) {
256                                         return new SizeF(1f, 1f);
257                                 }
258                                 return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
259                                         CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
260                         }
261                 }
262
263
264                 [Browsable (false)]
265                 [EditorBrowsable (EditorBrowsableState.Advanced)]
266                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
267                 public AutoScaleMode AutoScaleMode {
268                         get {
269                                 return auto_scale_mode;
270                         }
271                         set {
272                                 if (auto_scale_mode != value) {
273                                         auto_scale_mode = value;
274
275                                         auto_scale_dimensions = CurrentAutoScaleDimensions;
276                                 }
277                         }
278                 }
279 #endif // NET_2_0
280
281                 [Browsable (false)]
282                 public override BindingContext BindingContext {
283                         get {
284                                 if (base.BindingContext == null) {
285                                         base.BindingContext = new BindingContext();
286                                 }
287                                 return base.BindingContext;
288                         }
289
290                         set {
291                                 base.BindingContext = value;
292                         }
293                 }
294
295 #if NET_2_0
296                 [Browsable (false)]
297                 [EditorBrowsable (EditorBrowsableState.Advanced)]
298                 public SizeF CurrentAutoScaleDimensions {
299                         get {
300                                 switch(auto_scale_mode) {
301                                         case AutoScaleMode.Dpi:
302                                                 return TextRenderer.GetDpi ();
303
304                                         case AutoScaleMode.Font:
305                                                 Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font);
306                                                 int width = (int)((float)s.Width / 62f);
307                                                 
308                                                 return new SizeF (width, s.Height);
309                                 }
310
311                                 return auto_scale_dimensions;
312                         }
313                 }
314 #endif
315
316                 [Browsable (false)]
317                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
318                 public Form ParentForm {
319                         get {
320                                 Control parent;
321
322                                 parent = this.Parent;
323
324                                 while (parent != null) {
325                                         if (parent is Form) {
326                                                 return (Form)parent;
327                                         }
328                                         parent = parent.Parent;
329                                 }
330
331                                 return null;
332                         }
333                 }
334                 #endregion      // Public Instance Properties
335
336                 #region Protected Instance Methods
337                 protected override CreateParams CreateParams {
338                         get {
339                                 return base.CreateParams;
340                         }
341                 }
342                 #endregion      // Public Instance Methods
343
344                 #region Public Instance Methods
345 #if NET_2_0
346                 public void PerformAutoScale ()
347                 {
348                         SuspendLayout ();
349                         Scale (AutoScaleFactor);
350                         ResumeLayout (false);
351                         
352                         auto_scale_dimensions = CurrentAutoScaleDimensions;
353                 }
354 #endif
355
356                 [MonoTODO]
357                 static bool ValidateWarned;
358                 public bool Validate() {
359                         //throw new NotImplementedException();
360                         if (!ValidateWarned) {
361                                 Console.WriteLine("ContainerControl.Validate is not yet implemented");
362                                 ValidateWarned = true;
363                         }
364                         return true;
365                 }
366
367                 bool IContainerControl.ActivateControl(Control control) {
368                         return Select(control);
369                 }
370                 #endregion      // Public Instance Methods
371
372                 #region Protected Instance Methods
373                 [EditorBrowsable (EditorBrowsableState.Advanced)]
374                 protected override void AdjustFormScrollbars(bool displayScrollbars) {
375                         base.AdjustFormScrollbars(displayScrollbars);
376                 }
377
378                 protected override void Dispose(bool disposing) {
379                         base.Dispose(disposing);
380                 }
381
382                 // LAMESPEC This used to be documented, but it's not in code
383                 // and no longer listed in MSDN2
384                 // [EditorBrowsable (EditorBrowsableState.Advanced)]
385                 // protected override void OnControlRemoved(ControlEventArgs e) {
386                 private void OnControlRemoved(object sender, ControlEventArgs e) {
387                         if (e.Control == this.unvalidated_control) {
388                                 this.unvalidated_control = null;
389                         }
390
391                         if (e.Control == this.active_control) {
392                                 this.unvalidated_control = null;
393                         }
394
395                         // base.OnControlRemoved(e);
396                 }
397
398                 protected override void OnCreateControl() {
399                         base.OnCreateControl();
400                         // MS seems to call this here, it gets the whole databinding process started
401                         OnBindingContextChanged (EventArgs.Empty);
402                 }
403
404 #if NET_2_0
405                 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
406                 {
407                         if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
408                                 return true;
409                                 
410                         return base.ProcessCmdKey (ref msg, keyData);
411                 }
412 #endif
413
414                 [EditorBrowsable (EditorBrowsableState.Advanced)]
415                 protected override bool ProcessDialogChar(char charCode) {
416                         if (GetTopLevel()) {
417                                 if (ProcessMnemonic(charCode)) {
418                                         return true;
419                                 }
420                         }
421                         return base.ProcessDialogChar(charCode);
422                 }
423
424                 protected override bool ProcessDialogKey(Keys keyData) {
425                         Keys    key;
426                         bool    forward;
427
428                         key = keyData & Keys.KeyCode;
429                         forward = true;
430
431                         switch (key) {
432                                 case Keys.Tab: {
433                                         if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) {
434                                                 if (ProcessTabKey ((Control.ModifierKeys & Keys.Shift) == 0)) {
435                                                         return true;
436                                                 }
437                                         }
438                                         break;
439                                 }
440
441                                 case Keys.Left: {
442                                         forward = false;
443                                         goto case Keys.Down;
444                                 }
445
446                                 case Keys.Up: {
447                                         forward = false;
448                                         goto case Keys.Down;
449                                 }
450
451                                 case Keys.Right: {
452                                         goto case Keys.Down;
453                                 }
454                                 case Keys.Down: {
455                                         if (SelectNextControl(active_control, forward, false, false, true)) {
456                                                 return true;
457                                         }
458                                         break;
459                                 }
460
461
462                         }
463                         return base.ProcessDialogKey(keyData);
464                 }
465
466                 protected override bool ProcessMnemonic(char charCode) {
467                         bool    wrapped;
468                         Control c;
469
470                         wrapped = false;
471                         c = active_control;
472
473                         do {
474                                 c = GetNextControl(c, true);
475                                 if (c != null) {
476                                         // This is stupid. I want to be able to call c.ProcessMnemonic directly
477                                         if (c.ProcessControlMnemonic(charCode)) {
478                                                 return(true);
479                                         }
480                                         continue;
481                                 } else {
482                                         if (wrapped) {
483                                                 break;
484                                         }
485                                         wrapped = true;
486                                 }
487                         } while (c != active_control);
488
489                         return false;
490                 }
491
492                 protected virtual bool ProcessTabKey(bool forward) {
493                         return SelectNextControl(active_control, forward, true, true, false);
494                 }
495
496                 protected override void Select(bool directed, bool forward)
497                 {
498                         if (Parent != null) {
499                                 IContainerControl parent = Parent.GetContainerControl ();
500                                 if (parent != null) {
501                                         parent.ActiveControl = this;
502                                 }
503                         }
504
505                         if (directed && auto_select_child) {
506                                 SelectNextControl (null, forward, true, true, false);
507                         }
508                 }
509
510                 protected virtual void UpdateDefaultButton() {
511                         // MS Internal
512                 }
513
514                 [EditorBrowsable (EditorBrowsableState.Advanced)]
515                 protected override void WndProc(ref Message m) {
516                         switch ((Msg) m.Msg) {
517
518                                 case Msg.WM_SETFOCUS:
519                                         if (active_control != null)
520                                                 Select (active_control);
521                                         else
522                                                 base.WndProc (ref m);
523 #if false
524                                         else
525                                                 SelectNextControl (null, true, true, true, false);
526 #endif
527                                 break;
528
529                                 default:
530                                         base.WndProc(ref m);
531                                         break;
532                         }
533                 }
534                 #endregion      // Protected Instance Methods
535
536                 #region Internal Methods
537                 internal void ChildControlRemoved (Control control)
538                 {
539                         if (control == active_control) {
540                                 SelectNextControl (this, true, true, true, true);
541                                 if (control == active_control) {
542                                         active_control = null;
543                                 }
544                         }
545                 }
546                 internal virtual void CheckAcceptButton()
547                 {
548                         // do nothing here, only called if it is a Form
549                 }
550                 #endregion      // Internal Methods
551
552 #if NET_2_0
553                 protected override void OnParentChanged (EventArgs e)
554                 {
555                         base.OnParentChanged (e);
556                 }
557
558                 [EditorBrowsable (EditorBrowsableState.Advanced)]
559                 protected override void OnFontChanged (EventArgs e)
560                 {
561                         base.OnFontChanged (e);
562                         
563                         if (AutoScaleMode == AutoScaleMode.Font)
564                                 PerformAutoScale ();
565                 }
566
567                 protected override void OnLayout (LayoutEventArgs levent)
568                 {
569                         base.OnLayout (levent);
570                 }
571                 
572                 AutoValidate auto_validate = AutoValidate.Inherit;
573
574                 [Browsable (false)]
575                 [AmbientValue (AutoValidate.Inherit)]
576                 [EditorBrowsable (EditorBrowsableState.Never)]
577                 public virtual AutoValidate AutoValidate {
578                         get {
579                                 return auto_validate;
580                         }
581
582                         [MonoTODO("Currently does nothing with the setting")]
583                         set {
584                                 if (auto_validate != value){
585                                         auto_validate = value;
586                                         OnAutoValidateChanged (new EventArgs ());
587                                 }
588                         }
589                 }
590
591                 internal bool ShouldSerializeAutoValidate ()
592                 {
593                         return this.AutoValidate != AutoValidate.Inherit;
594                 }
595
596                 static object OnValidateChanged = new object ();
597
598                 protected virtual void OnAutoValidateChanged (EventArgs e)
599                 {
600                         EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
601                         if (eh != null)
602                                 eh (this, e);
603                 }
604
605                 [Browsable (false)]
606                 [EditorBrowsable (EditorBrowsableState.Never)]
607                 public event EventHandler AutoValidateChanged {
608                         add { Events.AddHandler (OnValidateChanged, value); }
609                         remove { Events.RemoveHandler (OnValidateChanged, value); }
610                 }
611 #endif
612         }
613 }