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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok pbartok@novell.com
29 using System.Collections;
30 using System.ComponentModel;
32 using System.Runtime.InteropServices;
34 namespace System.Windows.Forms {
35 [DesignTimeVisible(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;
56 #endregion // Local Variables
58 #region Public Classes
59 public new class ControlCollection : Control.ControlCollection {
61 private MdiClient owner;
63 public ControlCollection(MdiClient owner) : base(owner) {
65 owner.mdi_child_list = new ArrayList ();
68 public override void Add(Control value) {
69 if ((value is Form) == false || !(((Form)value).IsMdiChild)) {
70 throw new ArgumentException("Form must be MdiChild");
72 owner.mdi_child_list.Add (value);
75 // newest member is the active one
76 Form form = (Form) value;
77 owner.ActiveMdiChild = form;
80 public override void Remove(Control value)
82 Form form = value as Form;
84 MdiWindowManager wm = form.WindowManager as MdiWindowManager;
86 form.Closed -= wm.form_closed_handler;
90 owner.mdi_child_list.Remove (value);
94 #endregion // Public Classes
96 #region Public Constructors
99 BackColor = SystemColors.AppWorkspace;
100 Dock = DockStyle.Fill;
101 SetStyle (ControlStyles.Selectable, false);
103 #endregion // Public Constructors
106 internal void SetParentText(bool text_changed)
108 if (setting_form_text)
111 setting_form_text = true;
114 form_text = ParentForm.Text;
116 if (ParentForm.ActiveMaximizedMdiChild == null) {
117 ParentForm.Text = form_text;
119 string childText = ParentForm.ActiveMaximizedMdiChild.form.Text;
120 if (childText.Length > 0) {
121 ParentForm.Text = form_text + " - [" + ParentForm.ActiveMaximizedMdiChild.form.Text + "]";
123 ParentForm.Text = form_text;
127 setting_form_text = false;
130 internal override void OnPaintBackgroundInternal (PaintEventArgs pe)
132 if (BackgroundImage != null)
135 if (Parent == null || Parent.BackgroundImage == null)
137 Parent.PaintControlBackground (pe);
140 internal Form ParentForm {
141 get { return (Form) Parent; }
144 protected override Control.ControlCollection CreateControlsInstance ()
146 return new MdiClient.ControlCollection (this);
149 protected override void WndProc(ref Message m) {
151 switch ((Msg) m.Msg) {
153 Console.WriteLine ("ignoring paint");
158 switch ((Msg)m.Msg) {
159 case Msg.WM_NCCALCSIZE:
160 XplatUIWin32.NCCALCSIZE_PARAMS ncp;
162 if (m.WParam == (IntPtr) 1) {
163 ncp = (XplatUIWin32.NCCALCSIZE_PARAMS) Marshal.PtrToStructure (m.LParam,
164 typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
169 ncp.rgrc1.bottom -= bw;
170 ncp.rgrc1.left += bw;
171 ncp.rgrc1.right -= bw;
173 Marshal.StructureToPtr (ncp, m.LParam, true);
179 PaintEventArgs pe = XplatUI.PaintEventStart (Handle, false);
182 clip = new Rectangle (0, 0, Width, Height);
184 ControlPaint.DrawBorder3D (pe.Graphics, clip, Border3DStyle.Sunken);
185 XplatUI.PaintEventEnd (Handle, false);
186 m.Result = IntPtr.Zero;
190 base.WndProc (ref m);
193 protected override void OnResize (EventArgs e)
198 XplatUI.InvalidateNC (Parent.Handle);
199 // Should probably make this into one loop
204 protected override void ScaleCore (float dx, float dy)
206 base.ScaleCore (dx, dy);
209 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
211 base.SetBoundsCore (x, y, width, height, specified);
214 #region Public Instance Properties
216 public override System.Drawing.Image BackgroundImage {
218 return base.BackgroundImage;
221 base.BackgroundImage = value;
225 public Form [] MdiChildren {
227 if (mdi_child_list == null)
229 return (Form []) mdi_child_list.ToArray (typeof (Form));
232 #endregion // Public Instance Properties
234 #region Protected Instance Properties
235 protected override CreateParams CreateParams {
237 return base.CreateParams;
240 #endregion // Protected Instance Properties
242 #region Public Instance Methods
243 public void LayoutMdi (MdiLayout value) {
245 int max_width = Int32.MaxValue;
246 int max_height = Int32.MaxValue;
248 if (Parent != null) {
249 max_width = Parent.Width;
250 max_height = Parent.Height;
254 case MdiLayout.Cascade:
256 for (int c = Controls.Count - 1; c >= 0; c--) {
257 Form form = (Form) Controls [c];
262 if (i != 0 && (l + form.Width > max_width || t + form.Height > max_height)) {
275 throw new NotImplementedException();
278 #endregion // Public Instance Methods
280 #region Protected Instance Methods
281 #endregion // Protected Instance Methods
283 internal void SizeScrollBars ()
288 if (Controls.Count == 0 || ((Form) Controls [0]).WindowState == FormWindowState.Maximized) {
290 hbar.Visible = false;
292 vbar.Visible = false;
293 if (sizegrip != null)
294 sizegrip.Visible = false;
298 bool hbar_required = false;
299 bool vbar_required = false;
306 foreach (Form child in Controls) {
309 if (child.Right > right)
311 if (child.Left < left) {
312 hbar_required = true;
316 if (child.Bottom > bottom)
317 bottom = child.Bottom;
319 vbar_required = true;
324 int first_right = Width;
325 int first_bottom = Height;
326 int right_edge = first_right;
327 int bottom_edge = first_bottom;
329 int prev_bottom_edge;
331 bool need_hbar = false;
332 bool need_vbar = false;
335 prev_right_edge = right_edge;
336 prev_bottom_edge = bottom_edge;
338 if (hbar_required || right > right_edge) {
340 bottom_edge = first_bottom - SystemInformation.HorizontalScrollBarHeight;
343 bottom_edge = first_bottom;
346 if (vbar_required || bottom > bottom_edge) {
348 right_edge = first_right - SystemInformation.VerticalScrollBarWidth;
351 right_edge = first_right;
354 } while (right_edge != prev_right_edge || bottom_edge != prev_bottom_edge);
358 hbar = new ImplicitHScrollBar ();
359 Controls.AddImplicit (hbar);
362 CalcHBar (left, right, right_edge, need_vbar);
363 } else if (hbar != null)
364 hbar.Visible = false;
368 vbar = new ImplicitVScrollBar ();
369 Controls.AddImplicit (vbar);
372 CalcVBar (top, bottom, bottom_edge, need_hbar);
373 } else if (vbar != null)
374 vbar.Visible = false;
376 if (need_hbar && need_vbar) {
377 if (sizegrip == null) {
378 sizegrip = new SizeGrip ();
379 Controls.AddImplicit (sizegrip);
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;
390 private void CalcHBar (int left, int right, int right_edge, bool vert_vis)
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;
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;
408 private void CalcVBar (int top, int bottom, int bottom_edge, bool horz_vis)
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;
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;
426 private void HBarValueChanged (object sender, EventArgs e)
428 if (initializing_scrollbars)
431 if (hbar.Value == hbar_value)
437 foreach (Form child in Controls) {
438 child.Left += hbar_value - hbar.Value;
444 hbar_value = hbar.Value;
448 private void VBarValueChanged (object sender, EventArgs e)
450 if (initializing_scrollbars)
453 if (vbar.Value == vbar_value)
459 foreach (Form child in Controls) {
460 child.Top += vbar_value - vbar.Value;
466 vbar_value = vbar.Value;
470 private void ArrangeWindows ()
473 if (prev_bottom != -1)
474 change = Bottom - prev_bottom;
476 foreach (Control c in Controls) {
477 Form child = c as Form;
479 if (c == null || !child.Visible)
482 MdiWindowManager wm = child.WindowManager as MdiWindowManager;
483 if (wm.GetWindowState () == FormWindowState.Maximized)
486 if (wm.GetWindowState () == FormWindowState.Minimized) {
492 prev_bottom = Bottom;
495 private void FormLocationChanged (object sender, EventArgs e)
500 internal void ArrangeIconicWindows ()
505 Rectangle rect = new Rectangle (0, 0, xspacing, yspacing);
508 foreach (Form form in Controls) {
509 if (form.WindowState != FormWindowState.Minimized)
512 MdiWindowManager wm = (MdiWindowManager) form.WindowManager;
514 if (wm.IconicBounds != Rectangle.Empty) {
515 if (form.Bounds != wm.IconicBounds)
516 form.Bounds = wm.IconicBounds;
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);
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;
529 int startx, starty, currentx, currenty;
532 starty = Bottom - yspacing - 1;
533 if (this.hbar != null && this.hbar.Visible)
534 starty -= this.hbar.Height;
541 rect.Height = height;
543 foreach (Form form2 in Controls) {
544 if (form2 == form || form2.window_state != FormWindowState.Minimized)
547 if (form2.Bounds.IntersectsWith(rect)) {
553 currentx += xspacing;
554 if (currentx + xspacing > Right) {
556 currenty -= Math.Max(yspacing, height);
560 wm.IconicBounds = rect;
561 form.Bounds = wm.IconicBounds;
566 internal void CloseChildForm (Form form)
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);
575 Controls.Remove (form);
578 XplatUI.RequestNCRecalc (Handle);
579 if (Controls.Count == 0) {
580 XplatUI.RequestNCRecalc (Parent.Handle);
581 ParentForm.PerformLayout ();
584 SetParentText (false);
587 internal void ActivateNextChild ()
589 if (Controls.Count < 1)
591 if (Controls.Count == 1 && Controls[0] == ActiveMdiChild)
594 Form front = (Form) Controls [0];
595 Form form = (Form) Controls [1];
598 ActivateChild (form);
601 internal void ActivateChild (Form form)
603 if (Controls.Count < 1)
606 if (ParentForm.is_changing_visible_state)
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);
623 active_child = (Form) Controls [0];
626 internal bool SetWindowStates (MdiWindowManager wm)
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.
639 if (setting_windowstates) {
646 bool is_active = wm.IsActive();
647 bool maximize_this = false;
653 setting_windowstates = true;
654 foreach (Form frm in mdi_child_list) {
657 } else if (!frm.Visible){
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;
665 frm.WindowState = FormWindowState.Normal;//
669 wm.was_minimized = form.window_state == FormWindowState.Minimized;
670 form.WindowState = FormWindowState.Maximized;
672 SetParentText(false);
674 XplatUI.RequestNCRecalc(ParentForm.Handle);
675 XplatUI.RequestNCRecalc (Handle);
679 setting_windowstates = false;
681 return maximize_this;
684 internal int ChildrenCreated {
685 get { return mdi_created; }
686 set { mdi_created = value; }
689 internal Form ActiveMdiChild {
692 if (!ParentForm.Visible)
695 if (Controls.Count < 1)
698 if (!ParentForm.IsHandleCreated)
701 if (!ParentForm.has_been_visible)
704 if (!ParentForm.Visible)
708 for (int i = 0; i < Controls.Count; i++) {
709 if (Controls [i].Visible) {
710 active_child = (Form) Controls [i];
717 ActivateChild (value);
721 internal void ActivateActiveMdiChild ()
723 if (ParentForm.is_changing_visible_state)
726 for (int i = 0; i < Controls.Count; i++) {
727 if (Controls [i].Visible) {
728 ActivateChild ((Form) Controls [i]);