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 {
36 [ClassInterface(ClassInterfaceType.AutoDispatch)]
37 [DesignTimeVisible(false)]
39 public sealed class MdiClient : Control {
40 #region Local Variables
41 private int mdi_created;
42 private ImplicitHScrollBar hbar;
43 private ImplicitVScrollBar vbar;
44 private SizeGrip sizegrip;
45 private int hbar_value;
46 private int vbar_value;
47 private bool lock_sizing;
48 private bool initializing_scrollbars;
49 private int prev_bottom;
50 private bool setting_windowstates = false;
51 internal ArrayList mdi_child_list;
52 private string form_text;
53 private bool setting_form_text;
54 private Form active_child;
56 #endregion // Local Variables
58 #region Public Classes
60 public new class ControlCollection : Control.ControlCollection {
62 private MdiClient owner;
64 public ControlCollection(MdiClient owner) : base(owner) {
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 mdi_child_list = new ArrayList ();
100 BackColor = SystemColors.AppWorkspace;
101 Dock = DockStyle.Fill;
102 SetStyle (ControlStyles.Selectable, false);
104 #endregion // Public Constructors
106 internal void SendFocusToActiveChild ()
108 Form active = this.ActiveMdiChild;
109 if (active == null) {
110 ParentForm.SendControlFocus (this);
112 active.SendControlFocus (active);
113 ParentForm.ActiveControl = active;
117 internal bool HorizontalScrollbarVisible {
118 get { return hbar != null && hbar.Visible; }
120 internal bool VerticalScrollbarVisible {
121 get { return vbar != null && vbar.Visible; }
124 internal void SetParentText(bool text_changed)
126 if (setting_form_text)
129 setting_form_text = true;
132 form_text = ParentForm.Text;
134 if (ParentForm.ActiveMaximizedMdiChild == null) {
135 ParentForm.Text = form_text;
137 string childText = ParentForm.ActiveMaximizedMdiChild.form.Text;
138 if (childText.Length > 0) {
139 ParentForm.Text = form_text + " - [" + ParentForm.ActiveMaximizedMdiChild.form.Text + "]";
141 ParentForm.Text = form_text;
145 setting_form_text = false;
148 internal override void OnPaintBackgroundInternal (PaintEventArgs pe)
150 if (BackgroundImage != null)
153 if (Parent == null || Parent.BackgroundImage == null)
155 Parent.PaintControlBackground (pe);
158 internal Form ParentForm {
159 get { return (Form) Parent; }
162 protected override Control.ControlCollection CreateControlsInstance ()
164 return new MdiClient.ControlCollection (this);
167 protected override void WndProc(ref Message m) {
168 switch ((Msg)m.Msg) {
170 PaintEventArgs pe = XplatUI.PaintEventStart (ref m, Handle, false);
173 clip = new Rectangle (0, 0, Width, Height);
175 ControlPaint.DrawBorder3D (pe.Graphics, clip, Border3DStyle.Sunken);
176 XplatUI.PaintEventEnd (ref m, Handle, false);
177 m.Result = IntPtr.Zero;
181 base.WndProc (ref m);
184 protected override void OnResize (EventArgs e)
188 if (Parent != null && Parent.IsHandleCreated)
189 XplatUI.InvalidateNC (Parent.Handle);
190 // Should probably make this into one loop
195 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
197 // Never change the MdiClient's location
198 specified &= ~BoundsSpecified.Location;
200 base.ScaleControl (factor, specified);
203 [System.ComponentModel.EditorBrowsable (EditorBrowsableState.Never)]
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 [EditorBrowsable (EditorBrowsableState.Never)]
227 public override ImageLayout BackgroundImageLayout {
229 return base.BackgroundImageLayout;
232 base.BackgroundImageLayout = value;
236 public Form [] MdiChildren {
238 if (mdi_child_list == null)
240 return (Form []) mdi_child_list.ToArray (typeof (Form));
243 #endregion // Public Instance Properties
245 #region Protected Instance Properties
246 protected override CreateParams CreateParams {
248 CreateParams result = base.CreateParams;
249 result.ExStyle |= (int) WindowExStyles.WS_EX_CLIENTEDGE;
253 #endregion // Protected Instance Properties
255 #region Public Instance Methods
256 public void LayoutMdi (MdiLayout value) {
258 // Don't forget to always call ArrangeIconicWindows
259 ArrangeIconicWindows (true);
262 case MdiLayout.Cascade: {
264 for (int c = Controls.Count - 1; c >= 0; c--) {
265 Form form = (Form) Controls [c];
267 if (form.WindowState == FormWindowState.Minimized)
270 if (form.WindowState == FormWindowState.Maximized)
271 form.WindowState = FormWindowState.Normal;
273 form.Width = System.Convert.ToInt32 (ClientSize.Width * 0.8);
274 form.Height = Math.Max (
275 System.Convert.ToInt32 (ClientSize.Height * 0.8),
276 SystemInformation.MinimumWindowSize.Height + 2);
281 if (i != 0 && (l + form.Width > ClientSize.Width || t + form.Height > ClientSize.Height)) {
294 case MdiLayout.TileHorizontal:
295 case MdiLayout.TileVertical: {
296 // First count number of windows to tile
299 // And space used by iconic windows
300 int clientHeight = ClientSize.Height;
302 for (int i = 0; i < Controls.Count; i++) {
303 Form form = Controls [i] as Form;
311 if (form.WindowState == FormWindowState.Maximized)
312 form.WindowState = FormWindowState.Normal;
313 else if (form.WindowState == FormWindowState.Minimized) {
314 if (form.Bounds.Top < clientHeight)
315 clientHeight = form.Bounds.Top;
324 // Calculate desired height and width
328 if (value == MdiLayout.TileHorizontal) {
329 newSize = new Size(ClientSize.Width, clientHeight / total);
330 offset = new Size (0, newSize.Height);
332 newSize = new Size(ClientSize.Width / total, clientHeight);
333 offset = new Size (newSize.Width, 0);
336 // Loop again and set the size and location.
337 Point nextLocation = Point.Empty;
339 for (int i = 0; i < Controls.Count; i++) {
340 Form form = Controls [i] as Form;
348 if (form.WindowState == FormWindowState.Minimized)
352 form.Location = nextLocation;
353 nextLocation += offset;
360 #endregion // Public Instance Methods
362 #region Protected Instance Methods
363 #endregion // Protected Instance Methods
365 internal void SizeScrollBars ()
370 if (!IsHandleCreated)
373 if (Controls.Count == 0 || ((Form) Controls [0]).WindowState == FormWindowState.Maximized) {
375 hbar.Visible = false;
377 vbar.Visible = false;
378 if (sizegrip != null)
379 sizegrip.Visible = false;
388 foreach (Form child in Controls) {
391 if (child.Right > right)
393 if (child.Left < left) {
397 if (child.Bottom > bottom)
398 bottom = child.Bottom;
404 int available_width = ClientSize.Width;
405 int available_height = ClientSize.Height;
407 bool need_hbar = false;
408 bool need_vbar = false;
410 if (right - left > available_width || left < 0) {
412 available_height -= SystemInformation.HorizontalScrollBarHeight;
414 if (bottom - top > available_height || top < 0) {
416 available_width -= SystemInformation.VerticalScrollBarWidth;
418 if (!need_hbar && (right - left > available_width || left < 0)) {
420 available_height -= SystemInformation.HorizontalScrollBarHeight;
426 hbar = new ImplicitHScrollBar ();
427 Controls.AddImplicit (hbar);
430 CalcHBar (left, right, need_vbar);
431 } else if (hbar != null)
432 hbar.Visible = false;
436 vbar = new ImplicitVScrollBar ();
437 Controls.AddImplicit (vbar);
440 CalcVBar (top, bottom, need_hbar);
441 } else if (vbar != null)
442 vbar.Visible = false;
444 if (need_hbar && need_vbar) {
445 if (sizegrip == null) {
446 sizegrip = new SizeGrip (this.ParentForm);
447 Controls.AddImplicit (sizegrip);
449 sizegrip.Location = new Point (hbar.Right, vbar.Bottom);
450 sizegrip.Visible = true;
451 XplatUI.SetZOrder (sizegrip.Handle, vbar.Handle, false, false);
452 } else if (sizegrip != null) {
453 sizegrip.Visible = false;
456 XplatUI.InvalidateNC (Handle);
459 private void CalcHBar (int left, int right, bool vert_vis)
461 initializing_scrollbars = true;
464 hbar.Top = ClientRectangle.Bottom - hbar.Height;
465 hbar.Width = ClientRectangle.Width - (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0);
466 hbar.LargeChange = 50;
467 hbar.Minimum = Math.Min (left, 0);
468 hbar.Maximum = Math.Max (right - ClientSize.Width + 51 + (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0), 0);
471 hbar.ValueChanged += new EventHandler (HBarValueChanged);
472 XplatUI.SetZOrder (hbar.Handle, IntPtr.Zero, true, false);
474 initializing_scrollbars = false;
477 private void CalcVBar (int top, int bottom, bool horz_vis)
479 initializing_scrollbars = true;
482 vbar.Left = ClientRectangle.Right - vbar.Width;
483 vbar.Height = ClientRectangle.Height - (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0);
484 vbar.LargeChange = 50;
485 vbar.Minimum = Math.Min (top, 0);
486 vbar.Maximum = Math.Max (bottom - ClientSize.Height + 51 + (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0), 0);
489 vbar.ValueChanged += new EventHandler (VBarValueChanged);
490 XplatUI.SetZOrder (vbar.Handle, IntPtr.Zero, true, false);
492 initializing_scrollbars = false;
495 private void HBarValueChanged (object sender, EventArgs e)
497 if (initializing_scrollbars)
500 if (hbar.Value == hbar_value)
506 int diff = hbar_value - hbar.Value;
507 foreach (Form child in Controls) {
514 hbar_value = hbar.Value;
517 private void VBarValueChanged (object sender, EventArgs e)
519 if (initializing_scrollbars)
522 if (vbar.Value == vbar_value)
528 int diff = vbar_value - vbar.Value;
529 foreach (Form child in Controls) {
536 vbar_value = vbar.Value;
539 private void ArrangeWindows ()
541 if (!IsHandleCreated)
545 if (prev_bottom != -1)
546 change = Bottom - prev_bottom;
548 foreach (Control c in Controls) {
549 Form child = c as Form;
551 if (c == null || !child.Visible)
554 MdiWindowManager wm = child.WindowManager as MdiWindowManager;
555 if (wm.GetWindowState () == FormWindowState.Maximized)
556 child.Bounds = wm.MaximizedBounds;
558 if (wm.GetWindowState () == FormWindowState.Minimized) {
564 prev_bottom = Bottom;
567 internal void ArrangeIconicWindows (bool rearrange_all)
569 Rectangle rect = Rectangle.Empty;
572 foreach (Form form in Controls) {
573 if (form.WindowState != FormWindowState.Minimized)
576 MdiWindowManager wm = (MdiWindowManager) form.WindowManager;
578 if (wm.IconicBounds != Rectangle.Empty && !rearrange_all) {
579 if (form.Bounds != wm.IconicBounds)
580 form.Bounds = wm.IconicBounds;
585 int startx, starty, currentx, currenty;
587 rect.Size = wm.IconicSize;
590 starty = ClientSize.Height - rect.Height;
598 foreach (Form form2 in Controls) {
599 if (form2 == form || form2.window_state != FormWindowState.Minimized)
602 if (form2.Bounds.IntersectsWith(rect)) {
608 currentx += rect.Width;
609 if (currentx + rect.Width > Right) {
611 currenty -= rect.Height;
615 wm.IconicBounds = rect;
616 form.Bounds = wm.IconicBounds;
621 internal void ChildFormClosed (Form form)
623 FormWindowState closed_form_windowstate = form.WindowState;
625 form.Visible = false;
626 Controls.Remove (form);
628 if (Controls.Count == 0) {
629 ((MdiWindowManager) form.window_manager).RaiseDeactivate ();
630 } else if (closed_form_windowstate == FormWindowState.Maximized) {
631 Form current = (Form) Controls [0];
632 current.WindowState = FormWindowState.Maximized;
633 ActivateChild(current);
636 if (Controls.Count == 0) {
637 XplatUI.RequestNCRecalc (Parent.Handle);
638 ParentForm.PerformLayout ();
640 // If we closed the last child, unmerge the menus.
641 // If it's not the last child, the menu will be unmerged
642 // when another child takes focus.
643 MenuStrip parent_menu = form.MdiParent.MainMenuStrip;
645 if (parent_menu != null)
646 if (parent_menu.IsCurrentlyMerged)
647 ToolStripManager.RevertMerge (parent_menu);
650 SetParentText (false);
654 internal void ActivateNextChild ()
656 if (Controls.Count < 1)
658 if (Controls.Count == 1 && Controls[0] == ActiveMdiChild)
661 Form front = (Form) Controls [0];
662 Form form = (Form) Controls [1];
664 ActivateChild (form);
668 internal void ActivatePreviousChild ()
670 if (Controls.Count <= 1)
673 Form back = (Form) Controls [Controls.Count - 1];
675 ActivateChild (back);
678 internal void ActivateChild (Form form)
680 if (Controls.Count < 1)
683 if (ParentForm.is_changing_visible_state > 0)
686 Form current = (Form) Controls [0];
687 bool raise_deactivate = ParentForm.ActiveControl == current;
689 // We want to resize the new active form before it is
690 // made active to avoid flickering. Can't do it in the
691 // normal way (form.WindowState = Maximized) since it's not
692 // active yet and everything would just return to before.
693 // We also won't suspend layout, this way the layout will
694 // happen before the form is made active (and in many cases
695 // before it is visible, which avoids flickering as well).
696 MdiWindowManager wm = (MdiWindowManager)form.WindowManager;
698 if (current.WindowState == FormWindowState.Maximized && form.WindowState != FormWindowState.Maximized && form.Visible) {
699 FormWindowState old_state = form.window_state;
700 SetWindowState (form, old_state, FormWindowState.Maximized, true);
701 wm.was_minimized = form.window_state == FormWindowState.Minimized;
702 form.window_state = FormWindowState.Maximized;
703 SetParentText (false);
706 form.BringToFront ();
707 form.SendControlFocus (form);
708 SetWindowStates (wm);
709 if (current != form) {
710 form.has_focus = false;
711 if (current.IsHandleCreated)
712 XplatUI.InvalidateNC (current.Handle);
713 if (form.IsHandleCreated)
714 XplatUI.InvalidateNC (form.Handle);
715 if (raise_deactivate) {
716 MdiWindowManager current_wm = (MdiWindowManager) current.window_manager;
717 current_wm.RaiseDeactivate ();
721 active_child = (Form) Controls [0];
723 if (active_child.Visible) {
724 bool raise_activated = ParentForm.ActiveControl != active_child;
725 ParentForm.ActiveControl = active_child;
726 if (raise_activated) {
727 MdiWindowManager active_wm = (MdiWindowManager) active_child.window_manager;
728 active_wm.RaiseActivated ();
733 internal override IntPtr AfterTopMostControl ()
735 // order of scrollbars:
738 // bottom = horizontal
739 if (hbar != null && hbar.Visible)
741 // no need to check for sizegrip since it will only
742 // be visible if hbar is visible.
743 if (vbar != null && vbar.Visible)
746 return base.AfterTopMostControl ();
749 internal bool SetWindowStates (MdiWindowManager wm)
752 MDI WindowState behaviour:
753 - If the active window is maximized, all other maximized windows are normalized.
754 - If a normal window gets focus and the original active window was maximized,
755 the normal window gets maximized and the original window gets normalized.
756 - If a minimized window gets focus and the original window was maximized,
757 the minimzed window gets maximized and the original window gets normalized.
758 If the ex-minimized window gets deactivated, it will be normalized.
762 if (setting_windowstates) {
769 bool is_active = wm.IsActive;
770 bool maximize_this = false;
776 ArrayList minimize_these = new ArrayList ();
777 ArrayList normalize_these = new ArrayList ();
779 setting_windowstates = true;
780 foreach (Form frm in mdi_child_list) {
783 } else if (!frm.Visible){
786 if (frm.WindowState == FormWindowState.Maximized && is_active) {
787 maximize_this = true;
788 if (((MdiWindowManager) frm.window_manager).was_minimized) {
789 minimize_these.Add (frm);
791 normalize_these.Add (frm);
796 if (maximize_this && form.WindowState != FormWindowState.Maximized) {
797 wm.was_minimized = form.window_state == FormWindowState.Minimized;
798 form.WindowState = FormWindowState.Maximized;
801 foreach (Form frm in minimize_these)
802 frm.WindowState = FormWindowState.Minimized;
804 foreach (Form frm in normalize_these)
805 frm.WindowState = FormWindowState.Normal;
808 SetParentText (false);
810 XplatUI.RequestNCRecalc (ParentForm.Handle);
811 XplatUI.RequestNCRecalc (Handle);
815 setting_windowstates = false;
817 if (form.MdiParent.MainMenuStrip != null)
818 form.MdiParent.MainMenuStrip.RefreshMdiItems ();
820 // Implicit menu strip merging
821 // - When child is activated
822 // - Parent form must have a MainMenuStrip
823 // - Find the first menustrip on the child
825 MenuStrip parent_menu = form.MdiParent.MainMenuStrip;
827 if (parent_menu != null) {
828 if (parent_menu.IsCurrentlyMerged)
829 ToolStripManager.RevertMerge (parent_menu);
831 MenuStrip child_menu = LookForChildMenu (form);
833 if (form.WindowState != FormWindowState.Maximized)
834 RemoveControlMenuItems (wm);
836 if (form.WindowState == FormWindowState.Maximized) {
839 foreach (ToolStripItem tsi in parent_menu.Items) {
840 if (tsi is MdiControlStrip.SystemMenuItem) {
841 (tsi as MdiControlStrip.SystemMenuItem).MdiForm = form;
843 } else if (tsi is MdiControlStrip.ControlBoxMenuItem) {
844 (tsi as MdiControlStrip.ControlBoxMenuItem).MdiForm = form;
850 parent_menu.SuspendLayout ();
851 parent_menu.Items.Insert (0, new MdiControlStrip.SystemMenuItem (form));
852 parent_menu.Items.Add (new MdiControlStrip.ControlBoxMenuItem (form, MdiControlStrip.ControlBoxType.Close));
853 parent_menu.Items.Add (new MdiControlStrip.ControlBoxMenuItem (form, MdiControlStrip.ControlBoxType.Max));
854 parent_menu.Items.Add (new MdiControlStrip.ControlBoxMenuItem (form, MdiControlStrip.ControlBoxType.Min));
855 parent_menu.ResumeLayout ();
859 if (child_menu != null)
860 ToolStripManager.Merge (child_menu, parent_menu);
863 return maximize_this;
866 private MenuStrip LookForChildMenu (Control parent)
868 foreach (Control c in parent.Controls) {
872 if (c is ToolStripContainer || c is ToolStripPanel) {
873 MenuStrip ms = LookForChildMenu (c);
883 internal void RemoveControlMenuItems (MdiWindowManager wm)
886 MenuStrip parent_menu = form.MdiParent.MainMenuStrip;
888 // Only remove the items if the form requesting still owns the menu items
889 if (parent_menu != null) {
890 parent_menu.SuspendLayout ();
892 for (int i = parent_menu.Items.Count - 1; i >= 0; i--) {
893 if (parent_menu.Items[i] is MdiControlStrip.SystemMenuItem) {
894 if ((parent_menu.Items[i] as MdiControlStrip.SystemMenuItem).MdiForm == form)
895 parent_menu.Items.RemoveAt (i);
896 } else if (parent_menu.Items[i] is MdiControlStrip.ControlBoxMenuItem) {
897 if ((parent_menu.Items[i] as MdiControlStrip.ControlBoxMenuItem).MdiForm == form)
898 parent_menu.Items.RemoveAt (i);
902 parent_menu.ResumeLayout ();
906 internal void SetWindowState (Form form, FormWindowState old_window_state, FormWindowState new_window_state, bool is_activating_child)
908 bool mdiclient_layout;
910 MdiWindowManager wm = (MdiWindowManager) form.window_manager;
912 if (!is_activating_child && new_window_state == FormWindowState.Maximized && !wm.IsActive) {
913 ActivateChild (form);
917 if (old_window_state == FormWindowState.Normal)
918 wm.NormalBounds = form.Bounds;
920 if (SetWindowStates (wm))
923 if (old_window_state == new_window_state)
926 mdiclient_layout = old_window_state == FormWindowState.Maximized || new_window_state == FormWindowState.Maximized;
928 switch (new_window_state) {
929 case FormWindowState.Minimized:
930 ArrangeIconicWindows (false);
932 case FormWindowState.Maximized:
933 form.Bounds = wm.MaximizedBounds;
935 case FormWindowState.Normal:
936 form.Bounds = wm.NormalBounds;
940 wm.UpdateWindowDecorations (new_window_state);
944 if (mdiclient_layout)
945 Parent.PerformLayout ();
947 XplatUI.RequestNCRecalc (Parent.Handle);
948 XplatUI.RequestNCRecalc (form.Handle);
949 if (!setting_windowstates)
952 internal int ChildrenCreated {
953 get { return mdi_created; }
954 set { mdi_created = value; }
957 internal Form ActiveMdiChild {
959 if (ParentForm != null && !ParentForm.Visible)
962 if (Controls.Count < 1)
965 if (!ParentForm.IsHandleCreated)
968 if (!ParentForm.has_been_visible)
971 if (!ParentForm.Visible)
975 for (int i = 0; i < Controls.Count; i++) {
976 if (Controls [i].Visible) {
977 active_child = (Form) Controls [i];
984 ActivateChild (value);
988 internal void ActivateActiveMdiChild ()
990 if (ParentForm.is_changing_visible_state > 0)
993 for (int i = 0; i < Controls.Count; i++) {
994 if (Controls [i].Visible) {
995 ActivateChild ((Form) Controls [i]);