248d05b777e92691252aecea90db346d9e90a8ca
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / MdiClient.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) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 // NOT COMPLETE
28
29 using System.Collections;
30 using System.ComponentModel;
31 using System.Drawing;
32 using System.Runtime.InteropServices;
33
34 namespace System.Windows.Forms {
35         [DesignTimeVisible(false)]
36         [ToolboxItem(false)]
37         public sealed class MdiClient : Control {
38                 #region Local Variables
39                 private int mdi_created;
40                 private ImplicitHScrollBar hbar;
41                 private ImplicitVScrollBar vbar;
42                 private SizeGrip sizegrip;
43                 private int hbar_value;
44                 private int vbar_value;
45                 private bool lock_sizing;
46                 private bool initializing_scrollbars;
47                 private int prev_bottom;
48                 private bool setting_windowstates = false;
49                 internal ArrayList mdi_child_list;
50                 private string form_text;
51                 private bool setting_form_text;
52                 private Form active_child;
53
54                 #endregion      // Local Variables
55
56                 #region Public Classes
57                 public new class ControlCollection : Control.ControlCollection {
58
59                         private MdiClient owner;
60                         
61                         public ControlCollection(MdiClient owner) : base(owner) {
62                                 this.owner = owner;
63                         }
64
65                         public override void Add(Control value) {
66                                 if ((value is Form) == false || !(((Form)value).IsMdiChild)) {
67                                         throw new ArgumentException("Form must be MdiChild");
68                                 }
69                                 owner.mdi_child_list.Add (value);
70                                 base.Add (value);
71
72                                 // newest member is the active one
73                                 Form form = (Form) value;
74                                 owner.ActiveMdiChild = form;
75                         }
76
77                         public override void Remove(Control value)
78                         {
79                                 Form form = value as Form;
80                                 if (form != null) {
81                                         MdiWindowManager wm = form.WindowManager as MdiWindowManager;
82                                         if (wm != null) {
83                                                 form.Closed -= wm.form_closed_handler;
84                                         }
85                                 }
86
87                                 owner.mdi_child_list.Remove (value);
88                                 base.Remove (value);
89                         }
90                 }
91                 #endregion      // Public Classes
92
93                 #region Public Constructors
94                 public MdiClient()
95                 {
96                         mdi_child_list = new ArrayList ();
97                         BackColor = SystemColors.AppWorkspace;
98                         Dock = DockStyle.Fill;
99                         SetStyle (ControlStyles.Selectable, false);
100                 }
101                 #endregion      // Public Constructors
102
103
104                 internal void SetParentText(bool text_changed)
105                 {
106                         if (setting_form_text)
107                                 return;
108
109                         setting_form_text = true;
110
111                         if (text_changed)
112                                 form_text = ParentForm.Text;
113
114                         if (ParentForm.ActiveMaximizedMdiChild == null) {
115                                 ParentForm.Text = form_text;
116                         } else {
117                                 string childText = ParentForm.ActiveMaximizedMdiChild.form.Text;
118                                 if (childText.Length > 0) {
119                                         ParentForm.Text = form_text + " - [" + ParentForm.ActiveMaximizedMdiChild.form.Text + "]";
120                                 } else {
121                                         ParentForm.Text = form_text;
122                                 }
123                         }
124
125                         setting_form_text = false;
126                 }
127
128                 internal override void OnPaintBackgroundInternal (PaintEventArgs pe)
129                 {
130                         if (BackgroundImage != null)
131                                 return;
132
133                         if (Parent == null || Parent.BackgroundImage == null)
134                                 return;
135                         Parent.PaintControlBackground (pe);
136                 }
137
138                 internal Form ParentForm {
139                         get { return (Form) Parent; }
140                 }
141
142                 protected override Control.ControlCollection CreateControlsInstance ()
143                 {
144                         return new MdiClient.ControlCollection (this);
145                 }
146
147                 protected override void WndProc(ref Message m) {
148                         /*
149                         switch ((Msg) m.Msg) {
150                                 case Msg.WM_PAINT: {                            
151                                         Console.WriteLine ("ignoring paint");
152                                         return;
153                                 }
154                         }
155                         */
156                         switch ((Msg)m.Msg) {
157                         case Msg.WM_NCCALCSIZE:
158                                 XplatUIWin32.NCCALCSIZE_PARAMS  ncp;
159
160                                 if (m.WParam == (IntPtr) 1) {
161                                         ncp = (XplatUIWin32.NCCALCSIZE_PARAMS) Marshal.PtrToStructure (m.LParam,
162                                                         typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
163
164                                         int bw = 2;
165
166                                         ncp.rgrc1.top += bw;
167                                         ncp.rgrc1.bottom -= bw;
168                                         ncp.rgrc1.left += bw;
169                                         ncp.rgrc1.right -= bw;
170                                         
171                                         Marshal.StructureToPtr (ncp, m.LParam, true);
172                                 }
173
174                                 break;
175
176                         case Msg.WM_NCPAINT:
177                                 PaintEventArgs pe = XplatUI.PaintEventStart (Handle, false);
178
179                                 Rectangle clip;
180                                 clip = new Rectangle (0, 0, Width, Height);
181
182                                 ControlPaint.DrawBorder3D (pe.Graphics, clip, Border3DStyle.Sunken);
183                                 XplatUI.PaintEventEnd (Handle, false);
184                                 m.Result = IntPtr.Zero;
185                                 return ;
186                         }
187
188                         base.WndProc (ref m);
189                 }
190
191                 protected override void OnResize (EventArgs e)
192                 {
193                         base.OnResize (e);
194
195                         if (Parent != null)
196                                 XplatUI.InvalidateNC (Parent.Handle);
197                         // Should probably make this into one loop
198                         SizeScrollBars ();
199                         ArrangeWindows ();
200                 }
201
202                 protected override void ScaleCore (float dx, float dy)
203                 {
204                         base.ScaleCore (dx, dy);
205                 }
206
207                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
208                 {
209                         base.SetBoundsCore (x, y, width, height, specified);
210                 }
211
212                 #region Public Instance Properties
213                 [Localizable(true)]
214                 public override System.Drawing.Image BackgroundImage {
215                         get {
216                                 return base.BackgroundImage;
217                         }
218                         set {
219                                 base.BackgroundImage = value;
220                         }
221                 }
222
223                 public Form [] MdiChildren {
224                         get {
225                                 if (mdi_child_list == null)
226                                         return new Form [0];
227                                 return (Form []) mdi_child_list.ToArray (typeof (Form));
228                         }
229                 }
230                 #endregion      // Public Instance Properties
231
232 #region Protected Instance Properties
233                 protected override CreateParams CreateParams {
234                         get {
235                                 return base.CreateParams;
236                         }
237                 }
238                 #endregion      // Protected Instance Properties
239
240                 #region Public Instance Methods
241                 public void LayoutMdi (MdiLayout value) {
242
243                         int max_width = Int32.MaxValue;
244                         int max_height = Int32.MaxValue;
245
246                         if (Parent != null) {
247                                 max_width = Parent.Width;
248                                 max_height = Parent.Height;
249                         }
250
251                         switch (value) {
252                         case MdiLayout.Cascade:
253                                 int i = 0;
254                                 for (int c = Controls.Count - 1; c >= 0; c--) {
255                                         Form form = (Form) Controls [c];
256
257                                         int l = 22 * i;
258                                         int t = 22 * i;
259
260                                         if (i != 0 && (l + form.Width > max_width || t + form.Height > max_height)) {
261                                                 i = 0;
262                                                 l = 22 * i;
263                                                 t = 22 * i;
264                                         }
265
266                                         form.Left = l;
267                                         form.Top = t;
268
269                                         i++;
270                                 }
271                                 break;
272                         default:
273                                 throw new NotImplementedException();
274                         }
275                 }
276                 #endregion      // Public Instance Methods
277
278                 #region Protected Instance Methods
279                 #endregion      // Protected Instance Methods
280
281                 internal void SizeScrollBars ()
282                 {
283                         if (lock_sizing)
284                                 return;
285
286                         if (Controls.Count == 0 || ((Form) Controls [0]).WindowState == FormWindowState.Maximized) {
287                                 if (hbar != null)
288                                         hbar.Visible = false;
289                                 if (vbar != null)
290                                         vbar.Visible = false;
291                                 if (sizegrip != null)
292                                         sizegrip.Visible = false;
293                                 return;
294                         }
295                                 
296                         bool hbar_required = false;
297                         bool vbar_required = false;
298
299                         int right = 0;
300                         int left = 0;
301                         int top = 0;
302                         int bottom = 0;
303
304                         foreach (Form child in Controls) {
305                                 if (!child.Visible)
306                                         continue;
307                                 if (child.Right > right)
308                                         right = child.Right;
309                                 if (child.Left < left) {
310                                         hbar_required = true;
311                                         left = child.Left;
312                                 }
313                                 
314                                 if (child.Bottom > bottom)
315                                         bottom = child.Bottom;
316                                 if (child.Top < 0) {
317                                         vbar_required = true;
318                                         top = child.Top;
319                                 }
320                         }
321
322                         int first_right = Width;
323                         int first_bottom = Height;
324                         int right_edge = first_right;
325                         int bottom_edge = first_bottom;
326                         int prev_right_edge;
327                         int prev_bottom_edge;
328
329                         bool need_hbar = false;
330                         bool need_vbar = false;
331
332                         do {
333                                 prev_right_edge = right_edge;
334                                 prev_bottom_edge = bottom_edge;
335
336                                 if (hbar_required || right > right_edge) {
337                                         need_hbar = true;
338                                         bottom_edge = first_bottom - SystemInformation.HorizontalScrollBarHeight;
339                                 } else {
340                                         need_hbar = false;
341                                         bottom_edge = first_bottom;
342                                 }
343
344                                 if (vbar_required || bottom > bottom_edge) {
345                                         need_vbar = true;
346                                         right_edge = first_right - SystemInformation.VerticalScrollBarWidth;
347                                 } else {
348                                         need_vbar = false;
349                                         right_edge = first_right;
350                                 }
351
352                         } while (right_edge != prev_right_edge || bottom_edge != prev_bottom_edge);
353
354                         if (need_hbar) {
355                                 if (hbar == null) {
356                                         hbar = new ImplicitHScrollBar ();
357                                         Controls.AddImplicit (hbar);
358                                 }
359                                 hbar.Visible = true;
360                                 CalcHBar (left, right, right_edge, need_vbar);
361                         } else if (hbar != null)
362                                 hbar.Visible = false;
363
364                         if (need_vbar) {
365                                 if (vbar == null) {
366                                         vbar = new ImplicitVScrollBar ();
367                                         Controls.AddImplicit (vbar);
368                                 }
369                                 vbar.Visible = true;
370                                 CalcVBar (top, bottom, bottom_edge, need_hbar);
371                         } else if (vbar != null)
372                                 vbar.Visible = false;
373
374                         if (need_hbar && need_vbar) {
375                                 if (sizegrip == null) {
376                                         sizegrip = new SizeGrip ();
377                                         Controls.AddImplicit (sizegrip);
378                                 }
379                                 sizegrip.Location = new Point (hbar.Right, vbar.Bottom);
380                                 sizegrip.Width = vbar.Width;
381                                 sizegrip.Height = hbar.Height;
382                                 sizegrip.Visible = true;
383                         } else if (sizegrip != null) {
384                                 sizegrip.Visible = false;
385                         }
386                 }
387
388                 private void CalcHBar (int left, int right, int right_edge, bool vert_vis)
389                 {
390                         initializing_scrollbars = true;
391                         int virtual_left = Math.Min (left, 0);
392                         int virtual_right = Math.Max (right, right_edge);
393                         int diff = (virtual_right - virtual_left) - right_edge;
394
395                         hbar.Left = 0;
396                         hbar.Top = ClientRectangle.Bottom - hbar.Height;
397                         hbar.Width = ClientRectangle.Width - (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0);
398                         hbar.LargeChange = 50;
399                         hbar.Maximum = diff + 51 + (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0);
400                         hbar.Value = -virtual_left;
401                         hbar.ValueChanged += new EventHandler (HBarValueChanged);
402                         XplatUI.SetZOrder (hbar.Handle, IntPtr.Zero, true, false);
403                         initializing_scrollbars = false;
404                 }
405
406                 private void CalcVBar (int top, int bottom, int bottom_edge, bool horz_vis)
407                 {
408                         initializing_scrollbars = true;
409                         int virtual_top = Math.Min (top, 0);
410                         int virtual_bottom = Math.Max (bottom, bottom_edge);
411                         int diff = (virtual_bottom - virtual_top) - bottom_edge;
412                         
413                         vbar.Top = 0;
414                         vbar.Left = ClientRectangle.Right - vbar.Width;
415                         vbar.Height = ClientRectangle.Height - (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0);
416                         vbar.LargeChange = 50;
417                         vbar.Minimum = virtual_top;
418                         vbar.Maximum = diff + 51 + (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0);
419                         vbar.ValueChanged += new EventHandler (VBarValueChanged);
420                         XplatUI.SetZOrder (vbar.Handle, IntPtr.Zero, true, false);
421                         initializing_scrollbars = false;
422                 }
423
424                 private void HBarValueChanged (object sender, EventArgs e)
425                 {
426                         if (initializing_scrollbars)
427                                 return;
428                         
429                         if (hbar.Value == hbar_value)
430                                 return;
431
432                         lock_sizing = true;
433
434                         try {
435                                 foreach (Form child in Controls) {
436                                         child.Left += hbar_value - hbar.Value;
437                                 }
438                         } finally {
439                                 lock_sizing = false;
440                         }
441
442                         hbar_value = hbar.Value;
443                         lock_sizing = false;
444                 }
445
446                 private void VBarValueChanged (object sender, EventArgs e)
447                 {
448                         if (initializing_scrollbars)
449                                 return;
450                                 
451                         if (vbar.Value == vbar_value)
452                                 return;
453
454                         lock_sizing = true;
455
456                         try {
457                                 foreach (Form child in Controls) {
458                                         child.Top += vbar_value - vbar.Value;
459                                 }
460                         } finally {
461                                 lock_sizing = false;
462                         }
463
464                         vbar_value = vbar.Value;
465                         lock_sizing = false;
466                 }
467
468                 private void ArrangeWindows ()
469                 {
470                         int change = 0;
471                         if (prev_bottom != -1)
472                                 change = Bottom - prev_bottom;
473
474                         foreach (Control c in Controls) {
475                                 Form child = c as Form;
476
477                                 if (c == null || !child.Visible)
478                                         continue;
479
480                                 MdiWindowManager wm = child.WindowManager as MdiWindowManager;
481                                 if (wm.GetWindowState () == FormWindowState.Maximized)
482                                         wm.SizeMaximized ();
483
484                                 if (wm.GetWindowState () == FormWindowState.Minimized) {
485                                         child.Top += change;
486                                 }
487                                         
488                         }
489
490                         prev_bottom = Bottom;
491                 }
492
493                 internal void ArrangeIconicWindows ()
494                 {
495                         int xspacing = 160;
496                         int yspacing = 25;
497
498                         Rectangle rect = new Rectangle (0, 0, xspacing, yspacing);
499
500                         lock_sizing = true;
501                         foreach (Form form in Controls) {
502                                 if (form.WindowState != FormWindowState.Minimized)
503                                         continue;
504
505                                 MdiWindowManager wm = (MdiWindowManager) form.WindowManager;
506                                 
507                                 if (wm.IconicBounds != Rectangle.Empty) {
508                                         if (form.Bounds != wm.IconicBounds)
509                                                 form.Bounds = wm.IconicBounds;
510                                         continue;
511                                 }
512                                 
513                                 // Need to get the width in the loop cause some themes might have
514                                 // different widths for different styles
515                                 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (wm);
516                                 
517                                 // The extra one pixel is a cheap hack for now until we
518                                 // handle 0 client sizes properly in the driver
519                                 int height = wm.TitleBarHeight + (bw * 2) + 1;
520                                 
521                                 bool success = true;
522                                 int startx, starty, currentx, currenty;
523                                 
524                                 startx = 0;
525                                 starty = Bottom - yspacing - 1;
526                                 if (this.hbar != null && this.hbar.Visible)
527                                         starty -= this.hbar.Height;
528                                 currentx = startx;
529                                 currenty = starty;
530                                 
531                                 do {
532                                         rect.X = currentx;
533                                         rect.Y = currenty;
534                                         rect.Height = height;
535                                         success = true;
536                                         foreach (Form form2 in Controls) {
537                                                 if (form2 == form || form2.window_state != FormWindowState.Minimized)
538                                                         continue;
539                                                 
540                                                 if (form2.Bounds.IntersectsWith(rect)) {
541                                                         success = false;
542                                                         break;
543                                                 }
544                                         }
545                                         if (!success) { 
546                                                 currentx += xspacing;
547                                                 if (currentx + xspacing > Right) {
548                                                         currentx = startx;
549                                                         currenty -= Math.Max(yspacing, height);
550                                                 } 
551                                         }
552                                 } while (!success);
553                                 wm.IconicBounds = rect;
554                                 form.Bounds = wm.IconicBounds;
555                         }
556                         lock_sizing = false;
557                 }
558
559                 internal void CloseChildForm (Form form)
560                 {
561                         if (Controls.Count > 1) {
562                                 Form next = (Form) Controls [1];
563                                 if (form.WindowState == FormWindowState.Maximized)
564                                         next.WindowState = FormWindowState.Maximized;
565                                 ActivateChild (next);
566                         }
567
568                         Controls.Remove (form);
569                         form.Close ();
570
571                         XplatUI.RequestNCRecalc (Handle);
572                         if (Controls.Count == 0) {
573                                 XplatUI.RequestNCRecalc (Parent.Handle);
574                                 ParentForm.PerformLayout ();
575                         }
576                         SizeScrollBars ();
577                         SetParentText (false);
578                 }
579
580                 internal void ActivateNextChild ()
581                 {
582                         if (Controls.Count < 1)
583                                 return;
584                         if (Controls.Count == 1 && Controls[0] == ActiveMdiChild)
585                                 return;
586                                 
587                         Form front = (Form) Controls [0];
588                         Form form = (Form) Controls [1];
589
590                         front.SendToBack ();
591                         ActivateChild (form);
592                 }
593
594                 internal void ActivateChild (Form form)
595                 {
596                         if (Controls.Count < 1)
597                                 return;
598                         
599                         if (ParentForm.is_changing_visible_state)
600                                 return;
601                                 
602                         Form current = (Form) Controls [0];
603                         form.SuspendLayout ();
604                         form.BringToFront ();
605                         if (vbar != null && vbar.Visible)
606                                 XplatUI.SetZOrder (vbar.Handle, IntPtr.Zero, true, false);
607                         if (hbar != null && hbar.Visible)
608                                 XplatUI.SetZOrder (hbar.Handle, IntPtr.Zero, true, false);
609                         SetWindowStates ((MdiWindowManager) form.window_manager);
610                         form.ResumeLayout (false);
611                         if (current != form) {
612                                 form.has_focus = false;
613                                 XplatUI.InvalidateNC (current.Handle);
614                                 XplatUI.InvalidateNC (form.Handle);
615                         }
616                         active_child = (Form) Controls [0];
617                 }
618                 
619                 internal bool SetWindowStates (MdiWindowManager wm)
620                 {
621                 /*
622                         MDI WindowState behaviour:
623                         - If the active window is maximized, all other maximized windows are normalized.
624                         - If a normal window gets focus and the original active window was maximized, 
625                           the normal window gets maximized and the original window gets normalized.
626                         - If a minimized window gets focus and the original window was maximized, 
627                           the minimzed window gets maximized and the original window gets normalized. 
628                           If the ex-minimized window gets deactivated, it will be normalized.
629                 */
630                         Form form = wm.form;
631
632                         if (setting_windowstates) {
633                                 return false;
634                         }
635                         
636                         if (!form.Visible)
637                                 return false;
638                         
639                         bool is_active = wm.IsActive();
640                         bool maximize_this = false;
641                         
642                         if (!is_active){
643                                 return false;
644                         }
645
646                         setting_windowstates = true;
647                         foreach (Form frm in mdi_child_list) {
648                                 if (frm == form) {
649                                         continue;
650                                 } else if (!frm.Visible){
651                                         continue;
652                                 }
653                                 if (frm.WindowState == FormWindowState.Maximized && is_active) {
654                                         maximize_this = true;   
655                                         if (((MdiWindowManager) frm.window_manager).was_minimized)
656                                                 frm.WindowState = FormWindowState.Minimized;
657                                         else
658                                                 frm.WindowState = FormWindowState.Normal;//
659                                 }
660                         }
661                         if (maximize_this) {
662                                 wm.was_minimized = form.window_state == FormWindowState.Minimized;
663                                 form.WindowState = FormWindowState.Maximized;
664                         }
665                         SetParentText(false);
666                         
667                         XplatUI.RequestNCRecalc(ParentForm.Handle);
668                         XplatUI.RequestNCRecalc (Handle);
669
670                         SizeScrollBars ();
671
672                         setting_windowstates = false;
673
674                         return maximize_this;
675                 }
676
677                 internal int ChildrenCreated {
678                         get { return mdi_created; }
679                         set { mdi_created = value; }
680                 }
681
682                 internal Form ActiveMdiChild {
683                         get {
684 #if NET_2_0
685                                 if (!ParentForm.Visible)
686                                         return null;
687 #endif
688                                 if (Controls.Count < 1)
689                                         return null;
690                                         
691                                 if (!ParentForm.IsHandleCreated)
692                                         return null;
693                                 
694                                 if (!ParentForm.has_been_visible)
695                                         return null;
696                                         
697                                 if (!ParentForm.Visible)
698                                         return active_child;
699                                 
700                                 active_child = null;
701                                 for (int i = 0; i < Controls.Count; i++) {
702                                         if (Controls [i].Visible) {
703                                                 active_child = (Form) Controls [i];
704                                                 break;
705                                         }
706                                 }
707                                 return active_child;
708                         }
709                         set {
710                                 ActivateChild (value);
711                         }
712                 }
713                 
714                 internal void ActivateActiveMdiChild ()
715                 {
716                         if (ParentForm.is_changing_visible_state)
717                                 return;
718                                 
719                         for (int i = 0; i < Controls.Count; i++) {
720                                 if (Controls [i].Visible) {
721                                         ActivateChild ((Form) Controls [i]);
722                                         return;
723                                 }
724                         }
725                 }
726         }
727 }
728