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 {
37 private static Color titlebar_color;
39 private Size MinTitleBarSize = new Size (115, 25);
43 internal TitleButton close_button;
44 internal TitleButton maximize_button;
45 internal TitleButton minimize_button;
46 protected Rectangle icon_rect;
48 private TitleButton [] title_buttons = new TitleButton [3];
53 private FormPos sizing_edge;
54 internal Rectangle virtual_position;
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)
97 titlebar_color = Color.FromArgb (255, 0, 0, 255);
100 form.SizeChanged += new EventHandler (FormSizeChangedHandler);
109 public Rectangle CloseButtonRect {
110 get { return close_button.Rectangle; }
111 set { close_button.Rectangle = value; }
114 public Rectangle MinimizeButtonRect {
115 get { return minimize_button.Rectangle; }
116 set { minimize_button.Rectangle = value; }
119 public Rectangle MaximizeButtonRect {
120 get { return maximize_button.Rectangle; }
121 set { maximize_button.Rectangle = value; }
124 public Rectangle IconRect {
125 get { return icon_rect; }
126 set { value = icon_rect; }
129 public int IconWidth {
130 get { return TitleBarHeight - 5; }
133 public bool HandleMessage (ref Message m)
135 switch ((Msg)m.Msg) {
138 // The mouse handling messages are actually
139 // not WM_NC* messages except for the first button and NCMOVEs
140 // down because we capture on the form
142 case Msg.WM_MOUSEMOVE:
143 return HandleMouseMove (form, ref m);
145 case Msg.WM_LBUTTONUP:
146 HandleLButtonUp (ref m);
149 case Msg.WM_RBUTTONDOWN:
150 case Msg.WM_LBUTTONDOWN:
151 return HandleButtonDown (ref m);
153 case Msg.WM_NCHITTEST:
154 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
155 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
157 NCPointToClient (ref x, ref y);
159 FormPos pos = FormPosForCoords (x, y);
161 if (pos == FormPos.TitleBar) {
162 m.Result = new IntPtr ((int) HitTest.HTCAPTION);
171 m.Result = new IntPtr ((int) HitTest.HTTOP);
174 m.Result = new IntPtr ((int) HitTest.HTLEFT);
177 m.Result = new IntPtr ((int) HitTest.HTRIGHT);
180 m.Result = new IntPtr ((int) HitTest.HTBOTTOM);
182 case FormPos.TopLeft:
183 m.Result = new IntPtr ((int) HitTest.HTTOPLEFT);
185 case FormPos.TopRight:
186 m.Result = new IntPtr ((int) HitTest.HTTOPRIGHT);
188 case FormPos.BottomLeft:
189 m.Result = new IntPtr ((int) HitTest.HTBOTTOMLEFT);
191 case FormPos.BottomRight:
192 m.Result = new IntPtr ((int) HitTest.HTBOTTOMRIGHT);
195 // We return false so that DefWndProc handles things
200 // Return true from these guys, otherwise win32 will mess up z-order
201 case Msg.WM_NCLBUTTONUP:
202 HandleNCLButtonUp (ref m);
205 case Msg.WM_NCLBUTTONDOWN:
206 HandleNCLButtonDown (ref m);
209 case Msg.WM_NCLBUTTONDBLCLK:
210 HandleNCLButtonDblClick (ref m);
213 case Msg.WM_MOUSE_LEAVE:
214 FormMouseLeave (ref m);
217 case Msg.WM_NCCALCSIZE:
218 XplatUIWin32.NCCALCSIZE_PARAMS ncp;
220 if (m.WParam == (IntPtr) 1) {
221 ncp = (XplatUIWin32.NCCALCSIZE_PARAMS) Marshal.PtrToStructure (m.LParam,
222 typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
224 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
227 ncp.rgrc1.top += TitleBarHeight + bw;
228 ncp.rgrc1.bottom -= bw;
229 ncp.rgrc1.left += bw;
230 ncp.rgrc1.right -= bw;
233 Marshal.StructureToPtr(ncp, m.LParam, true);
239 PaintEventArgs pe = XplatUI.PaintEventStart (form.Handle, false);
242 if (m.WParam.ToInt32 () > 1) {
243 Region r = Region.FromHrgn (m.WParam);
244 RectangleF rf = r.GetBounds (pe.Graphics);
245 clip = new Rectangle ((int) rf.X, (int) rf.Y, (int) rf.Width, (int) rf.Height);
247 clip = new Rectangle (0, 0, form.Width, form.Height);
250 ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this);
251 XplatUI.PaintEventEnd (form.Handle, false);
258 public virtual void UpdateBorderStyle (FormBorderStyle border_style)
260 XplatUI.SetBorderStyle (form.Handle, border_style);
262 if (ShouldRemoveWindowManager (border_style)) {
263 form.RemoveWindowManager ();
270 public void HandleMenuMouseDown (MainMenu menu, int x, int y)
272 Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y));
274 foreach (TitleButton button in title_buttons) {
275 if (button != null && button.Rectangle.Contains (pt)) {
276 button.Clicked (this, EventArgs.Empty);
277 button.State = ButtonState.Pushed;
283 public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state)
287 public virtual FormWindowState GetWindowState ()
289 return form.window_state;
292 public virtual void PointToClient (ref int x, ref int y)
294 // toolwindows stay in screencoords we just have to make sure
295 // they obey the working area
296 Rectangle working = SystemInformation.WorkingArea;
298 if (x > working.Right)
300 if (x < working.Left)
305 if (y > working.Bottom)
309 public virtual void PointToScreen (ref int x, ref int y)
311 XplatUI.ClientToScreen (form.Handle, ref x, ref y);
314 protected virtual bool ShouldRemoveWindowManager (FormBorderStyle style)
316 return style != FormBorderStyle.FixedToolWindow && style != FormBorderStyle.SizableToolWindow;
319 protected virtual void Activate ()
321 // Hack to get a paint
322 NativeWindow.WndProc (form.Handle, Msg.WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
326 public virtual bool IsActive ()
331 private void FormSizeChangedHandler (object sender, EventArgs e)
333 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
334 Message m = new Message ();
335 m.Msg = (int) Msg.WM_NCPAINT;
336 m.HWnd = form.Handle;
337 m.LParam = IntPtr.Zero;
338 m.WParam = new IntPtr (1);
339 XplatUI.SendMessage (ref m);
342 protected void CreateButtons ()
344 switch (form.FormBorderStyle) {
345 case FormBorderStyle.None:
347 minimize_button = null;
348 maximize_button = null;
349 if (IsMaximized || IsMinimized)
350 goto case FormBorderStyle.Sizable;
352 case FormBorderStyle.FixedToolWindow:
353 case FormBorderStyle.SizableToolWindow:
354 close_button = new TitleButton (CaptionButton.Close, new EventHandler (CloseClicked));
355 if (IsMaximized || IsMinimized)
356 goto case FormBorderStyle.Sizable;
358 case FormBorderStyle.FixedSingle:
359 case FormBorderStyle.Fixed3D:
360 case FormBorderStyle.FixedDialog:
361 case FormBorderStyle.Sizable:
362 close_button = new TitleButton (CaptionButton.Close, new EventHandler (CloseClicked));
363 minimize_button = new TitleButton (CaptionButton.Minimize, new EventHandler (MinimizeClicked));
364 maximize_button = new TitleButton (CaptionButton.Maximize, new EventHandler (MaximizeClicked));
368 title_buttons [0] = close_button;
369 title_buttons [1] = minimize_button;
370 title_buttons [2] = maximize_button;
372 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
375 protected virtual bool HandleButtonDown (ref Message m)
381 protected virtual bool HandleNCLButtonDown (ref Message m)
385 start = Cursor.Position;
386 virtual_position = form.Bounds;
388 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
389 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
391 // Need to adjust because we are in NC land
392 NCPointToClient (ref x, ref y);
\r
393 FormPos pos = FormPosForCoords (x, y);
394 Console.WriteLine ("NC POS: {0} for coords: {1}, {2}", pos, x, y);
396 if (pos == FormPos.TitleBar) {
397 HandleTitleBarDown (x, y);
402 if ((pos & FormPos.AnyEdge) == 0)
405 virtual_position = form.Bounds;
406 state = State.Sizing;
415 protected virtual void HandleNCLButtonDblClick (ref Message m)
419 protected virtual void HandleTitleBarDown (int x, int y)
421 foreach (TitleButton button in title_buttons) {
422 if (button != null && button.Rectangle.Contains (x, y)) {
423 button.State = ButtonState.Pushed;
431 state = State.Moving;
435 private bool HandleMouseMove (Form form, ref Message m)
439 HandleWindowMove (m);
448 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
449 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
450 FormPos pos = FormPosForCoords (x, y);
451 Console.WriteLine ("position: " + pos);
452 SetCursorForPos (pos);
454 ClearVirtualPosition ();
462 private void FormMouseLeave (ref Message m)
467 protected virtual void HandleWindowMove (Message m)
469 Point move = MouseMove (m);
471 UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y,
472 virtual_position.Width, virtual_position.Height);
475 private void HandleSizing (Message m)
477 Rectangle pos = virtual_position;
478 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
479 int mw = MinTitleBarSize.Width + (bw * 2);
480 int mh = MinTitleBarSize.Height + (bw * 2);
481 int x = Cursor.Position.X;
482 int y = Cursor.Position.Y;
484 PointToClient (ref x, ref y);
486 if ((sizing_edge & FormPos.Top) != 0) {
487 if (pos.Bottom - y < mh)
489 pos.Height = pos.Bottom - y;
491 } else if ((sizing_edge & FormPos.Bottom) != 0) {
492 int height = y - pos.Top;
498 if ((sizing_edge & FormPos.Left) != 0) {
499 if (pos.Right - x < mw)
501 pos.Width = pos.Right - x;
503 } else if ((sizing_edge & FormPos.Right) != 0) {
504 int width = x - form.Left;
513 public bool IsMaximized {
514 get { return GetWindowState () == FormWindowState.Maximized; }
517 public bool IsMinimized {
518 get { return GetWindowState () == FormWindowState.Minimized; }
521 public bool IsSizable {
523 switch (form.FormBorderStyle) {
524 case FormBorderStyle.Sizable:
525 case FormBorderStyle.SizableToolWindow:
533 public bool HasBorders {
535 return form.FormBorderStyle != FormBorderStyle.None;
539 public bool IsToolWindow {
541 if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow ||
542 form.FormBorderStyle == FormBorderStyle.FixedToolWindow)
548 public int TitleBarHeight {
550 return ThemeEngine.Current.ManagedWindowTitleBarHeight (this);
554 protected void UpdateVP (Rectangle r)
556 UpdateVP (r.X, r.Y, r.Width, r.Height);
559 protected void UpdateVP (Point loc, int w, int h)
561 UpdateVP (loc.X, loc.Y, w, h);
564 protected void UpdateVP (int x, int y, int w, int h)
566 virtual_position.X = x;
567 virtual_position.Y = y;
568 virtual_position.Width = w;
569 virtual_position.Height = h;
571 DrawVirtualPosition (virtual_position);
574 private void HandleLButtonUp (ref Message m)
576 if (state == State.Idle)
579 ClearVirtualPosition ();
581 form.Capture = false;
582 form.Bounds = virtual_position;
585 OnWindowFinishedMoving ();
588 private bool HandleNCLButtonUp (ref Message m)
591 ClearVirtualPosition ();
593 form.Capture = false;
597 int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
598 int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
600 NCPointToClient (ref x, ref y);
602 foreach (TitleButton button in title_buttons) {
603 if (button != null && button.Rectangle.Contains (x, y)) {
604 button.Clicked (this, EventArgs.Empty);
612 protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip)
614 if (!button.Rectangle.IntersectsWith (clip))
617 dc.FillRectangle (SystemBrushes.Control, button.Rectangle);
619 ControlPaint.DrawCaptionButton (dc, button.Rectangle,
620 button.Caption, ButtonState.Normal);
623 public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe)
627 protected virtual void CloseClicked (object sender, EventArgs e)
632 private void MinimizeClicked (object sender, EventArgs e)
634 if (GetWindowState () != FormWindowState.Minimized) {
635 form.WindowState = FormWindowState.Minimized;
637 form.WindowState = FormWindowState.Normal;
641 private void MaximizeClicked (object sender, EventArgs e)
643 if (GetWindowState () != FormWindowState.Maximized) {
644 form.WindowState = FormWindowState.Maximized;
646 form.WindowState = FormWindowState.Normal;
650 protected Point MouseMove (Message m)
652 Point cp = Cursor.Position;
653 return new Point (cp.X - start.X, cp.Y - start.Y);
656 protected virtual void DrawVirtualPosition (Rectangle virtual_position)
658 form.Bounds = virtual_position;
659 start = Cursor.Position;
662 protected virtual void ClearVirtualPosition ()
667 protected virtual void OnWindowFinishedMoving ()
671 protected virtual void NCPointToClient(ref int x, ref int y) {
672 form.PointToClient(ref x, ref y);
674 y += ThemeEngine.Current.ManagedWindowBorderWidth (this);
677 protected FormPos FormPosForCoords (int x, int y)
679 int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
680 if (y < TitleBarHeight + bw) {
681 // Console.WriteLine ("A");
682 if (y > bw && x > bw &&
684 return FormPos.TitleBar;
686 if (x < bw || (x < 20 && y < bw))
687 return FormPos.TopLeft;
689 if (x > form.Width - bw ||
690 (x > form.Width - 20 && y < bw))
691 return FormPos.TopRight;
696 } else if (y > form.Height - 20) {
697 // Console.WriteLine ("B");
699 (x < 20 && y > form.Height - bw))
700 return FormPos.BottomLeft;
702 if (x > form.Width - (bw * 2) ||
703 (x > form.Width - 20 &&
704 y > form.Height - bw))
705 return FormPos.BottomRight;
707 if (y > form.Height - (bw * 2))
708 return FormPos.Bottom;
712 // Console.WriteLine ("C");
714 } else if (x > form.Width - (bw * 2)) {
715 // Console.WriteLine ("D");
716 return FormPos.Right;
718 // Console.WriteLine ("E {0}", form.Width - bw);