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
) {
173 case Msg
.WM_NCCALCSIZE
:
174 XplatUIWin32
.NCCALCSIZE_PARAMS ncp
;
176 if (m
.WParam
== (IntPtr
) 1) {
177 ncp
= (XplatUIWin32
.NCCALCSIZE_PARAMS
) Marshal
.PtrToStructure (m
.LParam
,
178 typeof (XplatUIWin32
.NCCALCSIZE_PARAMS
));
183 ncp
.rgrc1
.bottom
-= bw
;
184 ncp
.rgrc1
.left
+= bw
;
185 ncp
.rgrc1
.right
-= bw
;
187 Marshal
.StructureToPtr (ncp
, m
.LParam
, true);
193 PaintEventArgs pe
= XplatUI
.PaintEventStart (Handle
, false);
196 clip
= new Rectangle (0, 0, Width
, Height
);
198 ControlPaint
.DrawBorder3D (pe
.Graphics
, clip
, Border3DStyle
.Sunken
);
199 XplatUI
.PaintEventEnd (Handle
, false);
200 m
.Result
= IntPtr
.Zero
;
204 base.WndProc (ref m
);
207 protected override void OnResize (EventArgs e
)
212 XplatUI
.InvalidateNC (Parent
.Handle
);
213 // Should probably make this into one loop
218 [System
.ComponentModel
.EditorBrowsable (EditorBrowsableState
.Never
)]
220 protected override void ScaleCore (float dx
, float dy
)
222 base.ScaleCore (dx
, dy
);
225 protected override void SetBoundsCore (int x
, int y
, int width
, int height
, BoundsSpecified specified
)
227 base.SetBoundsCore (x
, y
, width
, height
, specified
);
230 #region Public Instance Properties
232 public override System
.Drawing
.Image BackgroundImage
{
234 return base.BackgroundImage
;
237 base.BackgroundImage
= value;
242 [EditorBrowsable (EditorBrowsableState
.Never
)]
244 public override ImageLayout BackgroundImageLayout
{
246 return base.BackgroundImageLayout
;
249 base.BackgroundImageLayout
= value;
254 public Form
[] MdiChildren
{
256 if (mdi_child_list
== null)
258 return (Form
[]) mdi_child_list
.ToArray (typeof (Form
));
261 #endregion // Public Instance Properties
263 #region Protected Instance Properties
264 protected override CreateParams CreateParams
{
266 return base.CreateParams
;
269 #endregion // Protected Instance Properties
271 #region Public Instance Methods
272 public void LayoutMdi (MdiLayout
value) {
274 int max_width
= Int32
.MaxValue
;
275 int max_height
= Int32
.MaxValue
;
277 if (Parent
!= null) {
278 max_width
= Parent
.Width
;
279 max_height
= Parent
.Height
;
283 case MdiLayout
.Cascade
: {
285 for (int c
= Controls
.Count
- 1; c
>= 0; c
--) {
286 Form form
= (Form
) Controls
[c
];
288 if (form
.WindowState
== FormWindowState
.Minimized
)
294 if (i
!= 0 && (l
+ form
.Width
> max_width
|| t
+ form
.Height
> max_height
)) {
307 case MdiLayout
.ArrangeIcons
:
308 ArrangeIconicWindows (true);
310 case MdiLayout
.TileHorizontal
:
311 case MdiLayout
.TileVertical
: {
312 // First count number of windows to tile
314 for (int i
= 0; i
< Controls
.Count
; i
++) {
315 Form form
= Controls
[i
] as Form
;
323 if (form
.WindowState
== FormWindowState
.Minimized
)
331 // Calculate desired height and width
334 if (value == MdiLayout
.TileHorizontal
) {
335 newSize
= new Size (ClientSize
.Width
, ClientSize
.Height
/ total
);
336 offset
= new Size (0, newSize
.Height
);
338 newSize
= new Size (ClientSize
.Width
/ total
, ClientSize
.Height
);
339 offset
= new Size (newSize
.Width
, 0);
342 // Loop again and set the size and location.
343 Point nextLocation
= Point
.Empty
;
345 for (int i
= 0; i
< Controls
.Count
; i
++) {
346 Form form
= Controls
[i
] as Form
;
354 if (form
.WindowState
== FormWindowState
.Minimized
)
358 form
.Location
= nextLocation
;
359 nextLocation
+= offset
;
366 #endregion // Public Instance Methods
368 #region Protected Instance Methods
369 #endregion // Protected Instance Methods
371 internal void SizeScrollBars ()
376 if (Controls
.Count
== 0 || ((Form
) Controls
[0]).WindowState
== FormWindowState
.Maximized
) {
378 hbar
.Visible
= false;
380 vbar
.Visible
= false;
381 if (sizegrip
!= null)
382 sizegrip
.Visible
= false;
391 foreach (Form child
in Controls
) {
394 if (child
.Right
> right
)
396 if (child
.Left
< left
) {
400 if (child
.Bottom
> bottom
)
401 bottom
= child
.Bottom
;
407 int available_width
= ClientSize
.Width
;
408 int available_height
= ClientSize
.Height
;
410 bool need_hbar
= false;
411 bool need_vbar
= false;
413 if (right
- left
> available_width
|| left
< 0) {
415 available_height
-= SystemInformation
.HorizontalScrollBarHeight
;
417 if (bottom
- top
> available_height
|| top
< 0) {
419 available_width
-= SystemInformation
.VerticalScrollBarWidth
;
421 if (!need_hbar
&& (right
- left
> available_width
|| left
< 0)) {
423 available_height
-= SystemInformation
.HorizontalScrollBarHeight
;
429 hbar
= new ImplicitHScrollBar ();
430 Controls
.AddImplicit (hbar
);
433 CalcHBar (left
, right
, need_vbar
);
434 } else if (hbar
!= null)
435 hbar
.Visible
= false;
439 vbar
= new ImplicitVScrollBar ();
440 Controls
.AddImplicit (vbar
);
443 CalcVBar (top
, bottom
, need_hbar
);
444 } else if (vbar
!= null)
445 vbar
.Visible
= false;
447 if (need_hbar
&& need_vbar
) {
448 if (sizegrip
== null) {
449 sizegrip
= new SizeGrip ();
450 sizegrip
.CapturedControl
= this.ParentForm
;
451 Controls
.AddImplicit (sizegrip
);
453 sizegrip
.Location
= new Point (hbar
.Right
, vbar
.Bottom
);
454 sizegrip
.Width
= vbar
.Width
;
455 sizegrip
.Height
= hbar
.Height
;
456 sizegrip
.Visible
= true;
457 XplatUI
.SetZOrder (sizegrip
.Handle
, vbar
.Handle
, false, false);
458 } else if (sizegrip
!= null) {
459 sizegrip
.Visible
= false;
463 private void CalcHBar (int left
, int right
, bool vert_vis
)
465 initializing_scrollbars
= true;
468 hbar
.Top
= ClientRectangle
.Bottom
- hbar
.Height
;
469 hbar
.Width
= ClientRectangle
.Width
- (vert_vis
? SystemInformation
.VerticalScrollBarWidth
: 0);
470 hbar
.LargeChange
= 50;
471 hbar
.Minimum
= Math
.Min (left
, 0);
472 hbar
.Maximum
= Math
.Max (right
- ClientSize
.Width
+ 51 + (vert_vis
? SystemInformation
.VerticalScrollBarWidth
: 0), 0);
475 hbar
.ValueChanged
+= new EventHandler (HBarValueChanged
);
476 XplatUI
.SetZOrder (hbar
.Handle
, IntPtr
.Zero
, true, false);
478 initializing_scrollbars
= false;
481 private void CalcVBar (int top
, int bottom
, bool horz_vis
)
483 initializing_scrollbars
= true;
486 vbar
.Left
= ClientRectangle
.Right
- vbar
.Width
;
487 vbar
.Height
= ClientRectangle
.Height
- (horz_vis
? SystemInformation
.HorizontalScrollBarHeight
: 0);
488 vbar
.LargeChange
= 50;
489 vbar
.Minimum
= Math
.Min (top
, 0);
490 vbar
.Maximum
= Math
.Max (bottom
- ClientSize
.Height
+ 51 + (horz_vis
? SystemInformation
.HorizontalScrollBarHeight
: 0), 0);
493 vbar
.ValueChanged
+= new EventHandler (VBarValueChanged
);
494 XplatUI
.SetZOrder (vbar
.Handle
, IntPtr
.Zero
, true, false);
496 initializing_scrollbars
= false;
499 private void HBarValueChanged (object sender
, EventArgs e
)
501 if (initializing_scrollbars
)
504 if (hbar
.Value
== hbar_value
)
510 int diff
= hbar_value
- hbar
.Value
;
511 foreach (Form child
in Controls
) {
518 hbar_value
= hbar
.Value
;
521 private void VBarValueChanged (object sender
, EventArgs e
)
523 if (initializing_scrollbars
)
526 if (vbar
.Value
== vbar_value
)
532 int diff
= vbar_value
- vbar
.Value
;
533 foreach (Form child
in Controls
) {
540 vbar_value
= vbar
.Value
;
543 private void ArrangeWindows ()
546 if (prev_bottom
!= -1)
547 change
= Bottom
- prev_bottom
;
549 foreach (Control c
in Controls
) {
550 Form child
= c
as Form
;
552 if (c
== null || !child
.Visible
)
555 MdiWindowManager wm
= child
.WindowManager
as MdiWindowManager
;
556 if (wm
.GetWindowState () == FormWindowState
.Maximized
)
559 if (wm
.GetWindowState () == FormWindowState
.Minimized
) {
565 prev_bottom
= Bottom
;
568 internal void ArrangeIconicWindows (bool rearrange_all
)
573 Rectangle rect
= new Rectangle (0, 0, xspacing
, yspacing
);
576 foreach (Form form
in Controls
) {
577 if (form
.WindowState
!= FormWindowState
.Minimized
)
580 MdiWindowManager wm
= (MdiWindowManager
) form
.WindowManager
;
582 if (wm
.IconicBounds
!= Rectangle
.Empty
&& !rearrange_all
) {
583 if (form
.Bounds
!= wm
.IconicBounds
)
584 form
.Bounds
= wm
.IconicBounds
;
588 // Need to get the width in the loop cause some themes might have
589 // different widths for different styles
590 int bw
= ThemeEngine
.Current
.ManagedWindowBorderWidth (wm
);
592 // The extra one pixel is a cheap hack for now until we
593 // handle 0 client sizes properly in the driver
594 int height
= wm
.TitleBarHeight
+ (bw
* 2) + 1;
597 int startx
, starty
, currentx
, currenty
;
600 starty
= Bottom
- yspacing
- bw
- 2;
607 rect
.Height
= height
;
609 foreach (Form form2
in Controls
) {
610 if (form2
== form
|| form2
.window_state
!= FormWindowState
.Minimized
)
613 if (form2
.Bounds
.IntersectsWith(rect
)) {
619 currentx
+= xspacing
;
620 if (currentx
+ xspacing
> Right
) {
622 currenty
-= Math
.Max(yspacing
, height
);
626 wm
.IconicBounds
= rect
;
627 form
.Bounds
= wm
.IconicBounds
;
632 internal void CloseChildForm (Form form
)
634 if (Controls
.Count
> 1) {
635 Form next
= (Form
) Controls
[1];
636 if (form
.WindowState
== FormWindowState
.Maximized
)
637 next
.WindowState
= FormWindowState
.Maximized
;
638 ActivateChild (next
);
641 Controls
.Remove (form
);
644 XplatUI
.RequestNCRecalc (Handle
);
645 if (Controls
.Count
== 0) {
646 XplatUI
.RequestNCRecalc (Parent
.Handle
);
647 ParentForm
.PerformLayout ();
650 SetParentText (false);
653 internal void ActivateNextChild ()
655 if (Controls
.Count
< 1)
657 if (Controls
.Count
== 1 && Controls
[0] == ActiveMdiChild
)
660 Form front
= (Form
) Controls
[0];
661 Form form
= (Form
) Controls
[1];
663 ActivateChild (form
);
667 internal void ActivatePreviousChild ()
669 if (Controls
.Count
<= 1)
672 Form back
= (Form
) Controls
[Controls
.Count
- 1];
674 ActivateChild (back
);
677 internal void ActivateChild (Form form
)
679 if (Controls
.Count
< 1)
682 if (ParentForm
.is_changing_visible_state
)
685 Form current
= (Form
) Controls
[0];
686 form
.SuspendLayout ();
687 form
.BringToFront ();
688 form
.SendControlFocus (form
);
689 form
.ResumeLayout(false);
690 SetWindowStates ((MdiWindowManager
) form
.window_manager
);
691 if (current
!= form
) {
692 form
.has_focus
= false;
693 XplatUI
.InvalidateNC (current
.Handle
);
694 XplatUI
.InvalidateNC (form
.Handle
);
696 active_child
= (Form
) Controls
[0];
698 if (active_child
.Visible
)
699 ParentForm
.ActiveControl
= active_child
;
702 internal override IntPtr
AfterTopMostControl ()
704 // order of scrollbars:
707 // bottom = horizontal
708 if (hbar
!= null && hbar
.Visible
)
710 // no need to check for sizegrip since it will only
711 // be visible if hbar is visible.
712 if (vbar
!= null && vbar
.Visible
)
715 return base.AfterTopMostControl ();
718 internal bool SetWindowStates (MdiWindowManager wm
)
721 MDI WindowState behaviour:
722 - If the active window is maximized, all other maximized windows are normalized.
723 - If a normal window gets focus and the original active window was maximized,
724 the normal window gets maximized and the original window gets normalized.
725 - If a minimized window gets focus and the original window was maximized,
726 the minimzed window gets maximized and the original window gets normalized.
727 If the ex-minimized window gets deactivated, it will be normalized.
731 if (setting_windowstates
) {
738 bool is_active
= wm
.IsActive();
739 bool maximize_this
= false;
745 setting_windowstates
= true;
746 foreach (Form frm
in mdi_child_list
) {
749 } else if (!frm
.Visible
){
752 if (frm
.WindowState
== FormWindowState
.Maximized
&& is_active
) {
753 maximize_this
= true;
754 if (((MdiWindowManager
) frm
.window_manager
).was_minimized
)
755 frm
.WindowState
= FormWindowState
.Minimized
;
757 frm
.WindowState
= FormWindowState
.Normal
;//
761 wm
.was_minimized
= form
.window_state
== FormWindowState
.Minimized
;
762 form
.WindowState
= FormWindowState
.Maximized
;
764 SetParentText(false);
766 XplatUI
.RequestNCRecalc(ParentForm
.Handle
);
767 XplatUI
.RequestNCRecalc (Handle
);
771 setting_windowstates
= false;
774 if (form
.MdiParent
.MainMenuStrip
!= null)
775 form
.MdiParent
.MainMenuStrip
.RefreshMdiItems ();
778 return maximize_this
;
781 internal int ChildrenCreated
{
782 get { return mdi_created; }
783 set { mdi_created = value; }
786 internal Form ActiveMdiChild
{
789 if (!ParentForm
.Visible
)
792 if (Controls
.Count
< 1)
795 if (!ParentForm
.IsHandleCreated
)
798 if (!ParentForm
.has_been_visible
)
801 if (!ParentForm
.Visible
)
805 for (int i
= 0; i
< Controls
.Count
; i
++) {
806 if (Controls
[i
].Visible
) {
807 active_child
= (Form
) Controls
[i
];
814 ActivateChild (value);
818 internal void ActivateActiveMdiChild ()
820 if (ParentForm
.is_changing_visible_state
)
823 for (int i
= 0; i
< Controls
.Count
; i
++) {
824 if (Controls
[i
].Visible
) {
825 ActivateChild ((Form
) Controls
[i
]);