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 // Jackson Harper (jackson@ximian.com)
30 using System.Drawing.Drawing2D;
31 using System.Runtime.InteropServices;
33 namespace System.Windows.Forms {
35 internal class MdiWindowManager : InternalWindowManager {
37 private MainMenu merged_menu;
38 private MainMenu maximized_menu;
39 private MenuItem icon_menu;
40 private ContextMenu icon_popup_menu;
41 private FormWindowState prev_window_state;
42 internal bool was_minimized;
44 private PaintEventHandler draw_maximized_buttons;
45 internal EventHandler form_closed_handler;
47 private MdiClient mdi_container;
48 private Rectangle prev_virtual_position;
49 private Rectangle prev_bounds;
51 private Rectangle iconic_bounds;
53 private Point icon_clicked;
54 private DateTime icon_clicked_time;
55 private bool icon_dont_show_popup;
57 internal Rectangle IconicBounds {
59 if (iconic_bounds == Rectangle.Empty)
60 return Rectangle.Empty;
61 Rectangle result = iconic_bounds;
62 result.Y = mdi_container.ClientRectangle.Bottom - iconic_bounds.Y;
66 iconic_bounds = value;
67 iconic_bounds.Y = mdi_container.ClientRectangle.Bottom - iconic_bounds.Y;
71 public MdiWindowManager (Form form, MdiClient mdi_container) : base (form)
73 this.mdi_container = mdi_container;
74 prev_bounds = form.Bounds;
75 prev_window_state = form.window_state;
76 form_closed_handler = new EventHandler (FormClosed);
77 form.Closed += form_closed_handler;
78 form.TextChanged += new EventHandler (FormTextChangedHandler);
79 form.SizeChanged += new EventHandler (FormSizeChangedHandler);
80 form.LocationChanged += new EventHandler (FormLocationChangedHandler);
81 form.VisibleChanged += new EventHandler (FormVisibleChangedHandler);
82 draw_maximized_buttons = new PaintEventHandler (DrawMaximizedButtons);
86 private void FormVisibleChangedHandler (object sender, EventArgs e)
88 if (mdi_container == null)
92 mdi_container.ActivateChild (form);
93 } else if (mdi_container.Controls.Count > 1) {
94 mdi_container.ActivateActiveMdiChild ();
98 private void FormTextChangedHandler (object sender, EventArgs e)
100 mdi_container.SetParentText (false);
103 if (form.MdiParent.MainMenuStrip != null)
104 form.MdiParent.MainMenuStrip.RefreshMdiItems ();
108 private void FormLocationChangedHandler (object sender, EventArgs e)
110 if (form.window_state == FormWindowState.Minimized)
111 IconicBounds = form.Bounds;
112 XplatUI.RequestNCRecalc (mdi_container.Handle);
113 form.MdiParent.MdiContainer.SizeScrollBars ();
116 private void FormSizeChangedHandler (object sender, EventArgs e)
118 XplatUI.RequestNCRecalc (form.MdiParent.MdiContainer.Handle);
119 XplatUI.RequestNCRecalc (form.Handle);
120 form.MdiParent.MdiContainer.SizeScrollBars ();
123 public MainMenu MergedMenu {
125 if (merged_menu == null)
126 merged_menu = CreateMergedMenu ();
131 private MainMenu CreateMergedMenu ()
133 Form parent = (Form) mdi_container.Parent;
134 MainMenu clone = (MainMenu) parent.Menu.CloneMenu ();
135 if (form.WindowState == FormWindowState.Maximized) {
138 clone.MergeMenu (form.Menu);
139 clone.MenuChanged += new EventHandler (MenuChangedHandler);
140 clone.SetForm (parent);
144 public MainMenu MaximizedMenu {
146 if (maximized_menu == null)
147 maximized_menu = CreateMaximizedMenu ();
148 return maximized_menu;
152 private MainMenu CreateMaximizedMenu ()
154 Form parent = (Form) mdi_container.Parent;
155 MainMenu res = new MainMenu ();
157 res.MenuItems.Add (icon_menu);
159 if (parent.Menu != null) {
160 MainMenu clone = (MainMenu) parent.Menu.CloneMenu ();
161 res.MergeMenu (clone);
164 if (form.Menu != null) {
165 MainMenu clone = (MainMenu) form.Menu.CloneMenu ();
166 res.MergeMenu (clone);
169 if (res.MenuItems.Count == 0)
170 res.MenuItems.Add (new MenuItem ()); // Dummy item to get the menu height correct
172 res.SetForm (parent);
176 private void CreateIconMenus ()
178 icon_menu = new MenuItem ();
179 icon_popup_menu = new ContextMenu ();
181 icon_menu.OwnerDraw = true;
182 icon_menu.MeasureItem += new MeasureItemEventHandler (MeasureIconMenuItem);
183 icon_menu.DrawItem += new DrawItemEventHandler (DrawIconMenuItem);
184 icon_menu.Click += new EventHandler (ClickIconMenuItem);
186 MenuItem restore = new MenuItem ("Restore", new EventHandler (RestoreItemHandler));
187 MenuItem move = new MenuItem ("Move", new EventHandler (MoveItemHandler));
188 MenuItem size = new MenuItem ("Size", new EventHandler (SizeItemHandler));
189 MenuItem minimize = new MenuItem ("Minimize", new EventHandler (MinimizeItemHandler));
190 MenuItem maximize = new MenuItem ("Maximize", new EventHandler (MaximizeItemHandler));
191 MenuItem close = new MenuItem ("Close", new EventHandler (CloseItemHandler));
192 MenuItem next = new MenuItem ("Next", new EventHandler (NextItemHandler));
194 icon_menu.MenuItems.AddRange (new MenuItem [] { restore, move, size, minimize,
195 maximize, close, next });
196 icon_popup_menu.MenuItems.AddRange (new MenuItem [] { restore, move, size, minimize,
197 maximize, close, next });
200 private void ClickIconMenuItem(object sender, EventArgs e)
202 if ((DateTime.Now - icon_clicked_time).TotalMilliseconds < 500) {
206 icon_clicked_time = DateTime.Now;
207 Point pnt = Point.Empty;
208 pnt = form.MdiParent.PointToScreen (pnt);
209 pnt = form.PointToClient (pnt);
213 private void ShowPopup (Point pnt)
215 icon_popup_menu.MenuItems[0].Enabled = form.window_state != FormWindowState.Normal; // restore
216 icon_popup_menu.MenuItems[1].Enabled = form.window_state != FormWindowState.Maximized; // move
217 icon_popup_menu.MenuItems[2].Enabled = form.window_state != FormWindowState.Maximized; // size
218 icon_popup_menu.MenuItems[3].Enabled = form.window_state != FormWindowState.Minimized; // minimize
219 icon_popup_menu.MenuItems[4].Enabled = form.window_state != FormWindowState.Maximized; // maximize
220 icon_popup_menu.MenuItems[5].Enabled = true; // close
221 icon_popup_menu.MenuItems[6].Enabled = true; // next
223 icon_popup_menu.Show(form, pnt);
226 private void RestoreItemHandler (object sender, EventArgs e)
228 form.WindowState = FormWindowState.Normal;
231 private void MoveItemHandler (object sender, EventArgs e)
236 PointToScreen (ref x, ref y);
237 Cursor.Position = new Point (x, y);
238 form.Cursor = Cursors.Cross;
239 state = State.Moving;
243 private void SizeItemHandler (object sender, EventArgs e)
248 PointToScreen (ref x, ref y);
249 Cursor.Position = new Point (x, y);
250 form.Cursor = Cursors.Cross;
251 state = State.Sizing;
255 private void MinimizeItemHandler (object sender, EventArgs e)
257 form.WindowState = FormWindowState.Minimized;
260 private void MaximizeItemHandler (object sender, EventArgs e)
262 if (form.WindowState != FormWindowState.Maximized)
263 form.WindowState = FormWindowState.Maximized;
266 private void CloseItemHandler (object sender, EventArgs e)
271 private void NextItemHandler (object sender, EventArgs e)
273 mdi_container.ActivateNextChild ();
276 private void DrawIconMenuItem (object sender, DrawItemEventArgs de)
278 de.Graphics.DrawIcon (form.Icon, new Rectangle (de.Bounds.X + 2, de.Bounds.Y + 2,
279 de.Bounds.Height - 4, de.Bounds.Height - 4));
282 private void MeasureIconMenuItem (object sender, MeasureItemEventArgs me)
284 int size = SystemInformation.MenuHeight;
285 me.ItemHeight = size;
286 me.ItemWidth = size + 2; // some padding
289 private void MenuChangedHandler (object sender, EventArgs e)
294 public override void PointToClient (ref int x, ref int y)
296 XplatUI.ScreenToClient (mdi_container.Handle, ref x, ref y);
299 public override void PointToScreen (ref int x, ref int y)
301 XplatUI.ClientToScreen (mdi_container.Handle, ref x, ref y);
304 public override void SetWindowState (FormWindowState old_state, FormWindowState window_state)
306 if (this.mdi_container.SetWindowStates (this))
309 if (prev_window_state == window_state)
312 if (prev_window_state == FormWindowState.Normal)
313 prev_bounds = form.Bounds;
315 switch (window_state) {
316 case FormWindowState.Minimized:
318 maximize_button.Caption = CaptionButton.Maximize;
319 minimize_button.Caption = CaptionButton.Restore;
320 prev_window_state = FormWindowState.Minimized;
321 mdi_container.ArrangeIconicWindows (false);
322 MaximizedMenu.Paint -= draw_maximized_buttons;
324 case FormWindowState.Maximized:
325 if (mdi_container.ActiveMdiChild != form)
326 mdi_container.ActivateChild (form);
328 maximize_button.Caption = CaptionButton.Restore;
329 minimize_button.Caption = CaptionButton.Minimize;
330 prev_window_state = FormWindowState.Maximized;
332 MaximizedMenu.Paint += draw_maximized_buttons;
334 case FormWindowState.Normal:
336 maximize_button.Caption = CaptionButton.Maximize;
337 minimize_button.Caption = CaptionButton.Minimize;
338 form.Bounds = prev_bounds;
339 prev_window_state =FormWindowState.Normal;
341 MaximizedMenu.Paint -= draw_maximized_buttons;
346 XplatUI.RequestNCRecalc (mdi_container.Parent.Handle);
347 XplatUI.RequestNCRecalc (form.Handle);
348 mdi_container.SizeScrollBars ();
351 internal void SizeMaximized ()
353 Rectangle pb = mdi_container.ClientRectangle;
354 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
355 form.Bounds = new Rectangle (pb.Left - bw,
356 pb.Top - TitleBarHeight - bw,
358 pb.Height + TitleBarHeight + bw * 2);
361 private void FormClosed (object sender, EventArgs e)
363 mdi_container.CloseChildForm (form);
366 if (form.MdiParent.MainMenuStrip != null)
367 form.MdiParent.MainMenuStrip.RefreshMdiItems ();
371 public override void DrawMaximizedButtons (object sender, PaintEventArgs pe)
373 Size bs = ThemeEngine.Current.ManagedWindowButtonSize (this);
374 Point pnt = XplatUI.GetMenuOrigin (mdi_container.ParentForm.Handle);
375 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
377 close_button.Rectangle = new Rectangle (mdi_container.ParentForm.Size.Width - 1 - bw - bs.Width - 2,
378 pnt.Y + 2, bs.Width, bs.Height);
380 maximize_button.Rectangle = new Rectangle (close_button.Rectangle.Left - 2 - bs.Width,
381 pnt.Y + 2, bs.Width, bs.Height);
383 minimize_button.Rectangle = new Rectangle (maximize_button.Rectangle.Left - bs.Width,
384 pnt.Y + 2, bs.Width, bs.Height);
386 DrawTitleButton (pe.Graphics, minimize_button, pe.ClipRectangle);
387 DrawTitleButton (pe.Graphics, maximize_button, pe.ClipRectangle);
388 DrawTitleButton (pe.Graphics, close_button, pe.ClipRectangle);
390 minimize_button.Rectangle.Y -= pnt.Y;
391 maximize_button.Rectangle.Y -= pnt.Y;
392 close_button.Rectangle.Y -= pnt.Y;
395 private bool IconRectangleContains (int x, int y)
397 if (form.Icon == null)
400 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
401 Rectangle icon = new Rectangle (bw + 3, bw + 2, IconWidth, IconWidth);
402 return icon.Contains (x, y);
405 public bool HandleMenuMouseDown (MainMenu menu, int x, int y)
407 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
409 HandleTitleBarDown (pt.X, pt.Y);
410 return AnyPushedTitleButtons;
413 public void HandleMenuMouseUp (MainMenu menu, int x, int y)
415 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
417 HandleTitleBarUp (pt.X, pt.Y);
420 public void HandleMenuMouseLeave (MainMenu menu, int x, int y)
422 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
423 HandleTitleBarLeave (pt.X, pt.Y);
427 public void HandleMenuMouseMove (MainMenu menu, int x, int y)
429 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
431 HandleTitleBarMouseMove (pt.X, pt.Y);
435 protected override void HandleTitleBarLeave (int x, int y)
437 base.HandleTitleBarLeave (x, y);
440 XplatUI.InvalidateNC (form.MdiParent.Handle);
443 protected override void HandleTitleBarUp (int x, int y)
445 if (IconRectangleContains (x, y)) {
446 if (!icon_dont_show_popup) {
448 ClickIconMenuItem (null, null);
450 ShowPopup (Point.Empty);
452 icon_dont_show_popup = false;
457 base.HandleTitleBarUp (x, y);
460 XplatUI.InvalidateNC (mdi_container.Parent.Handle);
463 protected override void HandleTitleBarDoubleClick (int x, int y)
465 if (IconRectangleContains (x, y)) {
468 form.WindowState = FormWindowState.Maximized;
470 base.HandleTitleBarDoubleClick (x, y);
473 protected override void HandleTitleBarDown (int x, int y)
475 if (IconRectangleContains (x, y)) {
476 if ((DateTime.Now - icon_clicked_time).TotalMilliseconds < 500 && icon_clicked.X == x && icon_clicked.Y == y) {
479 icon_clicked_time = DateTime.Now;
487 base.HandleTitleBarDown (x, y);
491 XplatUI.InvalidateNC (mdi_container.Parent.Handle);
495 protected override bool HandleLButtonDblClick (ref Message m)
498 int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
499 int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
501 // Correct since we are in NC land.
502 NCClientToNC (ref x, ref y);
504 if (IconRectangleContains (x, y)) {
505 icon_popup_menu.Wnd.Hide ();
510 return base.HandleLButtonDblClick (ref m);
513 protected override bool HandleLButtonDown (ref Message m)
516 int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
517 int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
519 // Correct y since we are in NC land.
520 NCClientToNC(ref x, ref y);
522 if (IconRectangleContains (x, y)){
523 if ((DateTime.Now - icon_clicked_time).TotalMilliseconds < 500) {
524 if (icon_popup_menu != null && icon_popup_menu.Wnd != null) {
525 icon_popup_menu.Wnd.Hide ();
529 } else if (form.Capture) {
530 icon_dont_show_popup = true;
533 return base.HandleLButtonDown (ref m);
536 protected override bool HandleNCLButtonDown (ref Message m)
538 return base.HandleNCLButtonDown (ref m);
541 protected override bool ShouldRemoveWindowManager (FormBorderStyle style)
546 protected override void HandleWindowMove (Message m)
548 Point move = MouseMove (m);
550 if (move.X == 0 && move.Y == 0)
553 int x = virtual_position.X + move.X;
554 int y = virtual_position.Y + move.Y;
556 Rectangle client = mdi_container.ClientRectangle;
557 if (mdi_container.VerticalScrollbarVisible)
558 client.Width -= SystemInformation.VerticalScrollBarWidth;
559 if (mdi_container.HorizontalScrollbarVisible)
560 client.Height -= SystemInformation.HorizontalScrollBarHeight;
561 if (!client.Contains (new Point(x + clicked_point.X, y + clicked_point.Y)))
564 UpdateVP (x, y, form.Width, form.Height);
565 start = Cursor.Position;
568 protected override bool HandleNCMouseMove (ref Message m)
570 XplatUI.RequestAdditionalWM_NCMessages (form.Handle, true, true);
571 return base.HandleNCMouseMove (ref m);
574 protected override void HandleNCCalcSize (ref Message m)
576 XplatUIWin32.NCCALCSIZE_PARAMS ncp;
578 if (m.WParam == (IntPtr)1) {
579 ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam,
580 typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
582 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
587 ncp.rgrc1.top += TitleBarHeight + bw;
589 //ncp.rgrc1.top -= 1;
590 ncp.rgrc1.left += bw - 2;
591 ncp.rgrc1.bottom -= bw - 2;
592 ncp.rgrc1.right -= bw;
595 ncp.rgrc1.bottom -= 2;
596 ncp.rgrc1.right -= 3;
600 if (ncp.rgrc1.bottom < ncp.rgrc1.top)
601 ncp.rgrc1.bottom = ncp.rgrc1.top + 1;
603 Marshal.StructureToPtr (ncp, m.LParam, true);
607 protected override void DrawVirtualPosition (Rectangle virtual_position)
609 ClearVirtualPosition ();
611 if (form.Parent != null)
612 XplatUI.DrawReversibleRectangle (form.Parent.Handle, virtual_position, 2);
613 prev_virtual_position = virtual_position;
616 protected override void ClearVirtualPosition ()
618 if (prev_virtual_position != Rectangle.Empty && form.Parent != null)
619 XplatUI.DrawReversibleRectangle (form.Parent.Handle,
620 prev_virtual_position, 2);
621 prev_virtual_position = Rectangle.Empty;
624 protected override void OnWindowFinishedMoving ()
629 public override bool IsActive ()
631 return mdi_container.ActiveMdiChild == form;
634 protected override void Activate ()
636 if (mdi_container.ActiveMdiChild != form) {
637 mdi_container.ActivateChild (form);
638 mdi_container.SetWindowStates (this);