deac1d5644d6adc0055d5876a9510d3b5159c9a9
[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                                 if (form != null) {
83                                         Control active = GetMostDeeplyNestedActiveControl (form);
84                                         Control common_container = GetCommonContainer (active, value);
85                                         ArrayList chain = new ArrayList ();
86                                         ArrayList validation_chain = new ArrayList ();
87                                         Control walk = active;
88
89                                         // we split this up into three steps:
90                                         //    1. walk up the tree (if we need to) to our root, firing leave events.
91                                         //    2. validate.
92                                         //    3. walk down the tree (if we need to), firing enter events.
93
94                                         // "our root" is either the common container of the current active
95                                         // control and the new active control, or the current active control,
96                                         // or the new active control.  That is, it's either one of these three
97                                         // configurations:
98
99                                         //  (1)     root            (2)  new          (3)  current
100                                         //          /  \                /   \               /   \
101                                         //        ...   ...           ...   ...           ...   ...
102                                         //        /      \            /                           \
103                                         //     current   new       current                        new
104
105
106                                         // note (3) doesn't require any upward walking, and no leave events are generated.
107                                         //      (2) doesn't require any downward walking, and no enter events are generated.
108
109                                         // as we walk up the tree, we generate a list of all controls which cause
110                                         // validation.  After firing the leave events, we invoke (in order starting from
111                                         // the most deeply nested) their Validating event.  If one sets CancelEventArgs.Cancel
112                                         // to true, we ignore the control the user wanted to set ActiveControl to, and use
113                                         // the Validating control instead.
114
115                                         bool fire_enter = true;
116                                         Control root = common_container;
117
118                                         // Generate the leave messages
119                                         while (walk != common_container) {
120                                                 if (walk == value) {
121                                                         root = value;
122                                                         fire_enter = false;
123                                                         break;
124                                                 }
125                                                 walk.FireLeave ();
126                                                 /* clear our idea of the active control as we go back up */
127                                                 if (walk is ContainerControl)
128                                                         ((ContainerControl)walk).active_control = null;
129
130                                                 if (walk.CausesValidation)
131                                                         validation_chain.Add (walk);
132
133                                                 walk = walk.Parent;
134                                         }
135
136                                         for (int i = 0; i < validation_chain.Count; i ++) {
137                                                 if (!ValidateControl ((Control)validation_chain[i])) {
138                                                         value = (Control)validation_chain[i];
139                                                         fire_enter = true;
140                                                 }
141                                         }
142
143                                         if (fire_enter) {
144                                                 walk = value;
145                                                 while (walk != root) {
146                                                         chain.Add (walk);
147                                                         walk = walk.Parent;
148                                                 }
149
150                                                 for (int i = chain.Count - 1; i >= 0; i--) {
151                                                         walk = (Control) chain [i];
152                                                         walk.FireEnter ();
153                                                 }
154                                         }
155
156                                         walk = this;
157                                         Control ctl = this;
158                                         while (walk != null) {
159                                                 if (walk.Parent is ContainerControl) {
160                                                         ((ContainerControl) walk.Parent).active_control = ctl;
161                                                         ctl = walk.Parent;
162                                                 }
163                                                 walk = walk.Parent;
164                                         }
165                                 }
166
167                                 active_control = value;
168
169                                 if (this is Form)
170                                         CheckAcceptButton();
171
172                                 // Scroll control into view
173                                 ScrollControlIntoView(active_control);
174
175                                 // Let the control know it's selected
176                                 SendControlFocus (value);
177                         }
178                 }
179
180                 private bool ValidateControl (Control c)
181                 {
182                         CancelEventArgs e = new CancelEventArgs ();
183
184                         c.FireValidating (e);
185
186                         if (e.Cancel)
187                                 return false;
188
189                         c.FireValidated ();
190                         return true;
191                 }
192
193                 private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
194                 {
195                         Control active = container.ActiveControl;
196                         while (active is ContainerControl) {
197                                 if (((ContainerControl)active).ActiveControl == null)
198                                         break;
199                                 active = ((ContainerControl)active).ActiveControl;
200                         }
201                         return active;
202                 }
203
204                 // Just in a separate method to make debugging a little easier,
205                 // should eventually be rolled into ActiveControl setter
206                 private Control GetCommonContainer (Control active_control, Control value)
207                 {
208                         Control new_container = null;
209                         Control prev_container = active_control;
210
211                         while (prev_container != null) {
212                                 new_container = value.Parent;
213                                 while (new_container != null) {
214                                         if (new_container == prev_container)
215                                                 return new_container;
216                                         new_container = new_container.Parent;
217                                 }
218
219                                 prev_container = prev_container.Parent;
220                         }
221
222                         return null;
223                 }
224
225                 internal void SendControlFocus (Control c)
226                 {
227                         if (c.IsHandleCreated) {
228                                 XplatUI.SetFocus (c.window.Handle);
229                         }
230                 }
231
232 #if NET_2_0
233                 [Browsable (false)]
234                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
235                 [EditorBrowsable (EditorBrowsableState.Advanced)]
236                 [Localizable (true)]
237                 public SizeF AutoScaleDimensions {
238                         get {
239                                 return auto_scale_dimensions;
240                         }
241
242                         set {
243                                 auto_scale_dimensions = value;
244                         }
245                 }
246
247                 protected SizeF AutoScaleFactor {
248                         get {
249                                 if (auto_scale_dimensions.IsEmpty) {
250                                         return new SizeF(1f, 1f);
251                                 }
252                                 return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
253                                         CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
254                         }
255                 }
256
257
258                 [MonoTODO("Call scaling method")]
259                 [Browsable (false)]
260                 [EditorBrowsable (EditorBrowsableState.Advanced)]
261                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
262                 public AutoScaleMode AutoScaleMode {
263                         get {
264                                 return auto_scale_mode;
265                         }
266                         set {
267                                 if (auto_scale_mode != value) {
268                                         auto_scale_mode = value;
269
270                                         // Trigger scaling
271                                 }
272                         }
273                 }
274 #endif // NET_2_0
275
276                 [Browsable (false)]
277                 public override BindingContext BindingContext {
278                         get {
279                                 if (base.BindingContext == null) {
280                                         base.BindingContext = new BindingContext();
281                                 }
282                                 return base.BindingContext;
283                         }
284
285                         set {
286                                 base.BindingContext = value;
287                         }
288                 }
289
290 #if NET_2_0
291                 [MonoTODO("Revisit when System.Drawing.GDI.WindowsGraphics.GetTextMetrics is done or come up with other cross-plat avg. font width calc method")]
292                 [Browsable (false)]
293                 [EditorBrowsable (EditorBrowsableState.Advanced)]
294                 public SizeF CurrentAutoScaleDimensions {
295                         get {
296                                 switch(auto_scale_mode) {
297                                         case AutoScaleMode.Dpi: {
298                                                 Bitmap          bmp;
299                                                 Graphics        g;
300                                                 SizeF           size;
301
302                                                 bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
303                                                 g = Graphics.FromImage(bmp);
304                                                 size = new SizeF(g.DpiX, g.DpiY);
305                                                 g.Dispose();
306                                                 bmp.Dispose();
307                                                 return size;
308                                         }
309
310                                         case AutoScaleMode.Font: {
311                                                 // http://msdn2.microsoft.com/en-us/library/system.windows.forms.containercontrol.currentautoscaledimensions(VS.80).aspx
312                                                 // Implement System.Drawing.GDI.WindowsGraphics.GetTextMetrics first...
313                                                 break;
314                                         }
315                                 }
316
317                                 return auto_scale_dimensions;
318                         }
319                 }
320 #endif
321
322                 [Browsable (false)]
323                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
324                 public Form ParentForm {
325                         get {
326                                 Control parent;
327
328                                 parent = this.Parent;
329
330                                 while (parent != null) {
331                                         if (parent is Form) {
332                                                 return (Form)parent;
333                                         }
334                                         parent = parent.Parent;
335                                 }
336
337                                 return null;
338                         }
339                 }
340                 #endregion      // Public Instance Properties
341
342                 #region Protected Instance Methods
343                 protected override CreateParams CreateParams {
344                         get {
345                                 return base.CreateParams;
346                         }
347                 }
348                 #endregion      // Public Instance Methods
349
350                 #region Public Instance Methods
351                 [MonoTODO]
352                 static bool ValidateWarned;
353                 public bool Validate() {
354                         //throw new NotImplementedException();
355                         if (!ValidateWarned) {
356                                 Console.WriteLine("ContainerControl.Validate is not yet implemented");
357                                 ValidateWarned = true;
358                         }
359                         return true;
360                 }
361
362                 bool IContainerControl.ActivateControl(Control control) {
363                         return Select(control);
364                 }
365                 #endregion      // Public Instance Methods
366
367                 #region Protected Instance Methods
368                 [EditorBrowsable (EditorBrowsableState.Advanced)]
369                 protected override void AdjustFormScrollbars(bool displayScrollbars) {
370                         base.AdjustFormScrollbars(displayScrollbars);
371                 }
372
373                 protected override void Dispose(bool disposing) {
374                         base.Dispose(disposing);
375                 }
376
377                 // LAMESPEC This used to be documented, but it's not in code
378                 // and no longer listed in MSDN2
379                 // [EditorBrowsable (EditorBrowsableState.Advanced)]
380                 // protected override void OnControlRemoved(ControlEventArgs e) {
381                 private void OnControlRemoved(object sender, ControlEventArgs e) {
382                         if (e.Control == this.unvalidated_control) {
383                                 this.unvalidated_control = null;
384                         }
385
386                         if (e.Control == this.active_control) {
387                                 this.unvalidated_control = null;
388                         }
389
390                         // base.OnControlRemoved(e);
391                 }
392
393                 protected override void OnCreateControl() {
394                         base.OnCreateControl();
395                         // MS seems to call this here, it gets the whole databinding process started
396                         OnBindingContextChanged (EventArgs.Empty);
397                 }
398
399 #if NET_2_0
400                 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
401                 {
402                         if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
403                                 return true;
404                                 
405                         return base.ProcessCmdKey (ref msg, keyData);
406                 }
407 #endif
408
409                 [EditorBrowsable (EditorBrowsableState.Advanced)]
410                 protected override bool ProcessDialogChar(char charCode) {
411                         if (GetTopLevel()) {
412                                 if (ProcessMnemonic(charCode)) {
413                                         return true;
414                                 }
415                         }
416                         return base.ProcessDialogChar(charCode);
417                 }
418
419                 protected override bool ProcessDialogKey(Keys keyData) {
420                         Keys    key;
421                         bool    forward;
422
423                         key = keyData & Keys.KeyCode;
424                         forward = true;
425
426                         switch (key) {
427                                 case Keys.Tab: {
428                                         if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) {
429                                                 if (ProcessTabKey ((Control.ModifierKeys & Keys.Shift) == 0)) {
430                                                         return true;
431                                                 }
432                                         }
433                                         break;
434                                 }
435
436                                 case Keys.Left: {
437                                         forward = false;
438                                         goto case Keys.Down;
439                                 }
440
441                                 case Keys.Up: {
442                                         forward = false;
443                                         goto case Keys.Down;
444                                 }
445
446                                 case Keys.Right: {
447                                         goto case Keys.Down;
448                                 }
449                                 case Keys.Down: {
450                                         if (SelectNextControl(active_control, forward, false, false, true)) {
451                                                 return true;
452                                         }
453                                         break;
454                                 }
455
456
457                         }
458                         return base.ProcessDialogKey(keyData);
459                 }
460
461                 protected override bool ProcessMnemonic(char charCode) {
462                         bool    wrapped;
463                         Control c;
464
465                         wrapped = false;
466                         c = active_control;
467
468                         do {
469                                 c = GetNextControl(c, true);
470                                 if (c != null) {
471                                         // This is stupid. I want to be able to call c.ProcessMnemonic directly
472                                         if (c.CanSelect && c.ProcessControlMnemonic(charCode)) {
473                                                 return(true);
474                                         }
475                                         continue;
476                                 } else {
477                                         if (wrapped) {
478                                                 break;
479                                         }
480                                         wrapped = true;
481                                 }
482                         } while (c != active_control);
483
484                         return false;
485                 }
486
487                 protected virtual bool ProcessTabKey(bool forward) {
488                         return SelectNextControl(active_control, forward, true, true, false);
489                 }
490
491                 protected override void Select(bool directed, bool forward)
492                 {
493                         if (Parent != null) {
494                                 IContainerControl parent = Parent.GetContainerControl ();
495                                 if (parent != null) {
496                                         parent.ActiveControl = this;
497                                 }
498                         }
499
500                         if (directed && auto_select_child) {
501                                 SelectNextControl (null, forward, true, true, false);
502                         }
503                 }
504
505                 protected virtual void UpdateDefaultButton() {
506                         // MS Internal
507                 }
508
509                 [EditorBrowsable (EditorBrowsableState.Advanced)]
510                 protected override void WndProc(ref Message m) {
511                         switch ((Msg) m.Msg) {
512
513                                 case Msg.WM_SETFOCUS:
514                                         if (active_control != null)
515                                                 Select (active_control);
516                                         else
517                                                 base.WndProc (ref m);
518 #if false
519                                         else
520                                                 SelectNextControl (null, true, true, true, false);
521 #endif
522                                 break;
523
524                                 default:
525                                         base.WndProc(ref m);
526                                         break;
527                         }
528                 }
529                 #endregion      // Protected Instance Methods
530
531                 #region Internal Methods
532                 internal void ChildControlRemoved (Control control)
533                 {
534                         if (control == active_control) {
535                                 SelectNextControl (this, true, true, true, true);
536                                 if (control == active_control) {
537                                         active_control = null;
538                                 }
539                         }
540                 }
541                 internal virtual void CheckAcceptButton()
542                 {
543                         // do nothing here, only called if it is a Form
544                 }
545                 #endregion      // Internal Methods
546
547 #if NET_2_0
548                 protected override void OnParentChanged (EventArgs e)
549                 {
550                         base.OnParentChanged (e);
551                 }
552
553                 [EditorBrowsable (EditorBrowsableState.Advanced)]
554                 protected override void OnFontChanged (EventArgs e)
555                 {
556                         base.OnFontChanged (e);
557                 }
558
559                 protected override void OnLayout (LayoutEventArgs levent)
560                 {
561                         base.OnLayout (levent);
562                 }
563                 
564                 AutoValidate auto_validate = AutoValidate.Inherit;
565
566                 [Browsable (false)]
567                 [AmbientValue (AutoValidate.Inherit)]
568                 [EditorBrowsable (EditorBrowsableState.Never)]
569                 public virtual AutoValidate AutoValidate {
570                         get {
571                                 return auto_validate;
572                         }
573
574                         [MonoTODO("Currently does nothing with the setting")]
575                         set {
576                                 if (auto_validate != value){
577                                         auto_validate = value;
578                                         OnAutoValidateChanged (new EventArgs ());
579                                 }
580                         }
581                 }
582
583                 static object OnValidateChanged = new object ();
584
585                 protected virtual void OnAutoValidateChanged (EventArgs e)
586                 {
587                         EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
588                         if (eh != null)
589                                 eh (this, e);
590                 }
591
592                 [Browsable (false)]
593                 [EditorBrowsable (EditorBrowsableState.Never)]
594                 public event EventHandler AutoValidateChanged {
595                         add { Events.AddHandler (OnValidateChanged, value); }
596                         remove { Events.RemoveHandler (OnValidateChanged, value); }
597                 }
598 #endif
599         }
600 }