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