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 internal bool was_minimized
;
43 private PaintEventHandler draw_maximized_buttons
;
44 internal EventHandler form_closed_handler
;
46 private MdiClient mdi_container
;
47 private Rectangle prev_virtual_position
;
49 private Point icon_clicked
;
50 private DateTime icon_clicked_time
;
51 private bool icon_dont_show_popup
;
53 private TitleButtons maximized_title_buttons
;
54 private bool is_visible_pending
;
56 internal bool IsVisiblePending
{
58 return is_visible_pending
;
61 is_visible_pending
= value;
65 private TitleButtons MaximizedTitleButtons
{
67 if (maximized_title_buttons
== null) {
68 maximized_title_buttons
= new TitleButtons (this.Form
);
69 maximized_title_buttons
.CloseButton
.Visible
= true;
70 maximized_title_buttons
.RestoreButton
.Visible
= true;
71 maximized_title_buttons
.MinimizeButton
.Visible
= true;
73 return maximized_title_buttons
;
77 internal override Rectangle MaximizedBounds
{
79 Rectangle pb
= mdi_container
.ClientRectangle
;
80 int bw
= ThemeEngine
.Current
.ManagedWindowBorderWidth (this);
81 int tw
= TitleBarHeight
;
83 Rectangle new_bounds
= new Rectangle (pb
.Left
- bw
,
86 pb
.Height
+ tw
+ bw
* 2);
93 public MdiWindowManager (Form form
, MdiClient mdi_container
) : base (form
)
95 this.mdi_container
= mdi_container
;
96 if (form
.WindowState
== FormWindowState
.Normal
) {
97 NormalBounds
= form
.Bounds
;
99 form_closed_handler
= new EventHandler (FormClosed
);
100 form
.Closed
+= form_closed_handler
;
101 form
.TextChanged
+= new EventHandler (FormTextChangedHandler
);
102 form
.SizeChanged
+= new EventHandler (FormSizeChangedHandler
);
103 form
.LocationChanged
+= new EventHandler (FormLocationChangedHandler
);
104 form
.VisibleChanged
+= new EventHandler (FormVisibleChangedHandler
);
105 draw_maximized_buttons
= new PaintEventHandler (DrawMaximizedButtons
);
109 private void FormVisibleChangedHandler (object sender
, EventArgs e
)
111 if (mdi_container
== null)
115 mdi_container
.ActivateChild (form
);
116 } else if (mdi_container
.Controls
.Count
> 1) {
117 mdi_container
.ActivateActiveMdiChild ();
121 private void FormTextChangedHandler (object sender
, EventArgs e
)
123 mdi_container
.SetParentText (false);
126 if (form
.MdiParent
.MainMenuStrip
!= null)
127 form
.MdiParent
.MainMenuStrip
.RefreshMdiItems ();
131 private void FormLocationChangedHandler (object sender
, EventArgs e
)
133 if (form
.window_state
== FormWindowState
.Minimized
)
134 IconicBounds
= form
.Bounds
;
135 form
.MdiParent
.MdiContainer
.SizeScrollBars ();
138 private void FormSizeChangedHandler (object sender
, EventArgs e
)
140 if (form
.window_state
== FormWindowState
.Maximized
&& form
.Bounds
!= MaximizedBounds
)
141 form
.Bounds
= MaximizedBounds
;
143 form
.MdiParent
.MdiContainer
.SizeScrollBars ();
146 public MainMenu MergedMenu
{
148 if (merged_menu
== null)
149 merged_menu
= CreateMergedMenu ();
154 private MainMenu
CreateMergedMenu ()
156 Form parent
= (Form
) mdi_container
.Parent
;
158 if (parent
.Menu
!= null)
159 clone
= (MainMenu
) parent
.Menu
.CloneMenu ();
161 clone
= new MainMenu ();
163 if (form
.WindowState
== FormWindowState
.Maximized
) {
166 clone
.MergeMenu (form
.Menu
);
167 clone
.MenuChanged
+= new EventHandler (MenuChangedHandler
);
168 clone
.SetForm (parent
);
172 public MainMenu MaximizedMenu
{
174 if (maximized_menu
== null)
175 maximized_menu
= CreateMaximizedMenu ();
176 return maximized_menu
;
180 private MainMenu
CreateMaximizedMenu ()
182 Form parent
= (Form
) mdi_container
.Parent
;
183 MainMenu res
= new MainMenu ();
185 if (parent
.Menu
!= null) {
186 MainMenu clone
= (MainMenu
) parent
.Menu
.CloneMenu ();
187 res
.MergeMenu (clone
);
190 if (form
.Menu
!= null) {
191 MainMenu clone
= (MainMenu
) form
.Menu
.CloneMenu ();
192 res
.MergeMenu (clone
);
195 if (res
.MenuItems
.Count
== 0)
196 res
.MenuItems
.Add (new MenuItem ()); // Dummy item to get the menu height correct
198 res
.MenuItems
.Insert (0, icon_menu
);
200 res
.SetForm (parent
);
204 private void CreateIconMenus ()
206 icon_menu
= new MenuItem ();
207 icon_popup_menu
= new ContextMenu ();
209 icon_menu
.OwnerDraw
= true;
210 icon_menu
.MeasureItem
+= new MeasureItemEventHandler (MeasureIconMenuItem
);
211 icon_menu
.DrawItem
+= new DrawItemEventHandler (DrawIconMenuItem
);
212 icon_menu
.Click
+= new EventHandler (ClickIconMenuItem
);
214 MenuItem restore
= new MenuItem ("Restore", new EventHandler (RestoreItemHandler
));
215 MenuItem move
= new MenuItem ("Move", new EventHandler (MoveItemHandler
));
216 MenuItem size
= new MenuItem ("Size", new EventHandler (SizeItemHandler
));
217 MenuItem minimize
= new MenuItem ("Minimize", new EventHandler (MinimizeItemHandler
));
218 MenuItem maximize
= new MenuItem ("Maximize", new EventHandler (MaximizeItemHandler
));
219 MenuItem close
= new MenuItem ("Close", new EventHandler (CloseItemHandler
));
220 MenuItem next
= new MenuItem ("Next", new EventHandler (NextItemHandler
));
222 icon_menu
.MenuItems
.AddRange (new MenuItem
[] { restore
, move
, size
, minimize
,
223 maximize
, close
, next
});
224 icon_popup_menu
.MenuItems
.AddRange (new MenuItem
[] { restore
, move
, size
, minimize
,
225 maximize
, close
, next
});
228 private void ClickIconMenuItem(object sender
, EventArgs e
)
230 if ((DateTime
.Now
- icon_clicked_time
).TotalMilliseconds
< 500) {
234 icon_clicked_time
= DateTime
.Now
;
235 Point pnt
= Point
.Empty
;
236 pnt
= form
.MdiParent
.PointToScreen (pnt
);
237 pnt
= form
.PointToClient (pnt
);
241 private void ShowPopup (Point pnt
)
243 icon_popup_menu
.MenuItems
[0].Enabled
= form
.window_state
!= FormWindowState
.Normal
; // restore
244 icon_popup_menu
.MenuItems
[1].Enabled
= form
.window_state
!= FormWindowState
.Maximized
; // move
245 icon_popup_menu
.MenuItems
[2].Enabled
= form
.window_state
!= FormWindowState
.Maximized
; // size
246 icon_popup_menu
.MenuItems
[3].Enabled
= form
.window_state
!= FormWindowState
.Minimized
; // minimize
247 icon_popup_menu
.MenuItems
[4].Enabled
= form
.window_state
!= FormWindowState
.Maximized
; // maximize
248 icon_popup_menu
.MenuItems
[5].Enabled
= true; // close
249 icon_popup_menu
.MenuItems
[6].Enabled
= true; // next
251 icon_popup_menu
.Show(form
, pnt
);
254 private void RestoreItemHandler (object sender
, EventArgs e
)
256 form
.WindowState
= FormWindowState
.Normal
;
259 private void MoveItemHandler (object sender
, EventArgs e
)
264 PointToScreen (ref x
, ref y
);
265 Cursor
.Position
= new Point (x
, y
);
266 form
.Cursor
= Cursors
.Cross
;
267 state
= State
.Moving
;
271 private void SizeItemHandler (object sender
, EventArgs e
)
276 PointToScreen (ref x
, ref y
);
277 Cursor
.Position
= new Point (x
, y
);
278 form
.Cursor
= Cursors
.Cross
;
279 state
= State
.Sizing
;
283 private void MinimizeItemHandler (object sender
, EventArgs e
)
285 form
.WindowState
= FormWindowState
.Minimized
;
288 private void MaximizeItemHandler (object sender
, EventArgs e
)
290 if (form
.WindowState
!= FormWindowState
.Maximized
)
291 form
.WindowState
= FormWindowState
.Maximized
;
294 private void CloseItemHandler (object sender
, EventArgs e
)
299 private void NextItemHandler (object sender
, EventArgs e
)
301 mdi_container
.ActivateNextChild ();
304 private void DrawIconMenuItem (object sender
, DrawItemEventArgs de
)
306 de
.Graphics
.DrawIcon (form
.Icon
, new Rectangle (de
.Bounds
.X
+ 2, de
.Bounds
.Y
+ 2,
307 de
.Bounds
.Height
- 4, de
.Bounds
.Height
- 4));
310 private void MeasureIconMenuItem (object sender
, MeasureItemEventArgs me
)
312 int size
= SystemInformation
.MenuHeight
;
313 me
.ItemHeight
= size
;
314 me
.ItemWidth
= size
+ 2; // some padding
317 private void MenuChangedHandler (object sender
, EventArgs e
)
322 public override void PointToClient (ref int x
, ref int y
)
324 XplatUI
.ScreenToClient (mdi_container
.Handle
, ref x
, ref y
);
327 public override void PointToScreen (ref int x
, ref int y
)
329 XplatUI
.ClientToScreen (mdi_container
.Handle
, ref x
, ref y
);
332 public override void UpdateWindowDecorations (FormWindowState window_state
)
334 switch (window_state
) {
335 case FormWindowState
.Minimized
:
336 case FormWindowState
.Normal
:
337 MaximizedMenu
.Paint
-= draw_maximized_buttons
;
338 MaximizedTitleButtons
.Visible
= false;
339 TitleButtons
.Visible
= true;
341 case FormWindowState
.Maximized
:
342 MaximizedMenu
.Paint
+= draw_maximized_buttons
;
343 MaximizedTitleButtons
.Visible
= true;
344 TitleButtons
.Visible
= false;
348 base.UpdateWindowDecorations (window_state
);
351 public override void SetWindowState (FormWindowState old_state
, FormWindowState window_state
)
353 mdi_container
.SetWindowState (form
, old_state
, window_state
, false);
356 private void FormClosed (object sender
, EventArgs e
)
358 mdi_container
.CloseChildForm (form
);
361 if (form
.MdiParent
.MainMenuStrip
!= null)
362 form
.MdiParent
.MainMenuStrip
.RefreshMdiItems ();
366 public override void DrawMaximizedButtons (object sender
, PaintEventArgs pe
)
368 Size bs
= ThemeEngine
.Current
.ManagedWindowButtonSize (this);
369 Point pnt
= XplatUI
.GetMenuOrigin (mdi_container
.ParentForm
.Handle
);
370 int bw
= ThemeEngine
.Current
.ManagedWindowBorderWidth (this);
371 TitleButtons buttons
= MaximizedTitleButtons
;
373 buttons
.Visible
= true;
374 TitleButtons
.Visible
= false;
376 buttons
.CloseButton
.Rectangle
= new Rectangle (mdi_container
.ParentForm
.Size
.Width
- 1 - bw
- bs
.Width
- 2,
377 pnt
.Y
+ 2, bs
.Width
, bs
.Height
);
379 buttons
.RestoreButton
.Rectangle
= new Rectangle (buttons
.CloseButton
.Rectangle
.Left
- 2 - bs
.Width
,
380 pnt
.Y
+ 2, bs
.Width
, bs
.Height
);
382 buttons
.MinimizeButton
.Rectangle
= new Rectangle (buttons
.RestoreButton
.Rectangle
.Left
- bs
.Width
,
383 pnt
.Y
+ 2, bs
.Width
, bs
.Height
);
385 DrawTitleButton (pe
.Graphics
, buttons
.MinimizeButton
, pe
.ClipRectangle
);
386 DrawTitleButton (pe
.Graphics
, buttons
.RestoreButton
, pe
.ClipRectangle
);
387 DrawTitleButton (pe
.Graphics
, buttons
.CloseButton
, pe
.ClipRectangle
);
389 buttons
.MinimizeButton
.Rectangle
.Y
-= pnt
.Y
;
390 buttons
.RestoreButton
.Rectangle
.Y
-= pnt
.Y
;
391 buttons
.CloseButton
.Rectangle
.Y
-= pnt
.Y
;
394 public bool HandleMenuMouseDown (MainMenu menu
, int x
, int y
)
396 Point pt
= MenuTracker
.ScreenToMenu (menu
, new Point (x
, y
));
398 HandleTitleBarDown (pt
.X
, pt
.Y
);
399 return TitleButtons
.AnyPushedTitleButtons
;
402 public void HandleMenuMouseUp (MainMenu menu
, int x
, int y
)
404 Point pt
= MenuTracker
.ScreenToMenu (menu
, new Point (x
, y
));
406 HandleTitleBarUp (pt
.X
, pt
.Y
);
409 public void HandleMenuMouseLeave (MainMenu menu
, int x
, int y
)
411 Point pt
= MenuTracker
.ScreenToMenu (menu
, new Point (x
, y
));
412 HandleTitleBarLeave (pt
.X
, pt
.Y
);
416 public void HandleMenuMouseMove (MainMenu menu
, int x
, int y
)
418 Point pt
= MenuTracker
.ScreenToMenu (menu
, new Point (x
, y
));
420 HandleTitleBarMouseMove (pt
.X
, pt
.Y
);
424 protected override void HandleTitleBarLeave (int x
, int y
)
426 base.HandleTitleBarLeave (x
, y
);
428 if (maximized_title_buttons
!= null) {
429 maximized_title_buttons
.MouseLeave (x
, y
);
433 XplatUI
.InvalidateNC (form
.MdiParent
.Handle
);
436 protected override void HandleTitleBarUp (int x
, int y
)
438 if (IconRectangleContains (x
, y
)) {
439 if (!icon_dont_show_popup
) {
441 ClickIconMenuItem (null, null);
443 ShowPopup (Point
.Empty
);
445 icon_dont_show_popup
= false;
450 bool was_maximized
= IsMaximized
;
451 base.HandleTitleBarUp (x
, y
);
452 if (maximized_title_buttons
!= null && was_maximized
) {
453 maximized_title_buttons
.MouseUp (x
, y
);
457 XplatUI
.InvalidateNC (mdi_container
.Parent
.Handle
);
460 protected override void HandleTitleBarDoubleClick (int x
, int y
)
462 if (IconRectangleContains (x
, y
)) {
465 form
.WindowState
= FormWindowState
.Maximized
;
467 base.HandleTitleBarDoubleClick (x
, y
);
470 protected override void HandleTitleBarDown (int x
, int y
)
472 if (IconRectangleContains (x
, y
)) {
473 if ((DateTime
.Now
- icon_clicked_time
).TotalMilliseconds
< 500 && icon_clicked
.X
== x
&& icon_clicked
.Y
== y
) {
476 icon_clicked_time
= DateTime
.Now
;
484 base.HandleTitleBarDown (x
, y
);
486 if (maximized_title_buttons
!= null) {
487 maximized_title_buttons
.MouseDown (x
, y
);
491 XplatUI
.InvalidateNC (mdi_container
.Parent
.Handle
);
495 protected override void HandleTitleBarMouseMove (int x
, int y
)
497 base.HandleTitleBarMouseMove (x
, y
);
499 if (maximized_title_buttons
!= null) {
500 maximized_title_buttons
.MouseMove (x
, y
);
504 protected override bool HandleLButtonDblClick (ref Message m
)
507 int x
= Control
.LowOrder ((int)m
.LParam
.ToInt32 ());
508 int y
= Control
.HighOrder ((int)m
.LParam
.ToInt32 ());
510 // Correct since we are in NC land.
511 NCClientToNC (ref x
, ref y
);
513 if (IconRectangleContains (x
, y
)) {
514 icon_popup_menu
.Wnd
.Hide ();
519 return base.HandleLButtonDblClick (ref m
);
522 protected override bool HandleLButtonDown (ref Message m
)
525 int x
= Control
.LowOrder ((int)m
.LParam
.ToInt32 ());
526 int y
= Control
.HighOrder ((int)m
.LParam
.ToInt32 ());
528 // Correct y since we are in NC land.
529 NCClientToNC(ref x
, ref y
);
531 if (IconRectangleContains (x
, y
)){
532 if ((DateTime
.Now
- icon_clicked_time
).TotalMilliseconds
< 500) {
533 if (icon_popup_menu
!= null && icon_popup_menu
.Wnd
!= null) {
534 icon_popup_menu
.Wnd
.Hide ();
538 } else if (form
.Capture
) {
539 icon_dont_show_popup
= true;
542 return base.HandleLButtonDown (ref m
);
545 protected override bool ShouldRemoveWindowManager (FormBorderStyle style
)
550 protected override void HandleWindowMove (Message m
)
552 Point move
= MouseMove (m
);
554 if (move
.X
== 0 && move
.Y
== 0)
557 int x
= virtual_position
.X
+ move
.X
;
558 int y
= virtual_position
.Y
+ move
.Y
;
560 Rectangle client
= mdi_container
.ClientRectangle
;
561 if (mdi_container
.VerticalScrollbarVisible
)
562 client
.Width
-= SystemInformation
.VerticalScrollBarWidth
;
563 if (mdi_container
.HorizontalScrollbarVisible
)
564 client
.Height
-= SystemInformation
.HorizontalScrollBarHeight
;
565 if (!client
.Contains (new Point(x
+ clicked_point
.X
, y
+ clicked_point
.Y
)))
568 UpdateVP (x
, y
, form
.Width
, form
.Height
);
569 start
= Cursor
.Position
;
572 protected override bool HandleNCMouseMove (ref Message m
)
574 XplatUI
.RequestAdditionalWM_NCMessages (form
.Handle
, true, true);
575 return base.HandleNCMouseMove (ref m
);
578 protected override void DrawVirtualPosition (Rectangle virtual_position
)
580 ClearVirtualPosition ();
582 if (form
.Parent
!= null)
583 XplatUI
.DrawReversibleRectangle (form
.Parent
.Handle
, virtual_position
, 2);
584 prev_virtual_position
= virtual_position
;
587 protected override void ClearVirtualPosition ()
589 if (prev_virtual_position
!= Rectangle
.Empty
&& form
.Parent
!= null)
590 XplatUI
.DrawReversibleRectangle (form
.Parent
.Handle
,
591 prev_virtual_position
, 2);
592 prev_virtual_position
= Rectangle
.Empty
;
595 protected override void OnWindowFinishedMoving ()
600 public override bool IsActive
{
602 return mdi_container
.ActiveMdiChild
== form
;
606 protected override void Activate ()
608 if (mdi_container
.ActiveMdiChild
!= form
) {
609 mdi_container
.ActivateChild (form
);