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.Runtime.InteropServices;
33 namespace System.Windows.Forms {
35 internal class InternalWindowManager {
36 private Size MinTitleBarSize = new Size (115, 25);
40 internal TitleButton close_button;
41 internal TitleButton maximize_button;
42 internal TitleButton minimize_button;
43 protected Rectangle icon_rect;
45 private TitleButton [] title_buttons = new TitleButton [3];
50 protected Point clicked_point;
51 private FormPos sizing_edge;
52 internal Rectangle virtual_position;
54 private bool is_mouse_down_menu;
56 public class TitleButton {
57 public Rectangle Rectangle;
58 public ButtonState State;
59 public CaptionButton Caption;
60 public EventHandler Clicked;
62 public TitleButton (CaptionButton caption, EventHandler clicked)
87 TopRight = Top | Right,
89 BottomLeft = Bottom | Left,
90 BottomRight = Bottom | Right,
92 AnyEdge = Top | Left | Right | Bottom,
95 public InternalWindowManager (Form form)
99 form.SizeChanged += new EventHandler (FormSizeChangedHandler);
108 public Rectangle CloseButtonRect {
109 get { return close_button.Rectangle; }
110 set { close_button.Rectangle = value; }
113 public Rectangle MinimizeButtonRect {
114 get { return minimize_button.Rectangle; }
115 set { minimize_button.Rectangle = value; }
118 public Rectangle MaximizeButtonRect {
119 get { return maximize_button.Rectangle; }
120 set { maximize_button.Rectangle = value; }
123 public Rectangle IconRect {
124 get { return icon_rect; }
125 set { value = icon_rect; }
128 public int IconWidth {
129 get { return TitleBarHeight - 5; }
132 public virtual bool HandleMessage (ref Message m)
134 switch ((Msg)m.Msg) {
137 // The mouse handling messages are actually
138 // not WM_NC* messages except for the first button and NCMOVEs
139 // down because we capture on the form
141 case Msg.WM_MOUSEMOVE:
142 return HandleMouseMove (form, ref m);
144 case Msg.WM_LBUTTONUP:
145 HandleLButtonUp (ref m);
148 case Msg.WM_RBUTTONDOWN:
149 case Msg.WM_LBUTTONDOWN:
150 return HandleButtonDown (ref m);
151 case Msg.WM_PARENTNOTIFY:
152 if (Control.LowOrder(m.WParam.ToInt32()) == (int) Msg.WM_LBUTTONDOWN)
156 case Msg.WM_NCHITTEST:
157 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
158 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
160 NCPointToClient (ref x, ref y);
162 FormPos pos = FormPosForCoords (x, y);
164 if (pos == FormPos.TitleBar) {
165 m.Result = new IntPtr ((int) HitTest.HTCAPTION);
174 m.Result = new IntPtr ((int) HitTest.HTTOP);
177 m.Result = new IntPtr ((int) HitTest.HTLEFT);
180 m.Result = new IntPtr ((int) HitTest.HTRIGHT);
183 m.Result = new IntPtr ((int) HitTest.HTBOTTOM);
185 case FormPos.TopLeft:
186 m.Result = new IntPtr ((int) HitTest.HTTOPLEFT);
188 case FormPos.TopRight:
189 m.Result = new IntPtr ((int) HitTest.HTTOPRIGHT);
191 case FormPos.BottomLeft:
192 m.Result = new IntPtr ((int) HitTest.HTBOTTOMLEFT);
194 case FormPos.BottomRight:
195 m.Result = new IntPtr ((int) HitTest.HTBOTTOMRIGHT);
198 // We return false so that DefWndProc handles things
203 // Return true from these guys, otherwise win32 will mess up z-order
204 case Msg.WM_NCLBUTTONUP:
205 HandleNCLButtonUp (ref m);
208 case Msg.WM_NCLBUTTONDOWN:
209 HandleNCLButtonDown (ref m);
212 case Msg.WM_NCMOUSEMOVE:
213 HandleNCMouseMove (ref m);
216 case Msg.WM_NCLBUTTONDBLCLK:
217 HandleNCLButtonDblClick (ref m);
220 case Msg.WM_NCMOUSELEAVE:
221 HandleNCMouseLeave (ref m);
224 case Msg.WM_MOUSE_LEAVE:
225 FormMouseLeave (ref m);
228 case Msg.WM_NCCALCSIZE:
229 XplatUIWin32.NCCALCSIZE_PARAMS ncp;
231 if (m.WParam == (IntPtr) 1) {
232 ncp = (XplatUIWin32.NCCALCSIZE_PARAMS) Marshal.PtrToStructure (m.LParam,
233 typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
235 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
238 ncp.rgrc1.top += TitleBarHeight + bw;
239 ncp.rgrc1.bottom -= bw;
240 ncp.rgrc1.left += bw;
241 ncp.rgrc1.right -= bw;
244 Marshal.StructureToPtr(ncp, m.LParam, true);
250 PaintEventArgs pe = XplatUI.PaintEventStart (form.Handle, false);
253 // clip region is not correct on win32.
254 // if (m.WParam.ToInt32 () > 1) {
255 // Region r = Region.FromHrgn (m.WParam);
256 // RectangleF rf = r.GetBounds (pe.Graphics);
257 // clip = new Rectangle ((int) rf.X, (int) rf.Y, (int) rf.Width, (int) rf.Height);
259 clip = new Rectangle (0, 0, form.Width, form.Height);
262 ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this);
263 XplatUI.PaintEventEnd (form.Handle, false);
270 public virtual void UpdateBorderStyle (FormBorderStyle border_style)
272 XplatUI.SetBorderStyle (form.Handle, border_style);
274 if (ShouldRemoveWindowManager (border_style)) {
275 form.RemoveWindowManager ();
282 public bool HandleMenuMouseDown (MainMenu menu, int x, int y)
284 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
286 is_mouse_down_menu = false;
287 foreach (TitleButton button in title_buttons) {
288 if (button != null) {
289 if (button.Rectangle.Contains (pt)) {
290 button.State = ButtonState.Pushed;
291 is_mouse_down_menu = true;
293 button.State = ButtonState.Normal;
297 XplatUI.InvalidateNC (menu.GetForm().Handle);
298 return is_mouse_down_menu;
301 public void HandleMenuMouseUp (MainMenu menu, int x, int y)
303 Point pt = MenuTracker.ScreenToMenu (menu, new Point(x, y));
305 foreach (TitleButton button in title_buttons) {
306 if (button != null) {
307 if (button.Rectangle.Contains (pt)) {
308 button.Clicked (this, EventArgs.Empty);
309 button.State = ButtonState.Pushed;
311 button.State = ButtonState.Normal;
315 XplatUI.InvalidateNC (menu.GetForm().Handle);
316 is_mouse_down_menu = false;
320 public void HandleMenuMouseLeave(MainMenu menu, int x, int y)
322 foreach (TitleButton button in title_buttons) {
323 if (button != null) {
324 button.State = ButtonState.Normal;
327 XplatUI.InvalidateNC (menu.GetForm().Handle);
331 public void HandleMenuMouseMove (MainMenu menu, int x, int y)
333 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
335 if (!is_mouse_down_menu)
338 bool any_change = false;
339 foreach (TitleButton button in title_buttons) {
343 if (button.Rectangle.Contains(pt)) {
344 any_change |= button.State != ButtonState.Pushed;
345 button.State = ButtonState.Pushed;
347 any_change |= button.State != ButtonState.Normal;
348 button.State = ButtonState.Normal;
352 XplatUI.InvalidateNC (menu.GetForm().Handle);
355 public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state)
359 public virtual FormWindowState GetWindowState ()
361 return form.window_state;
364 public virtual void PointToClient (ref int x, ref int y)
366 // toolwindows stay in screencoords we just have to make sure
367 // they obey the working area
368 Rectangle working = SystemInformation.WorkingArea;
370 if (x > working.Right)
372 if (x < working.Left)
377 if (y > working.Bottom)
381 public virtual void PointToScreen (ref int x, ref int y)
383 XplatUI.ClientToScreen (form.Handle, ref x, ref y);
386 protected virtual bool ShouldRemoveWindowManager (FormBorderStyle style)
388 return style != FormBorderStyle.FixedToolWindow && style != FormBorderStyle.SizableToolWindow;
391 protected virtual void Activate ()
393 // Hack to get a paint
394 //NativeWindow.WndProc (form.Handle, Msg.WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
398 public virtual bool IsActive ()
404 private void FormSizeChangedHandler (object sender, EventArgs e)
406 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
407 Message m = new Message ();
408 m.Msg = (int) Msg.WM_NCPAINT;
409 m.HWnd = form.Handle;
410 m.LParam = IntPtr.Zero;
411 m.WParam = new IntPtr (1);
412 XplatUI.SendMessage (ref m);
415 protected void CreateButtons ()
417 switch (form.FormBorderStyle) {
418 case FormBorderStyle.None:
420 minimize_button = null;
421 maximize_button = null;
422 if (IsMaximized || IsMinimized)
423 goto case FormBorderStyle.Sizable;
425 case FormBorderStyle.FixedToolWindow:
426 case FormBorderStyle.SizableToolWindow:
427 close_button = new TitleButton (CaptionButton.Close, new EventHandler (CloseClicked));
428 if (IsMaximized || IsMinimized)
429 goto case FormBorderStyle.Sizable;
431 case FormBorderStyle.FixedSingle:
432 case FormBorderStyle.Fixed3D:
433 case FormBorderStyle.FixedDialog:
434 case FormBorderStyle.Sizable:
435 close_button = new TitleButton (CaptionButton.Close, new EventHandler (CloseClicked));
436 minimize_button = new TitleButton (CaptionButton.Minimize, new EventHandler (MinimizeClicked));
437 maximize_button = new TitleButton (CaptionButton.Maximize, new EventHandler (MaximizeClicked));
441 title_buttons [0] = close_button;
442 title_buttons [1] = minimize_button;
443 title_buttons [2] = maximize_button;
445 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
448 protected virtual bool HandleButtonDown (ref Message m)
454 protected virtual bool HandleNCMouseLeave (ref Message m)
456 int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
457 int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
459 NCPointToClient (ref x, ref y);
460 FormPos pos = FormPosForCoords (x, y);
462 if (pos != FormPos.TitleBar) {
463 HandleTitleBarLeave (x, y);
470 protected virtual bool HandleNCMouseMove (ref Message m)
473 int x = Control.LowOrder((int)m.LParam.ToInt32( ));
474 int y = Control.HighOrder((int)m.LParam.ToInt32( ));
476 NCPointToClient (ref x, ref y);
477 FormPos pos = FormPosForCoords (x, y);
479 if (pos == FormPos.TitleBar) {
480 HandleTitleBarMove (x, y);
488 protected virtual bool HandleNCLButtonDown (ref Message m)
492 start = Cursor.Position;
493 virtual_position = form.Bounds;
495 is_mouse_down_menu = false;
497 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
498 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
500 // Need to adjust because we are in NC land
501 NCPointToClient (ref x, ref y);
502 FormPos pos = FormPosForCoords (x, y);
504 if (pos == FormPos.TitleBar) {
505 HandleTitleBarDown (x, y);
510 if ((pos & FormPos.AnyEdge) == 0)
513 virtual_position = form.Bounds;
514 state = State.Sizing;
523 protected virtual void HandleNCLButtonDblClick (ref Message m)
527 protected virtual void HandleTitleBarLeave (int x, int y)
529 is_mouse_down_menu = false;
532 protected virtual void HandleTitleBarMove (int x, int y)
534 if (!is_mouse_down_menu)
537 bool any_change = false;
538 foreach (TitleButton button in title_buttons) {
542 if (button.Rectangle.Contains (x, y)) {
543 any_change |= button.State != ButtonState.Pushed;
544 button.State = ButtonState.Pushed;
546 any_change |= button.State != ButtonState.Normal;
547 button.State = ButtonState.Normal;
551 XplatUI.InvalidateNC (form.Handle);
554 protected virtual void HandleTitleBarUp (int x, int y)
556 is_mouse_down_menu = false;
558 foreach (TitleButton button in title_buttons) {
562 button.State = ButtonState.Normal;
563 if (button.Rectangle.Contains (x, y)) {
564 button.Clicked (this, EventArgs.Empty);
569 protected virtual void HandleTitleBarDown (int x, int y)
571 foreach (TitleButton button in title_buttons) {
572 if (button != null && button.Rectangle.Contains (x, y)) {
573 button.State = ButtonState.Pushed;
574 XplatUI.InvalidateNC (form.Handle);
575 is_mouse_down_menu = true;
583 state = State.Moving;
584 clicked_point = new Point(x, y);
588 private bool HandleMouseMove (Form form, ref Message m)
592 HandleWindowMove (m);
601 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
602 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
603 FormPos pos = FormPosForCoords (x, y);
604 Console.WriteLine ("position: " + pos);
605 SetCursorForPos (pos);
607 ClearVirtualPosition ();
615 private void FormMouseLeave (ref Message m)
620 protected virtual void HandleWindowMove (Message m)
622 Point move = MouseMove (m);
624 UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y,
625 virtual_position.Width, virtual_position.Height);
628 private void HandleSizing (Message m)
630 Rectangle pos = virtual_position;
631 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
632 int mw = MinTitleBarSize.Width + (bw * 2);
633 int mh = MinTitleBarSize.Height + (bw * 2);
634 int x = Cursor.Position.X;
635 int y = Cursor.Position.Y;
637 PointToClient (ref x, ref y);
639 if ((sizing_edge & FormPos.Top) != 0) {
640 if (pos.Bottom - y < mh)
642 pos.Height = pos.Bottom - y;
644 } else if ((sizing_edge & FormPos.Bottom) != 0) {
645 int height = y - pos.Top;
651 if ((sizing_edge & FormPos.Left) != 0) {
652 if (pos.Right - x < mw)
654 pos.Width = pos.Right - x;
656 } else if ((sizing_edge & FormPos.Right) != 0) {
657 int width = x - form.Left;
666 public bool IsMaximized {
667 get { return GetWindowState () == FormWindowState.Maximized; }
670 public bool IsMinimized {
671 get { return GetWindowState () == FormWindowState.Minimized; }
674 public bool IsSizable {
676 switch (form.FormBorderStyle) {
677 case FormBorderStyle.Sizable:
678 case FormBorderStyle.SizableToolWindow:
679 return (form.window_state != FormWindowState.Minimized);
686 public bool HasBorders {
688 return form.FormBorderStyle != FormBorderStyle.None;
692 public bool IsToolWindow {
694 if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow ||
695 form.FormBorderStyle == FormBorderStyle.FixedToolWindow)
701 public int TitleBarHeight {
703 return ThemeEngine.Current.ManagedWindowTitleBarHeight (this);
707 protected void UpdateVP (Rectangle r)
709 UpdateVP (r.X, r.Y, r.Width, r.Height);
712 protected void UpdateVP (Point loc, int w, int h)
714 UpdateVP (loc.X, loc.Y, w, h);
717 protected void UpdateVP (int x, int y, int w, int h)
719 virtual_position.X = x;
720 virtual_position.Y = y;
721 virtual_position.Width = w;
722 virtual_position.Height = h;
724 DrawVirtualPosition (virtual_position);
727 private void HandleLButtonUp (ref Message m)
729 if (state == State.Idle)
732 ClearVirtualPosition ();
734 form.Capture = false;
735 form.Bounds = virtual_position;
738 OnWindowFinishedMoving ();
741 private bool HandleNCLButtonUp (ref Message m)
744 ClearVirtualPosition ();
746 form.Capture = false;
748 if (form.MdiContainer != null)
749 form.MdiContainer.SizeScrollBars();
752 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
753 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
755 NCPointToClient (ref x, ref y);
756 FormPos pos = FormPosForCoords (x, y);
758 if (pos == FormPos.TitleBar) {
759 HandleTitleBarUp (x, y);
766 protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip)
768 if (!button.Rectangle.IntersectsWith (clip))
771 dc.FillRectangle (SystemBrushes.Control, button.Rectangle);
772 ControlPaint.DrawCaptionButton (dc, button.Rectangle,
773 button.Caption, button.State);
776 public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe)
780 protected virtual void CloseClicked (object sender, EventArgs e)
785 private void MinimizeClicked (object sender, EventArgs e)
787 if (GetWindowState () != FormWindowState.Minimized) {
788 form.WindowState = FormWindowState.Minimized;
790 form.WindowState = FormWindowState.Normal;
794 private void MaximizeClicked (object sender, EventArgs e)
796 if (GetWindowState () != FormWindowState.Maximized) {
797 form.WindowState = FormWindowState.Maximized;
799 form.WindowState = FormWindowState.Normal;
803 protected Point MouseMove (Message m)
805 Point cp = Cursor.Position;
806 return new Point (cp.X - start.X, cp.Y - start.Y);
809 protected virtual void DrawVirtualPosition (Rectangle virtual_position)
811 form.Bounds = virtual_position;
812 start = Cursor.Position;
815 protected virtual void ClearVirtualPosition ()
820 protected virtual void OnWindowFinishedMoving ()
824 protected virtual void NCPointToClient(ref int x, ref int y) {
825 form.PointToClient(ref x, ref y);
827 y += ThemeEngine.Current.ManagedWindowBorderWidth (this);
830 protected FormPos FormPosForCoords (int x, int y)
832 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
833 if (y < TitleBarHeight + bw) {
834 // Console.WriteLine ("A");
835 if (y > bw && x > bw &&
837 return FormPos.TitleBar;
839 if (x < bw || (x < 20 && y < bw))
840 return FormPos.TopLeft;
842 if (x > form.Width - bw ||
843 (x > form.Width - 20 && y < bw))
844 return FormPos.TopRight;
849 } else if (y > form.Height - 20) {
850 // Console.WriteLine ("B");
852 (x < 20 && y > form.Height - bw))
853 return FormPos.BottomLeft;
855 if (x > form.Width - (bw * 2) ||
856 (x > form.Width - 20 &&
857 y > form.Height - bw))
858 return FormPos.BottomRight;
860 if (y > form.Height - (bw * 2))
861 return FormPos.Bottom;
865 // Console.WriteLine ("C");
867 } else if (x > form.Width - (bw * 2)) {
868 // Console.WriteLine ("D");
869 return FormPos.Right;
871 // Console.WriteLine ("E {0}", form.Width - bw);