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 {
37 [ClassInterface(ClassInterfaceType.AutoDispatch)]
39 [DesignTimeVisible(false)]
41 public sealed class MdiClient : Control {
42 #region Local Variables
43 private int mdi_created;
44 private ImplicitHScrollBar hbar;
45 private ImplicitVScrollBar vbar;
46 private SizeGrip sizegrip;
47 private int hbar_value;
48 private int vbar_value;
49 private bool lock_sizing;
50 private bool initializing_scrollbars;
51 private int prev_bottom;
52 private bool setting_windowstates = false;
53 internal ArrayList mdi_child_list;
54 private string form_text;
55 private bool setting_form_text;
56 private Form active_child;
58 #endregion // Local Variables
60 #region Public Classes
64 public new class ControlCollection : Control.ControlCollection {
66 private MdiClient owner;
68 public ControlCollection(MdiClient owner) : base(owner) {
72 public override void Add(Control value) {
73 if ((value is Form) == false || !(((Form)value).IsMdiChild)) {
74 throw new ArgumentException("Form must be MdiChild");
76 owner.mdi_child_list.Add (value);
79 // newest member is the active one
80 Form form = (Form) value;
81 owner.ActiveMdiChild = form;
84 public override void Remove(Control value)
86 Form form = value as Form;
88 MdiWindowManager wm = form.WindowManager as MdiWindowManager;
90 form.Closed -= wm.form_closed_handler;
94 owner.mdi_child_list.Remove (value);
98 #endregion // Public Classes
100 #region Public Constructors
103 mdi_child_list = new ArrayList ();
104 BackColor = SystemColors.AppWorkspace;
105 Dock = DockStyle.Fill;
106 SetStyle (ControlStyles.Selectable, false);
108 #endregion // Public Constructors
110 internal void SendFocusToActiveChild ()
112 Form active = this.ActiveMdiChild;
113 if (active == null) {
114 ParentForm.SendControlFocus (this);
116 active.SendControlFocus (active);
117 ParentForm.ActiveControl = active;
121 internal bool HorizontalScrollbarVisible {
122 get { return hbar != null && hbar.Visible; }
124 internal bool VerticalScrollbarVisible {
125 get { return vbar != null && vbar.Visible; }
128 internal void SetParentText(bool text_changed)
130 if (setting_form_text)
133 setting_form_text = true;
136 form_text = ParentForm.Text;
138 if (ParentForm.ActiveMaximizedMdiChild == null) {
139 ParentForm.Text = form_text;
141 string childText = ParentForm.ActiveMaximizedMdiChild.form.Text;
142 if (childText.Length > 0) {
143 ParentForm.Text = form_text + " - [" + ParentForm.ActiveMaximizedMdiChild.form.Text + "]";
145 ParentForm.Text = form_text;
149 setting_form_text = false;
152 internal override void OnPaintBackgroundInternal (PaintEventArgs pe)
154 if (BackgroundImage != null)
157 if (Parent == null || Parent.BackgroundImage == null)
159 Parent.PaintControlBackground (pe);
162 internal Form ParentForm {
163 get { return (Form) Parent; }
166 protected override Control.ControlCollection CreateControlsInstance ()
168 return new MdiClient.ControlCollection (this);
171 protected override void WndProc(ref Message m) {
172 switch ((Msg)m.Msg) {
174 PaintEventArgs pe = XplatUI.PaintEventStart (ref m, Handle, false);
177 clip = new Rectangle (0, 0, Width, Height);
179 ControlPaint.DrawBorder3D (pe.Graphics, clip, Border3DStyle.Sunken);
180 XplatUI.PaintEventEnd (ref m, Handle, false);
181 m.Result = IntPtr.Zero;
185 base.WndProc (ref m);
188 protected override void OnResize (EventArgs e)
192 if (Parent != null && Parent.IsHandleCreated)
193 XplatUI.InvalidateNC (Parent.Handle);
194 // Should probably make this into one loop
199 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
201 // Never change the MdiClient's location
202 specified &= ~BoundsSpecified.Location;
204 base.ScaleControl (factor, specified);
207 [System.ComponentModel.EditorBrowsable (EditorBrowsableState.Never)]
209 protected override void ScaleCore (float dx, float dy)
211 base.ScaleCore (dx, dy);
214 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
216 base.SetBoundsCore (x, y, width, height, specified);
219 #region Public Instance Properties
221 public override System.Drawing.Image BackgroundImage {
223 return base.BackgroundImage;
226 base.BackgroundImage = value;
231 [EditorBrowsable (EditorBrowsableState.Never)]
233 public override ImageLayout BackgroundImageLayout {
235 return base.BackgroundImageLayout;
238 base.BackgroundImageLayout = value;
243 public Form [] MdiChildren {
245 if (mdi_child_list == null)
247 return (Form []) mdi_child_list.ToArray (typeof (Form));
250 #endregion // Public Instance Properties
252 #region Protected Instance Properties
253 protected override CreateParams CreateParams {
255 CreateParams result = base.CreateParams;
256 result.ExStyle |= (int) WindowExStyles.WS_EX_CLIENTEDGE;
260 #endregion // Protected Instance Properties
262 #region Public Instance Methods
263 public void LayoutMdi (MdiLayout value) {
265 // Don't forget to always call ArrangeIconicWindows
266 ArrangeIconicWindows (true);
269 case MdiLayout.Cascade: {
271 for (int c = Controls.Count - 1; c >= 0; c--) {
272 Form form = (Form) Controls [c];
274 if (form.WindowState == FormWindowState.Minimized)
277 if (form.WindowState == FormWindowState.Maximized)
278 form.WindowState = FormWindowState.Normal;
280 form.Width = System.Convert.ToInt32 (ClientSize.Width * 0.8);
281 form.Height = Math.Max (
282 System.Convert.ToInt32 (ClientSize.Height * 0.8),
283 SystemInformation.MinimumWindowSize.Height + 2);
288 if (i != 0 && (l + form.Width > ClientSize.Width || t + form.Height > ClientSize.Height)) {
301 case MdiLayout.TileHorizontal:
302 case MdiLayout.TileVertical: {
303 // First count number of windows to tile
306 // And space used by iconic windows
307 int clientHeight = ClientSize.Height;
309 for (int i = 0; i < Controls.Count; i++) {
310 Form form = Controls [i] as Form;
318 if (form.WindowState == FormWindowState.Maximized)
319 form.WindowState = FormWindowState.Normal;
320 else if (form.WindowState == FormWindowState.Minimized) {
321 if (form.Bounds.Top < clientHeight)
322 clientHeight = form.Bounds.Top;
331 // Calculate desired height and width
335 if (value == MdiLayout.TileHorizontal) {
336 newSize = new Size(ClientSize.Width, clientHeight / total);
337 offset = new Size (0, newSize.Height);
339 newSize = new Size(ClientSize.Width / total, clientHeight);
340 offset = new Size (newSize.Width, 0);
343 // Loop again and set the size and location.
344 Point nextLocation = Point.Empty;
346 for (int i = 0; i < Controls.Count; i++) {
347 Form form = Controls [i] as Form;
355 if (form.WindowState == FormWindowState.Minimized)
359 form.Location = nextLocation;
360 nextLocation += offset;
367 #endregion // Public Instance Methods
369 #region Protected Instance Methods
370 #endregion // Protected Instance Methods
372 internal void SizeScrollBars ()
377 if (!IsHandleCreated)
380 if (Controls.Count == 0 || ((Form) Controls [0]).WindowState == FormWindowState.Maximized) {
382 hbar.Visible = false;
384 vbar.Visible = false;
385 if (sizegrip != null)
386 sizegrip.Visible = false;
395 foreach (Form child in Controls) {
398 if (child.Right > right)
400 if (child.Left < left) {
404 if (child.Bottom > bottom)
405 bottom = child.Bottom;
411 int available_width = ClientSize.Width;
412 int available_height = ClientSize.Height;
414 bool need_hbar = false;
415 bool need_vbar = false;
417 if (right - left > available_width || left < 0) {
419 available_height -= SystemInformation.HorizontalScrollBarHeight;
421 if (bottom - top > available_height || top < 0) {
423 available_width -= SystemInformation.VerticalScrollBarWidth;
425 if (!need_hbar && (right - left > available_width || left < 0)) {
427 available_height -= SystemInformation.HorizontalScrollBarHeight;
433 hbar = new ImplicitHScrollBar ();
434 Controls.AddImplicit (hbar);
437 CalcHBar (left, right, need_vbar);
438 } else if (hbar != null)
439 hbar.Visible = false;
443 vbar = new ImplicitVScrollBar ();
444 Controls.AddImplicit (vbar);
447 CalcVBar (top, bottom, need_hbar);
448 } else if (vbar != null)
449 vbar.Visible = false;
451 if (need_hbar && need_vbar) {
452 if (sizegrip == null) {
453 sizegrip = new SizeGrip (this.ParentForm);
454 Controls.AddImplicit (sizegrip);
456 sizegrip.Location = new Point (hbar.Right, vbar.Bottom);
457 sizegrip.Visible = true;
458 XplatUI.SetZOrder (sizegrip.Handle, vbar.Handle, false, false);
459 } else if (sizegrip != null) {
460 sizegrip.Visible = false;
463 XplatUI.InvalidateNC (Handle);
466 private void CalcHBar (int left, int right, bool vert_vis)
468 initializing_scrollbars = true;
471 hbar.Top = ClientRectangle.Bottom - hbar.Height;
472 hbar.Width = ClientRectangle.Width - (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0);
473 hbar.LargeChange = 50;
474 hbar.Minimum = Math.Min (left, 0);
475 hbar.Maximum = Math.Max (right - ClientSize.Width + 51 + (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0), 0);
478 hbar.ValueChanged += new EventHandler (HBarValueChanged);
479 XplatUI.SetZOrder (hbar.Handle, IntPtr.Zero, true, false);
481 initializing_scrollbars = false;
484 private void CalcVBar (int top, int bottom, bool horz_vis)
486 initializing_scrollbars = true;
489 vbar.Left = ClientRectangle.Right - vbar.Width;
490 vbar.Height = ClientRectangle.Height - (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0);
491 vbar.LargeChange = 50;
492 vbar.Minimum = Math.Min (top, 0);
493 vbar.Maximum = Math.Max (bottom - ClientSize.Height + 51 + (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0), 0);
496 vbar.ValueChanged += new EventHandler (VBarValueChanged);
497 XplatUI.SetZOrder (vbar.Handle, IntPtr.Zero, true, false);
499 initializing_scrollbars = false;
502 private void HBarValueChanged (object sender, EventArgs e)
504 if (initializing_scrollbars)
507 if (hbar.Value == hbar_value)
513 int diff = hbar_value - hbar.Value;
514 foreach (Form child in Controls) {
521 hbar_value = hbar.Value;
524 private void VBarValueChanged (object sender, EventArgs e)
526 if (initializing_scrollbars)
529 if (vbar.Value == vbar_value)
535 int diff = vbar_value - vbar.Value;
536 foreach (Form child in Controls) {
543 vbar_value = vbar.Value;
546 private void ArrangeWindows ()
548 if (!IsHandleCreated)
552 if (prev_bottom != -1)
553 change = Bottom - prev_bottom;
555 foreach (Control c in Controls) {
556 Form child = c as Form;
558 if (c == null || !child.Visible)
561 MdiWindowManager wm = child.WindowManager as MdiWindowManager;
562 if (wm.GetWindowState () == FormWindowState.Maximized)
563 child.Bounds = wm.MaximizedBounds;
565 if (wm.GetWindowState () == FormWindowState.Minimized) {
571 prev_bottom = Bottom;
574 internal void ArrangeIconicWindows (bool rearrange_all)
576 Rectangle rect = Rectangle.Empty;
579 foreach (Form form in Controls) {
580 if (form.WindowState != FormWindowState.Minimized)
583 MdiWindowManager wm = (MdiWindowManager) form.WindowManager;
585 if (wm.IconicBounds != Rectangle.Empty && !rearrange_all) {
586 if (form.Bounds != wm.IconicBounds)
587 form.Bounds = wm.IconicBounds;
592 int startx, starty, currentx, currenty;
594 rect.Size = wm.IconicSize;
597 starty = ClientSize.Height - rect.Height;
605 foreach (Form form2 in Controls) {
606 if (form2 == form || form2.window_state != FormWindowState.Minimized)
609 if (form2.Bounds.IntersectsWith(rect)) {
615 currentx += rect.Width;
616 if (currentx + rect.Width > Right) {
618 currenty -= rect.Height;
622 wm.IconicBounds = rect;
623 form.Bounds = wm.IconicBounds;
628 internal void ChildFormClosed (Form form)
630 FormWindowState closed_form_windowstate = form.WindowState;
632 form.Visible = false;
633 Controls.Remove (form);
635 if (Controls.Count == 0) {
636 ((MdiWindowManager) form.window_manager).RaiseDeactivate ();
637 } else if (closed_form_windowstate == FormWindowState.Maximized) {
638 Form current = (Form) Controls [0];
639 current.WindowState = FormWindowState.Maximized;
640 ActivateChild(current);
643 if (Controls.Count == 0) {
644 XplatUI.RequestNCRecalc (Parent.Handle);
645 ParentForm.PerformLayout ();
648 // If we closed the last child, unmerge the menus.
649 // If it's not the last child, the menu will be unmerged
650 // when another child takes focus.
651 MenuStrip parent_menu = form.MdiParent.MainMenuStrip;
653 if (parent_menu != null)
654 if (parent_menu.IsCurrentlyMerged)
655 ToolStripManager.RevertMerge (parent_menu);
659 SetParentText (false);
663 internal void ActivateNextChild ()
665 if (Controls.Count < 1)
667 if (Controls.Count == 1 && Controls[0] == ActiveMdiChild)
670 Form front = (Form) Controls [0];
671 Form form = (Form) Controls [1];
673 ActivateChild (form);
677 internal void ActivatePreviousChild ()
679 if (Controls.Count <= 1)
682 Form back = (Form) Controls [Controls.Count - 1];
684 ActivateChild (back);
687 internal void ActivateChild (Form form)
689 if (Controls.Count < 1)
692 if (ParentForm.is_changing_visible_state > 0)
695 Form current = (Form) Controls [0];
696 bool raise_deactivate = ParentForm.ActiveControl == current;
698 // We want to resize the new active form before it is
699 // made active to avoid flickering. Can't do it in the
700 // normal way (form.WindowState = Maximized) since it's not
701 // active yet and everything would just return to before.
702 // We also won't suspend layout, this way the layout will
703 // happen before the form is made active (and in many cases
704 // before it is visible, which avoids flickering as well).
705 MdiWindowManager wm = (MdiWindowManager)form.WindowManager;
707 if (current.WindowState == FormWindowState.Maximized && form.WindowState != FormWindowState.Maximized && form.Visible) {
708 FormWindowState old_state = form.window_state;
709 SetWindowState (form, old_state, FormWindowState.Maximized, true);
710 wm.was_minimized = form.window_state == FormWindowState.Minimized;
711 form.window_state = FormWindowState.Maximized;
712 SetParentText (false);
715 form.BringToFront ();
716 form.SendControlFocus (form);
717 SetWindowStates (wm);
718 if (current != form) {
719 form.has_focus = false;
720 if (current.IsHandleCreated)
721 XplatUI.InvalidateNC (current.Handle);
722 if (form.IsHandleCreated)
723 XplatUI.InvalidateNC (form.Handle);
724 if (raise_deactivate) {
725 MdiWindowManager current_wm = (MdiWindowManager) current.window_manager;
726 current_wm.RaiseDeactivate ();
730 active_child = (Form) Controls [0];
732 if (active_child.Visible) {
733 bool raise_activated = ParentForm.ActiveControl != active_child;
734 ParentForm.ActiveControl = active_child;
735 if (raise_activated) {
736 MdiWindowManager active_wm = (MdiWindowManager) active_child.window_manager;
737 active_wm.RaiseActivated ();
742 internal override IntPtr AfterTopMostControl ()
744 // order of scrollbars:
747 // bottom = horizontal
748 if (hbar != null && hbar.Visible)
750 // no need to check for sizegrip since it will only
751 // be visible if hbar is visible.
752 if (vbar != null && vbar.Visible)
755 return base.AfterTopMostControl ();
758 internal bool SetWindowStates (MdiWindowManager wm)
761 MDI WindowState behaviour:
762 - If the active window is maximized, all other maximized windows are normalized.
763 - If a normal window gets focus and the original active window was maximized,
764 the normal window gets maximized and the original window gets normalized.
765 - If a minimized window gets focus and the original window was maximized,
766 the minimzed window gets maximized and the original window gets normalized.
767 If the ex-minimized window gets deactivated, it will be normalized.
771 if (setting_windowstates) {
778 bool is_active = wm.IsActive;
779 bool maximize_this = false;
785 ArrayList minimize_these = new ArrayList ();
786 ArrayList normalize_these = new ArrayList ();
788 setting_windowstates = true;
789 foreach (Form frm in mdi_child_list) {
792 } else if (!frm.Visible){
795 if (frm.WindowState == FormWindowState.Maximized && is_active) {
796 maximize_this = true;
797 if (((MdiWindowManager) frm.window_manager).was_minimized) {
798 minimize_these.Add (frm);
800 normalize_these.Add (frm);
805 if (maximize_this && form.WindowState != FormWindowState.Maximized) {
806 wm.was_minimized = form.window_state == FormWindowState.Minimized;
807 form.WindowState = FormWindowState.Maximized;
810 foreach (Form frm in minimize_these)
811 frm.WindowState = FormWindowState.Minimized;
813 foreach (Form frm in normalize_these)
814 frm.WindowState = FormWindowState.Normal;
817 SetParentText (false);
819 XplatUI.RequestNCRecalc (ParentForm.Handle);
820 XplatUI.RequestNCRecalc (Handle);
824 setting_windowstates = false;
827 if (form.MdiParent.MainMenuStrip != null)
828 form.MdiParent.MainMenuStrip.RefreshMdiItems ();
830 // Implicit menu strip merging
831 // - When child is activated
832 // - Parent form must have a MainMenuStrip
833 // - Find the first menustrip on the child
835 MenuStrip parent_menu = form.MdiParent.MainMenuStrip;
837 if (parent_menu != null) {
838 if (parent_menu.IsCurrentlyMerged)
839 ToolStripManager.RevertMerge (parent_menu);
841 MenuStrip child_menu = LookForChildMenu (form);
843 if (form.WindowState != FormWindowState.Maximized)
844 RemoveControlMenuItems (wm);
846 if (form.WindowState == FormWindowState.Maximized) {
849 foreach (ToolStripItem tsi in parent_menu.Items) {
850 if (tsi is MdiControlStrip.SystemMenuItem) {
851 (tsi as MdiControlStrip.SystemMenuItem).MdiForm = form;
853 } else if (tsi is MdiControlStrip.ControlBoxMenuItem) {
854 (tsi as MdiControlStrip.ControlBoxMenuItem).MdiForm = form;
860 parent_menu.SuspendLayout ();
861 parent_menu.Items.Insert (0, new MdiControlStrip.SystemMenuItem (form));
862 parent_menu.Items.Add (new MdiControlStrip.ControlBoxMenuItem (form, MdiControlStrip.ControlBoxType.Close));
863 parent_menu.Items.Add (new MdiControlStrip.ControlBoxMenuItem (form, MdiControlStrip.ControlBoxType.Max));
864 parent_menu.Items.Add (new MdiControlStrip.ControlBoxMenuItem (form, MdiControlStrip.ControlBoxType.Min));
865 parent_menu.ResumeLayout ();
869 if (child_menu != null)
870 ToolStripManager.Merge (child_menu, parent_menu);
874 return maximize_this;
878 private MenuStrip LookForChildMenu (Control parent)
880 foreach (Control c in parent.Controls) {
884 if (c is ToolStripContainer || c is ToolStripPanel) {
885 MenuStrip ms = LookForChildMenu (c);
895 internal void RemoveControlMenuItems (MdiWindowManager wm)
898 MenuStrip parent_menu = form.MdiParent.MainMenuStrip;
900 // Only remove the items if the form requesting still owns the menu items
901 if (parent_menu != null) {
902 parent_menu.SuspendLayout ();
904 for (int i = parent_menu.Items.Count - 1; i >= 0; i--) {
905 if (parent_menu.Items[i] is MdiControlStrip.SystemMenuItem) {
906 if ((parent_menu.Items[i] as MdiControlStrip.SystemMenuItem).MdiForm == form)
907 parent_menu.Items.RemoveAt (i);
908 } else if (parent_menu.Items[i] is MdiControlStrip.ControlBoxMenuItem) {
909 if ((parent_menu.Items[i] as MdiControlStrip.ControlBoxMenuItem).MdiForm == form)
910 parent_menu.Items.RemoveAt (i);
914 parent_menu.ResumeLayout ();
919 internal void SetWindowState (Form form, FormWindowState old_window_state, FormWindowState new_window_state, bool is_activating_child)
921 bool mdiclient_layout;
923 MdiWindowManager wm = (MdiWindowManager) form.window_manager;
925 if (!is_activating_child && new_window_state == FormWindowState.Maximized && !wm.IsActive) {
926 ActivateChild (form);
930 if (old_window_state == FormWindowState.Normal)
931 wm.NormalBounds = form.Bounds;
933 if (SetWindowStates (wm))
936 if (old_window_state == new_window_state)
939 mdiclient_layout = old_window_state == FormWindowState.Maximized || new_window_state == FormWindowState.Maximized;
941 switch (new_window_state) {
942 case FormWindowState.Minimized:
943 ArrangeIconicWindows (false);
945 case FormWindowState.Maximized:
946 form.Bounds = wm.MaximizedBounds;
948 case FormWindowState.Normal:
949 form.Bounds = wm.NormalBounds;
953 wm.UpdateWindowDecorations (new_window_state);
957 if (mdiclient_layout)
958 Parent.PerformLayout ();
960 XplatUI.RequestNCRecalc (Parent.Handle);
961 XplatUI.RequestNCRecalc (form.Handle);
962 if (!setting_windowstates)
965 internal int ChildrenCreated {
966 get { return mdi_created; }
967 set { mdi_created = value; }
970 internal Form ActiveMdiChild {
973 if (ParentForm != null && !ParentForm.Visible)
976 if (Controls.Count < 1)
979 if (!ParentForm.IsHandleCreated)
982 if (!ParentForm.has_been_visible)
985 if (!ParentForm.Visible)
989 for (int i = 0; i < Controls.Count; i++) {
990 if (Controls [i].Visible) {
991 active_child = (Form) Controls [i];
998 ActivateChild (value);
1002 internal void ActivateActiveMdiChild ()
1004 if (ParentForm.is_changing_visible_state > 0)
1007 for (int i = 0; i < Controls.Count; i++) {
1008 if (Controls [i].Visible) {
1009 ActivateChild ((Form) Controls [i]);