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-2006 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok (pbartok@novell.com)
30 using System.Collections;
32 using System.Runtime.InteropServices;
34 // NOTE: Possible optimization:
35 // Several properties calculate dimensions on the fly; instead; they can
36 // be stored in a field and only be recalculated when a style is changed (DefaultClientRect, for example)
38 namespace System.Windows.Forms {
39 internal class Hwnd : IDisposable {
40 #region Local Variables
41 private static Hashtable windows = new Hashtable(100, 0.5f);
42 //private const int menu_height = 14; // FIXME - Read this value from somewhere
44 private IntPtr handle;
45 internal IntPtr client_window;
46 internal IntPtr whole_window;
47 internal IntPtr cursor;
49 internal TitleStyle title_style;
50 internal FormBorderStyle border_style;
51 internal bool border_static;
56 internal bool allow_drop;
58 internal bool visible;
60 internal uint opacity;
61 internal bool enabled;
62 internal bool zero_sized;
63 internal ArrayList invalid_list;
64 internal Rectangle nc_invalid;
65 internal bool expose_pending;
66 internal bool nc_expose_pending;
67 internal bool configure_pending;
68 internal bool resizing_or_moving; // Used by the X11 backend to track form resize/move
69 internal bool reparented;
70 internal Stack drawing_stack;
71 internal object user_data;
72 internal Rectangle client_rectangle;
73 internal ArrayList marshal_free_list;
74 internal int caption_height;
75 internal int tool_caption_height;
76 internal bool whacky_wm;
77 internal bool fixed_size;
78 internal bool zombie; /* X11 only flag. true if the X windows have been destroyed but we haven't been Disposed */
79 internal bool topmost; /* X11 only. */
80 internal Region user_clip;
81 internal XEventQueue queue;
82 internal WindowExStyles initial_ex_style;
83 internal WindowStyles initial_style;
84 internal FormWindowState cached_window_state = (FormWindowState)(-1); /* X11 only field */
85 internal Point previous_child_startup_location = new Point (int.MinValue, int.MinValue);
86 static internal Point previous_main_startup_location = new Point (int.MinValue, int.MinValue);
87 internal ArrayList children;
90 private static Bitmap bmp;
92 private static Graphics bmp_g;
93 #endregion // Local Variables
95 // locks for some operations (used in XplatUIX11.cs)
96 internal object configure_lock = new object ();
97 internal object expose_lock = new object ();
99 #region Constructors and destructors
107 border_style = FormBorderStyle.None;
108 client_window = IntPtr.Zero;
109 whole_window = IntPtr.Zero;
110 cursor = IntPtr.Zero;
111 handle = IntPtr.Zero;
113 invalid_list = new ArrayList();
114 expose_pending = false;
115 nc_expose_pending = false;
118 client_rectangle = Rectangle.Empty;
119 marshal_free_list = new ArrayList(2);
120 opacity = 0xffffffff;
122 drawing_stack = new Stack ();
123 children = new ArrayList ();
124 resizing_or_moving = false;
129 public void Dispose() {
130 expose_pending = false;
131 nc_expose_pending = false;
134 windows.Remove(client_window);
135 windows.Remove(whole_window);
137 client_window = IntPtr.Zero;
138 whole_window = IntPtr.Zero;
140 for (int i = 0; i < marshal_free_list.Count; i++) {
141 Marshal.FreeHGlobal((IntPtr)marshal_free_list[i]);
143 marshal_free_list.Clear();
147 #region Static Methods
148 public static Hwnd ObjectFromWindow(IntPtr window) {
151 rv = (Hwnd)windows[window];
156 public static Hwnd ObjectFromHandle(IntPtr handle) {
157 //return (Hwnd)(((GCHandle)handle).Target);
160 rv = (Hwnd)windows[handle];
165 public static IntPtr HandleFromObject(Hwnd obj) {
169 public static Hwnd GetObjectFromWindow(IntPtr window) {
172 rv = (Hwnd)windows[window];
177 public static IntPtr GetHandleFromWindow(IntPtr window) {
181 hwnd = (Hwnd)windows[window];
190 public static Borders GetBorderWidth (CreateParams cp)
192 Borders border_size = new Borders ();
194 Size windowborder = ThemeEngine.Current.BorderSize; /*new Size (1, 1);*/ // This is the only one that can be changed from the display properties in windows.
195 Size border = ThemeEngine.Current.BorderStaticSize; /*new Size (1, 1);*/
196 Size clientedge = ThemeEngine.Current.Border3DSize; /*new Size (2, 2);*/
197 Size thickframe = new Size (2 + windowborder.Width, 2 + windowborder.Height);
198 Size dialogframe = ThemeEngine.Current.BorderSizableSize; /* new Size (3, 3);*/
200 if (cp.IsSet (WindowStyles.WS_CAPTION)) {
201 border_size.Inflate (dialogframe);
202 } else if (cp.IsSet (WindowStyles.WS_BORDER)) {
203 if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME)) {
204 if (cp.IsSet (WindowStyles.WS_THICKFRAME) && (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE) || cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE))) {
205 border_size.Inflate (border);
208 border_size.Inflate (border);
210 } else if (cp.IsSet (WindowStyles.WS_DLGFRAME)) {
211 border_size.Inflate (dialogframe);
214 if (cp.IsSet (WindowStyles.WS_THICKFRAME)) {
215 if (cp.IsSet (WindowStyles.WS_DLGFRAME)) {
216 border_size.Inflate (border);
218 border_size.Inflate (thickframe);
222 bool only_small_border;
223 Size small_border = Size.Empty;
225 only_small_border = cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME);
226 if (only_small_border && cp.IsSet (WindowStyles.WS_THICKFRAME) && !cp.IsSet (WindowStyles.WS_BORDER) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) {
227 small_border = border;
230 if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) {
231 border_size.Inflate (clientedge + (only_small_border ? small_border : dialogframe));
232 } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) {
233 border_size.Inflate (only_small_border ? small_border : dialogframe);
234 } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_CLIENTEDGE)) {
235 border_size.Inflate (border + (only_small_border ? Size.Empty : clientedge));
237 if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE)) {
238 border_size.Inflate (clientedge);
240 if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) {
241 border_size.Inflate (cp.IsSet (WindowStyles.WS_THICKFRAME) ? border : dialogframe);
243 if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE)) {
244 if (cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME)) {
245 border_size.Inflate (new Size (-border.Width, -border.Height));
247 border_size.Inflate (border);
255 public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu)
257 return GetWindowRectangle (cp, menu, Rectangle.Empty);
260 public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu, Rectangle client_rect)
265 borders = GetBorders (cp, menu);
267 rect = new Rectangle (Point.Empty, client_rect.Size);
268 rect.Y -= borders.top;
269 rect.Height += borders.top + borders.bottom;
270 rect.X -= borders.left;
271 rect.Width += borders.left + borders.right;
274 Console.WriteLine ("GetWindowRectangle ({0}, {1}, {2}): {3}", cp, menu != null, client_rect, rect);
279 public Rectangle GetClientRectangle (int width, int height)
281 CreateParams cp = new CreateParams ();
282 cp.WindowStyle = initial_style;
283 cp.WindowExStyle = initial_ex_style;
284 return GetClientRectangle (cp, menu, width, height);
287 // This could be greatly optimized by caching the outputs and only updating when something is moved
288 // in the parent planar space. To do that we need to track z-order in the parent space as well
289 public ArrayList GetClippingRectangles ()
291 ArrayList masks = new ArrayList ();
294 masks.Add (new Rectangle (0, 0, x*-1, Height));
296 masks.Add (new Rectangle (x*-1, 0, Width, y*-1));
299 masks.Add (new Rectangle (0, 0, Width, y*-1));
302 foreach (Hwnd child in children) {
304 masks.Add (new Rectangle (child.X, child.Y, child.Width, child.Height));
307 if (parent == null) {
311 ArrayList siblings = parent.children;
313 foreach (Hwnd sibling in siblings) {
314 IntPtr sibling_handle = whole_window;
319 // This entire method should be cached to find all higher views at the time of query
321 sibling_handle = XplatUI.GetPreviousWindow (sibling_handle);
323 if (sibling_handle == sibling.WholeWindow && sibling.visible) {
325 Rectangle intersect = Rectangle.Intersect (new Rectangle (X, Y, Width, Height), new Rectangle (sibling.X, sibling.Y, sibling.Width, sibling.Height));
327 if (intersect == Rectangle.Empty)
333 masks.Add (intersect);
335 } while (sibling_handle != IntPtr.Zero);
341 public static Borders GetBorders (CreateParams cp, Menu menu)
344 Borders borders = new Borders ();
347 int menu_height = menu.Rect.Height;
348 if (menu_height == 0)
349 menu_height = ThemeEngine.Current.CalcMenuBarSize (GraphicsContext, menu, cp.Width);
350 borders.top += menu_height;
353 if (cp.IsSet (WindowStyles.WS_CAPTION)) {
355 if (cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) {
356 caption_height = ThemeEngine.Current.ToolWindowCaptionHeight;
358 caption_height = ThemeEngine.Current.CaptionHeight;
360 borders.top += caption_height;
363 Borders border_width = GetBorderWidth (cp);
365 borders.left += border_width.left;
366 borders.right += border_width.right;
367 borders.top += border_width.top;
368 borders.bottom += border_width.bottom;
373 public static Rectangle GetClientRectangle(CreateParams cp, Menu menu, int width, int height) {
377 borders = GetBorders (cp, menu);
379 rect = new Rectangle(0, 0, width, height);
380 rect.Y += borders.top;
381 rect.Height -= borders.top + borders.bottom;
382 rect.X += borders.left;
383 rect.Width -= borders.left + borders.right;
386 Console.WriteLine ("GetClientRectangle ({0}, {1}, {2}, {3}): {4}", cp, menu != null, width, height, rect);
392 public static Graphics GraphicsContext {
395 bmp = new Bitmap (1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
396 bmp_g = Graphics.FromImage (bmp);
402 #endregion // Static Methods
404 #region Instance Properties
405 public FormBorderStyle BorderStyle {
411 border_style = value;
415 public Rectangle ClientRect {
417 if (client_rectangle == Rectangle.Empty) {
418 return DefaultClientRect;
420 return client_rectangle;
424 client_rectangle = value;
428 public IntPtr Cursor {
438 public IntPtr ClientWindow {
440 return client_window;
444 client_window = value;
449 if (client_window != IntPtr.Zero) {
451 if (windows[client_window] == null) {
452 windows[client_window] = this;
459 public Region UserClip {
469 public Rectangle DefaultClientRect {
471 // We pass a Zero for the menu handle so the menu size is
472 // not computed this is done via an WM_NCCALC
473 CreateParams cp = new CreateParams ();
476 cp.WindowStyle = initial_style;
477 cp.WindowExStyle = initial_ex_style;
479 rect = GetClientRectangle (cp, null, width, height);
485 public bool ExposePending {
487 return expose_pending;
491 public IntPtr Handle {
493 if (handle == IntPtr.Zero) {
494 throw new ArgumentNullException("Handle", "Handle is not yet assigned, need a ClientWindow");
520 public bool Reparented {
530 public uint Opacity {
540 public XEventQueue Queue {
550 public bool Enabled {
556 if (parent != null) {
557 return parent.Enabled;
568 public IntPtr EnabledHwnd {
570 if (Enabled || parent == null) {
574 return parent.EnabledHwnd;
578 public Point MenuOrigin {
580 Form frm = Control.FromHandle (handle) as Form;
581 if (frm != null && frm.window_manager != null)
582 return frm.window_manager.GetMenuOrigin ();
585 Size border_3D_size = ThemeEngine.Current.Border3DSize;
587 pt = new Point(0, 0);
589 if (border_style == FormBorderStyle.Fixed3D) {
590 pt.X += border_3D_size.Width;
591 pt.Y += border_3D_size.Height;
592 } else if (border_style == FormBorderStyle.FixedSingle) {
597 if (this.title_style == TitleStyle.Normal) {
598 pt.Y += caption_height;
599 } else if (this.title_style == TitleStyle.Tool) {
600 pt.Y += tool_caption_height;
607 public Rectangle Invalid {
609 if (invalid_list.Count == 0)
610 return Rectangle.Empty;
612 Rectangle result = (Rectangle)invalid_list[0];
613 for (int i = 1; i < invalid_list.Count; i ++) {
614 result = Rectangle.Union (result, (Rectangle)invalid_list[i]);
620 public Rectangle[] ClipRectangles {
622 return (Rectangle[]) invalid_list.ToArray (typeof (Rectangle));
626 public Rectangle NCInvalid {
627 get { return nc_invalid; }
628 set { nc_invalid = value; }
632 public bool NCExposePending {
634 return nc_expose_pending;
645 parent.children.Remove (this);
648 parent.children.Add (this);
658 if (parent != null) {
659 return parent.Mapped;
670 public int CaptionHeight {
671 get { return caption_height; }
672 set { caption_height = value; }
675 public int ToolCaptionHeight {
676 get { return tool_caption_height; }
677 set { tool_caption_height = value; }
680 public TitleStyle TitleStyle {
690 public object UserData {
700 public IntPtr WholeWindow {
706 whole_window = value;
710 if (whole_window != IntPtr.Zero) {
712 if (windows[whole_window] == null) {
713 windows[whole_window] = this;
730 public bool Visible {
760 #endregion // Instance properties
763 public void AddInvalidArea(int x, int y, int width, int height) {
764 AddInvalidArea(new Rectangle(x, y, width, height));
767 public void AddInvalidArea(Rectangle rect) {
768 ArrayList tmp = new ArrayList ();
769 foreach (Rectangle r in invalid_list) {
770 if (!rect.Contains (r)) {
778 public void ClearInvalidArea() {
779 invalid_list.Clear();
780 expose_pending = false;
783 public void AddNcInvalidArea(int x, int y, int width, int height) {
784 if (nc_invalid == Rectangle.Empty) {
785 nc_invalid = new Rectangle (x, y, width, height);
790 right = Math.Max (nc_invalid.Right, x + width);
791 bottom = Math.Max (nc_invalid.Bottom, y + height);
792 nc_invalid.X = Math.Min (nc_invalid.X, x);
793 nc_invalid.Y = Math.Min (nc_invalid.Y, y);
795 nc_invalid.Width = right - nc_invalid.X;
796 nc_invalid.Height = bottom - nc_invalid.Y;
799 public void AddNcInvalidArea(Rectangle rect) {
800 if (nc_invalid == Rectangle.Empty) {
804 nc_invalid = Rectangle.Union (nc_invalid, rect);
807 public void ClearNcInvalidArea() {
808 nc_invalid = Rectangle.Empty;
809 nc_expose_pending = false;
812 public override string ToString() {
813 return String.Format("Hwnd, Mapped:{3} ClientWindow:0x{0:X}, WholeWindow:0x{1:X}, Zombie={4}, Parent:[{2:X}]", client_window.ToInt32(), whole_window.ToInt32(), parent != null ? parent.ToString() : "<null>", Mapped, zombie);
816 public static Point GetNextStackedFormLocation (CreateParams cp, Hwnd parent_hwnd)
818 if (cp.control == null)
823 Point previous, next;
826 if (parent_hwnd != null) {
827 Control parent = cp.control.Parent;
828 previous = parent_hwnd.previous_child_startup_location;
829 if (parent_hwnd.client_rectangle == Rectangle.Empty && parent != null) {
830 within = parent.ClientRectangle;
832 within = parent_hwnd.client_rectangle;
835 previous = Hwnd.previous_main_startup_location;
836 within = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
839 if (previous.X == int.MinValue || previous.Y == int.MinValue) {
842 next = new Point (previous.X + 22, previous.Y + 22);
845 if (!within.Contains (next.X * 3, next.Y * 3)) {
849 if (next == Point.Empty && cp.Parent == IntPtr.Zero) {
850 next = new Point (22, 22);
853 if (parent_hwnd != null) {
854 parent_hwnd.previous_child_startup_location = next;
856 Hwnd.previous_main_startup_location = next;
859 if (X == int.MinValue && Y == int.MinValue) {
864 return new Point (X, Y);
867 #endregion // Methods
869 internal struct Borders
876 public void Inflate (Size size)
881 bottom += size.Height;
884 public override string ToString ()
886 return string.Format("{{top={0}, bottom={1}, left={2}, right={3}}}", top, bottom, left, right);
889 public static bool operator == (Borders a, Borders b)
891 return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
894 public static bool operator != (Borders a, Borders b)
899 public override bool Equals (object obj)
901 return base.Equals (obj);
904 public override int GetHashCode ()
906 return base.GetHashCode ();