* TextBoxBase.cs: Use the new SuspendRecalc/ResumeRecalc methods
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / MdiClient.cs
blobd5367d520a5d612b0ea27f69ad09c4bbdb72fa28
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.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
22 // Authors:
23 // Peter Bartok pbartok@novell.com
27 // NOT COMPLETE
29 using System.Collections;
30 using System.ComponentModel;
31 using System.Drawing;
32 using System.Runtime.InteropServices;
34 namespace System.Windows.Forms {
35 #if NET_2_0
36 [ComVisible (true)]
37 [ClassInterface(ClassInterfaceType.AutoDispatch)]
38 #endif
39 [DesignTimeVisible(false)]
40 [ToolboxItem(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
61 #if NET_2_0
62 [ComVisible (false)]
63 #endif
64 public new class ControlCollection : Control.ControlCollection {
66 private MdiClient owner;
68 public ControlCollection(MdiClient owner) : base(owner) {
69 this.owner = 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);
77 base.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;
87 if (form != null) {
88 MdiWindowManager wm = form.WindowManager as MdiWindowManager;
89 if (wm != null) {
90 form.Closed -= wm.form_closed_handler;
94 owner.mdi_child_list.Remove (value);
95 base.Remove (value);
98 #endregion // Public Classes
100 #region Public Constructors
101 public MdiClient()
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);
115 } else {
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)
131 return;
133 setting_form_text = true;
135 if (text_changed)
136 form_text = ParentForm.Text;
138 if (ParentForm.ActiveMaximizedMdiChild == null) {
139 ParentForm.Text = form_text;
140 } else {
141 string childText = ParentForm.ActiveMaximizedMdiChild.form.Text;
142 if (childText.Length > 0) {
143 ParentForm.Text = form_text + " - [" + ParentForm.ActiveMaximizedMdiChild.form.Text + "]";
144 } else {
145 ParentForm.Text = form_text;
149 setting_form_text = false;
152 internal override void OnPaintBackgroundInternal (PaintEventArgs pe)
154 if (BackgroundImage != null)
155 return;
157 if (Parent == null || Parent.BackgroundImage == null)
158 return;
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));
180 int bw = 2;
182 ncp.rgrc1.top += bw;
183 ncp.rgrc1.bottom -= bw;
184 ncp.rgrc1.left += bw;
185 ncp.rgrc1.right -= bw;
187 Marshal.StructureToPtr (ncp, m.LParam, true);
190 break;
192 case Msg.WM_NCPAINT:
193 PaintEventArgs pe = XplatUI.PaintEventStart (Handle, false);
195 Rectangle clip;
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;
201 return ;
204 base.WndProc (ref m);
207 protected override void OnResize (EventArgs e)
209 base.OnResize (e);
211 if (Parent != null)
212 XplatUI.InvalidateNC (Parent.Handle);
213 // Should probably make this into one loop
214 SizeScrollBars ();
215 ArrangeWindows ();
217 #if NET_2_0
218 [System.ComponentModel.EditorBrowsable (EditorBrowsableState.Never)]
219 #endif
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
231 [Localizable(true)]
232 public override System.Drawing.Image BackgroundImage {
233 get {
234 return base.BackgroundImage;
236 set {
237 base.BackgroundImage = value;
241 #if NET_2_0
242 [EditorBrowsable (EditorBrowsableState.Never)]
243 [Browsable (false)]
244 public override ImageLayout BackgroundImageLayout {
245 get {
246 return base.BackgroundImageLayout;
248 set {
249 base.BackgroundImageLayout = value;
252 #endif
254 public Form [] MdiChildren {
255 get {
256 if (mdi_child_list == null)
257 return new Form [0];
258 return (Form []) mdi_child_list.ToArray (typeof (Form));
261 #endregion // Public Instance Properties
263 #region Protected Instance Properties
264 protected override CreateParams CreateParams {
265 get {
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;
282 switch (value) {
283 case MdiLayout.Cascade: {
284 int i = 0;
285 for (int c = Controls.Count - 1; c >= 0; c--) {
286 Form form = (Form) Controls [c];
288 if (form.WindowState == FormWindowState.Minimized)
289 continue;
291 int l = 22 * i;
292 int t = 22 * i;
294 if (i != 0 && (l + form.Width > max_width || t + form.Height > max_height)) {
295 i = 0;
296 l = 22 * i;
297 t = 22 * i;
300 form.Left = l;
301 form.Top = t;
303 i++;
305 break;
307 case MdiLayout.ArrangeIcons:
308 ArrangeIconicWindows (true);
309 break;
310 case MdiLayout.TileHorizontal:
311 case MdiLayout.TileVertical: {
312 // First count number of windows to tile
313 int total = 0;
314 for (int i = 0; i < Controls.Count; i++) {
315 Form form = Controls [i] as Form;
317 if (form == null)
318 continue;
320 if (!form.Visible)
321 continue;
323 if (form.WindowState == FormWindowState.Minimized)
324 continue;
326 total++;
328 if (total <= 0)
329 return;
331 // Calculate desired height and width
332 Size newSize;
333 Size offset;
334 if (value == MdiLayout.TileHorizontal) {
335 newSize = new Size (ClientSize.Width, ClientSize.Height / total);
336 offset = new Size (0, newSize.Height);
337 } else {
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;
348 if (form == null)
349 continue;
351 if (!form.Visible)
352 continue;
354 if (form.WindowState == FormWindowState.Minimized)
355 continue;
357 form.Size = newSize;
358 form.Location = nextLocation;
359 nextLocation += offset;
362 break;
366 #endregion // Public Instance Methods
368 #region Protected Instance Methods
369 #endregion // Protected Instance Methods
371 internal void SizeScrollBars ()
373 if (lock_sizing)
374 return;
376 if (Controls.Count == 0 || ((Form) Controls [0]).WindowState == FormWindowState.Maximized) {
377 if (hbar != null)
378 hbar.Visible = false;
379 if (vbar != null)
380 vbar.Visible = false;
381 if (sizegrip != null)
382 sizegrip.Visible = false;
383 return;
386 int right = 0;
387 int left = 0;
388 int top = 0;
389 int bottom = 0;
391 foreach (Form child in Controls) {
392 if (!child.Visible)
393 continue;
394 if (child.Right > right)
395 right = child.Right;
396 if (child.Left < left) {
397 left = child.Left;
400 if (child.Bottom > bottom)
401 bottom = child.Bottom;
402 if (child.Top < 0) {
403 top = child.Top;
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) {
414 need_hbar = true;
415 available_height -= SystemInformation.HorizontalScrollBarHeight;
417 if (bottom - top > available_height || top < 0) {
418 need_vbar = true;
419 available_width -= SystemInformation.VerticalScrollBarWidth;
421 if (!need_hbar && (right - left > available_width || left < 0)) {
422 need_hbar = true;
423 available_height -= SystemInformation.HorizontalScrollBarHeight;
427 if (need_hbar) {
428 if (hbar == null) {
429 hbar = new ImplicitHScrollBar ();
430 Controls.AddImplicit (hbar);
432 hbar.Visible = true;
433 CalcHBar (left, right, need_vbar);
434 } else if (hbar != null)
435 hbar.Visible = false;
437 if (need_vbar) {
438 if (vbar == null) {
439 vbar = new ImplicitVScrollBar ();
440 Controls.AddImplicit (vbar);
442 vbar.Visible = true;
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;
467 hbar.Left = 0;
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);
473 hbar.Value = 0;
474 hbar_value = 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;
485 vbar.Top = 0;
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);
491 vbar.Value = 0;
492 vbar_value = 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)
502 return;
504 if (hbar.Value == hbar_value)
505 return;
507 lock_sizing = true;
509 try {
510 int diff = hbar_value - hbar.Value;
511 foreach (Form child in Controls) {
512 child.Left += diff;
514 } finally {
515 lock_sizing = false;
518 hbar_value = hbar.Value;
521 private void VBarValueChanged (object sender, EventArgs e)
523 if (initializing_scrollbars)
524 return;
526 if (vbar.Value == vbar_value)
527 return;
529 lock_sizing = true;
531 try {
532 int diff = vbar_value - vbar.Value;
533 foreach (Form child in Controls) {
534 child.Top += diff;
536 } finally {
537 lock_sizing = false;
540 vbar_value = vbar.Value;
543 private void ArrangeWindows ()
545 int change = 0;
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)
553 continue;
555 MdiWindowManager wm = child.WindowManager as MdiWindowManager;
556 if (wm.GetWindowState () == FormWindowState.Maximized)
557 wm.SizeMaximized ();
559 if (wm.GetWindowState () == FormWindowState.Minimized) {
560 child.Top += change;
565 prev_bottom = Bottom;
568 internal void ArrangeIconicWindows (bool rearrange_all)
570 int xspacing = 160;
571 int yspacing = 25;
573 Rectangle rect = new Rectangle (0, 0, xspacing, yspacing);
575 lock_sizing = true;
576 foreach (Form form in Controls) {
577 if (form.WindowState != FormWindowState.Minimized)
578 continue;
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;
585 continue;
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;
596 bool success = true;
597 int startx, starty, currentx, currenty;
599 startx = 0;
600 starty = Bottom - yspacing - bw - 2;
601 currentx = startx;
602 currenty = starty;
604 do {
605 rect.X = currentx;
606 rect.Y = currenty;
607 rect.Height = height;
608 success = true;
609 foreach (Form form2 in Controls) {
610 if (form2 == form || form2.window_state != FormWindowState.Minimized)
611 continue;
613 if (form2.Bounds.IntersectsWith(rect)) {
614 success = false;
615 break;
618 if (!success) {
619 currentx += xspacing;
620 if (currentx + xspacing > Right) {
621 currentx = startx;
622 currenty -= Math.Max(yspacing, height);
625 } while (!success);
626 wm.IconicBounds = rect;
627 form.Bounds = wm.IconicBounds;
629 lock_sizing = false;
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);
642 form.Close ();
644 XplatUI.RequestNCRecalc (Handle);
645 if (Controls.Count == 0) {
646 XplatUI.RequestNCRecalc (Parent.Handle);
647 ParentForm.PerformLayout ();
649 SizeScrollBars ();
650 SetParentText (false);
653 internal void ActivateNextChild ()
655 if (Controls.Count < 1)
656 return;
657 if (Controls.Count == 1 && Controls[0] == ActiveMdiChild)
658 return;
660 Form front = (Form) Controls [0];
661 Form form = (Form) Controls [1];
663 ActivateChild (form);
664 front.SendToBack ();
667 internal void ActivatePreviousChild ()
669 if (Controls.Count <= 1)
670 return;
672 Form back = (Form) Controls [Controls.Count - 1];
674 ActivateChild (back);
677 internal void ActivateChild (Form form)
679 if (Controls.Count < 1)
680 return;
682 if (ParentForm.is_changing_visible_state)
683 return;
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:
705 // top = vertical
706 // sizegrid
707 // bottom = horizontal
708 if (hbar != null && hbar.Visible)
709 return hbar.Handle;
710 // no need to check for sizegrip since it will only
711 // be visible if hbar is visible.
712 if (vbar != null && vbar.Visible)
713 return vbar.Handle;
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.
729 Form form = wm.form;
731 if (setting_windowstates) {
732 return false;
735 if (!form.Visible)
736 return false;
738 bool is_active = wm.IsActive();
739 bool maximize_this = false;
741 if (!is_active){
742 return false;
745 setting_windowstates = true;
746 foreach (Form frm in mdi_child_list) {
747 if (frm == form) {
748 continue;
749 } else if (!frm.Visible){
750 continue;
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;
756 else
757 frm.WindowState = FormWindowState.Normal;//
760 if (maximize_this) {
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);
769 SizeScrollBars ();
771 setting_windowstates = false;
773 #if NET_2_0
774 if (form.MdiParent.MainMenuStrip != null)
775 form.MdiParent.MainMenuStrip.RefreshMdiItems ();
776 #endif
778 return maximize_this;
781 internal int ChildrenCreated {
782 get { return mdi_created; }
783 set { mdi_created = value; }
786 internal Form ActiveMdiChild {
787 get {
788 #if NET_2_0
789 if (!ParentForm.Visible)
790 return null;
791 #endif
792 if (Controls.Count < 1)
793 return null;
795 if (!ParentForm.IsHandleCreated)
796 return null;
798 if (!ParentForm.has_been_visible)
799 return null;
801 if (!ParentForm.Visible)
802 return active_child;
804 active_child = null;
805 for (int i = 0; i < Controls.Count; i++) {
806 if (Controls [i].Visible) {
807 active_child = (Form) Controls [i];
808 break;
811 return active_child;
813 set {
814 ActivateChild (value);
818 internal void ActivateActiveMdiChild ()
820 if (ParentForm.is_changing_visible_state)
821 return;
823 for (int i = 0; i < Controls.Count; i++) {
824 if (Controls [i].Visible) {
825 ActivateChild ((Form) Controls [i]);
826 return;