aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Internal/Hwnd.cs
diff options
context:
space:
mode:
authorMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
committerMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
commitd40fed5ce2bc806a91245adb18039634eac13ed0 (patch)
treef1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Internal/Hwnd.cs
parentf1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff)
downloadshiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz
shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2
shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Internal/Hwnd.cs')
-rw-r--r--source/ShiftUI/Internal/Hwnd.cs910
1 files changed, 910 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/Hwnd.cs b/source/ShiftUI/Internal/Hwnd.cs
new file mode 100644
index 0000000..fcdc17d
--- /dev/null
+++ b/source/ShiftUI/Internal/Hwnd.cs
@@ -0,0 +1,910 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2005-2006 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Peter Bartok ([email protected])
+//
+//
+
+// NOT COMPLETE
+
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+// NOTE: Possible optimization:
+// Several properties calculate dimensions on the fly; instead; they can
+// be stored in a field and only be recalculated when a style is changed (DefaultClientRect, for example)
+
+namespace ShiftUI {
+ internal class Hwnd : IDisposable {
+ #region Local Variables
+ private static Hashtable windows = new Hashtable(100, 0.5f);
+ //private const int menu_height = 14; // FIXME - Read this value from somewhere
+
+ private IntPtr handle;
+ internal IntPtr client_window;
+ internal IntPtr whole_window;
+ internal IntPtr cursor;
+ internal Menu menu;
+ internal TitleStyle title_style;
+ internal FormBorderStyle border_style;
+ internal bool border_static;
+ internal int x;
+ internal int y;
+ internal int width;
+ internal int height;
+ internal bool allow_drop;
+ internal Hwnd parent;
+ internal bool visible;
+ internal bool mapped;
+ internal uint opacity;
+ internal bool enabled;
+ internal bool zero_sized;
+ internal ArrayList invalid_list;
+ internal Rectangle nc_invalid;
+ internal bool expose_pending;
+ internal bool nc_expose_pending;
+ internal bool configure_pending;
+ internal bool resizing_or_moving; // Used by the X11 backend to track form resize/move
+ internal bool reparented;
+ internal Stack drawing_stack;
+ internal object user_data;
+ internal Rectangle client_rectangle;
+ internal ArrayList marshal_free_list;
+ internal int caption_height;
+ internal int tool_caption_height;
+ internal bool whacky_wm;
+ internal bool fixed_size;
+ internal bool zombie; /* X11 only flag. true if the X windows have been destroyed but we haven't been Disposed */
+ internal bool topmost; /* X11 only. */
+ internal Region user_clip;
+ internal XEventQueue queue;
+ internal WindowExStyles initial_ex_style;
+ internal WindowStyles initial_style;
+ internal FormWindowState cached_window_state = (FormWindowState)(-1); /* X11 only field */
+ internal Point previous_child_startup_location = new Point (int.MinValue, int.MinValue);
+ static internal Point previous_main_startup_location = new Point (int.MinValue, int.MinValue);
+ internal ArrayList children;
+
+ [ThreadStatic]
+ private static Bitmap bmp;
+ [ThreadStatic]
+ private static Graphics bmp_g;
+ #endregion // Local Variables
+
+ // locks for some operations (used in XplatUIX11.cs)
+ internal object configure_lock = new object ();
+ internal object expose_lock = new object ();
+
+ #region Constructors and destructors
+ public Hwnd() {
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ visible = false;
+ menu = null;
+ border_style = FormBorderStyle.None;
+ client_window = IntPtr.Zero;
+ whole_window = IntPtr.Zero;
+ cursor = IntPtr.Zero;
+ handle = IntPtr.Zero;
+ parent = null;
+ invalid_list = new ArrayList();
+ expose_pending = false;
+ nc_expose_pending = false;
+ enabled = true;
+ reparented = false;
+ client_rectangle = Rectangle.Empty;
+ marshal_free_list = new ArrayList(2);
+ opacity = 0xffffffff;
+ fixed_size = false;
+ drawing_stack = new Stack ();
+ children = new ArrayList ();
+ resizing_or_moving = false;
+ whacky_wm = false;
+ topmost = false;
+ }
+
+ public void Dispose() {
+ expose_pending = false;
+ nc_expose_pending = false;
+ Parent = null;
+ lock (windows) {
+ windows.Remove(client_window);
+ windows.Remove(whole_window);
+ }
+ client_window = IntPtr.Zero;
+ whole_window = IntPtr.Zero;
+ zombie = false;
+ for (int i = 0; i < marshal_free_list.Count; i++) {
+ Marshal.FreeHGlobal((IntPtr)marshal_free_list[i]);
+ }
+ marshal_free_list.Clear();
+ }
+ #endregion
+
+ #region Static Methods
+ public static Hwnd ObjectFromWindow(IntPtr window) {
+ Hwnd rv;
+ lock (windows) {
+ rv = (Hwnd)windows[window];
+ }
+ return rv;
+ }
+
+ public static Hwnd ObjectFromHandle(IntPtr handle) {
+ //return (Hwnd)(((GCHandle)handle).Target);
+ Hwnd rv;
+ lock (windows) {
+ rv = (Hwnd)windows[handle];
+ }
+ return rv;
+ }
+
+ public static IntPtr HandleFromObject(Hwnd obj) {
+ return obj.handle;
+ }
+
+ public static Hwnd GetObjectFromWindow(IntPtr window) {
+ Hwnd rv;
+ lock (windows) {
+ rv = (Hwnd)windows[window];
+ }
+ return rv;
+ }
+
+ public static IntPtr GetHandleFromWindow(IntPtr window) {
+ Hwnd hwnd;
+
+ lock (windows) {
+ hwnd = (Hwnd)windows[window];
+ }
+ if (hwnd != null) {
+ return hwnd.handle;
+ } else {
+ return IntPtr.Zero;
+ }
+ }
+
+ public static Borders GetBorderWidth (CreateParams cp)
+ {
+ Borders border_size = new Borders ();
+
+ Size windowborder = ThemeEngine.Current.BorderSize; /*new Size (1, 1);*/ // This is the only one that can be changed from the display properties in windows.
+ Size border = ThemeEngine.Current.BorderStaticSize; /*new Size (1, 1);*/
+ Size clientedge = ThemeEngine.Current.Border3DSize; /*new Size (2, 2);*/
+ Size thickframe = new Size (2 + windowborder.Width, 2 + windowborder.Height);
+ Size dialogframe = ThemeEngine.Current.BorderSizableSize; /* new Size (3, 3);*/
+
+ if (cp.IsSet (WindowStyles.WS_CAPTION)) {
+ border_size.Inflate (dialogframe);
+ } else if (cp.IsSet (WindowStyles.WS_BORDER)) {
+ if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME)) {
+ if (cp.IsSet (WindowStyles.WS_THICKFRAME) && (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE) || cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE))) {
+ border_size.Inflate (border);
+ }
+ } else {
+ border_size.Inflate (border);
+ }
+ } else if (cp.IsSet (WindowStyles.WS_DLGFRAME)) {
+ border_size.Inflate (dialogframe);
+ }
+
+ if (cp.IsSet (WindowStyles.WS_THICKFRAME)) {
+ if (cp.IsSet (WindowStyles.WS_DLGFRAME)) {
+ border_size.Inflate (border);
+ } else {
+ border_size.Inflate (thickframe);
+ }
+ }
+
+ bool only_small_border;
+ Size small_border = Size.Empty;
+
+ only_small_border = cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME);
+ if (only_small_border && cp.IsSet (WindowStyles.WS_THICKFRAME) && !cp.IsSet (WindowStyles.WS_BORDER) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) {
+ small_border = border;
+ }
+
+ if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) {
+ border_size.Inflate (clientedge + (only_small_border ? small_border : dialogframe));
+ } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) {
+ border_size.Inflate (only_small_border ? small_border : dialogframe);
+ } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_CLIENTEDGE)) {
+ border_size.Inflate (border + (only_small_border ? Size.Empty : clientedge));
+ } else {
+ if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE)) {
+ border_size.Inflate (clientedge);
+ }
+ if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) {
+ border_size.Inflate (cp.IsSet (WindowStyles.WS_THICKFRAME) ? border : dialogframe);
+ }
+ if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE)) {
+ if (cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME)) {
+ border_size.Inflate (new Size (-border.Width, -border.Height));
+ } else {
+ border_size.Inflate (border);
+ }
+ }
+ }
+
+ return border_size;
+ }
+
+ public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu)
+ {
+ return GetWindowRectangle (cp, menu, Rectangle.Empty);
+ }
+
+ public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu, Rectangle client_rect)
+ {
+ Rectangle rect;
+ Borders borders;
+
+ borders = GetBorders (cp, menu);
+
+ rect = new Rectangle (Point.Empty, client_rect.Size);
+ rect.Y -= borders.top;
+ rect.Height += borders.top + borders.bottom;
+ rect.X -= borders.left;
+ rect.Width += borders.left + borders.right;
+
+ #if debug
+ Console.WriteLine ("GetWindowRectangle ({0}, {1}, {2}): {3}", cp, menu != null, client_rect, rect);
+ #endif
+ return rect;
+ }
+
+ public Rectangle GetClientRectangle (int width, int height)
+ {
+ CreateParams cp = new CreateParams ();
+ cp.WindowStyle = initial_style;
+ cp.WindowExStyle = initial_ex_style;
+ return GetClientRectangle (cp, menu, width, height);
+ }
+
+ // This could be greatly optimized by caching the outputs and only updating when something is moved
+ // in the parent planar space. To do that we need to track z-order in the parent space as well
+ public ArrayList GetClippingRectangles ()
+ {
+ ArrayList masks = new ArrayList ();
+
+ if (x < 0) {
+ masks.Add (new Rectangle (0, 0, x*-1, Height));
+ if (y < 0) {
+ masks.Add (new Rectangle (x*-1, 0, Width, y*-1));
+ }
+ } else if (y < 0) {
+ masks.Add (new Rectangle (0, 0, Width, y*-1));
+ }
+
+ foreach (Hwnd child in children) {
+ if (child.visible)
+ masks.Add (new Rectangle (child.X, child.Y, child.Width, child.Height));
+ }
+
+ if (parent == null) {
+ return masks;
+ }
+
+ ArrayList siblings = parent.children;
+
+ foreach (Hwnd sibling in siblings) {
+ IntPtr sibling_handle = whole_window;
+
+ if (sibling == this)
+ continue;
+
+ // This entire method should be cached to find all higher views at the time of query
+ do {
+ sibling_handle = XplatUI.GetPreviousWindow (sibling_handle);
+
+ if (sibling_handle == sibling.WholeWindow && sibling.visible) {
+
+ Rectangle intersect = Rectangle.Intersect (new Rectangle (X, Y, Width, Height), new Rectangle (sibling.X, sibling.Y, sibling.Width, sibling.Height));
+
+ if (intersect == Rectangle.Empty)
+ continue;
+
+ intersect.X -= X;
+ intersect.Y -= Y;
+
+ masks.Add (intersect);
+ }
+ } while (sibling_handle != IntPtr.Zero);
+ }
+
+ return masks;
+ }
+
+ public static Borders GetBorders (CreateParams cp, Menu menu)
+ {
+
+ Borders borders = new Borders ();
+
+ if (menu != null) {
+ int menu_height = menu.Rect.Height;
+ if (menu_height == 0)
+ menu_height = ThemeEngine.Current.CalcMenuBarSize (GraphicsContext, menu, cp.Width);
+ borders.top += menu_height;
+ }
+
+ if (cp.IsSet (WindowStyles.WS_CAPTION)) {
+ int caption_height;
+ if (cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) {
+ caption_height = ThemeEngine.Current.ToolWindowCaptionHeight;
+ } else {
+ caption_height = ThemeEngine.Current.CaptionHeight;
+ }
+ borders.top += caption_height;
+ }
+
+ Borders border_width = GetBorderWidth (cp);
+
+ borders.left += border_width.left;
+ borders.right += border_width.right;
+ borders.top += border_width.top;
+ borders.bottom += border_width.bottom;
+
+ return borders;
+ }
+
+ public static Rectangle GetClientRectangle(CreateParams cp, Menu menu, int width, int height) {
+ Rectangle rect;
+ Borders borders;
+
+ borders = GetBorders (cp, menu);
+
+ rect = new Rectangle(0, 0, width, height);
+ rect.Y += borders.top;
+ rect.Height -= borders.top + borders.bottom;
+ rect.X += borders.left;
+ rect.Width -= borders.left + borders.right;
+
+ #if debug
+ Console.WriteLine ("GetClientRectangle ({0}, {1}, {2}, {3}): {4}", cp, menu != null, width, height, rect);
+ #endif
+
+ return rect;
+ }
+
+ public static Graphics GraphicsContext {
+ get {
+ if (bmp_g == null) {
+ bmp = new Bitmap (1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+ bmp_g = Graphics.FromImage (bmp);
+ }
+
+ return bmp_g;
+ }
+ }
+ #endregion // Static Methods
+
+ #region Instance Properties
+ public FormBorderStyle BorderStyle {
+ get {
+ return border_style;
+ }
+
+ set {
+ border_style = value;
+ }
+ }
+
+ public Rectangle ClientRect {
+ get {
+ if (client_rectangle == Rectangle.Empty) {
+ return DefaultClientRect;
+ }
+ return client_rectangle;
+ }
+
+ set {
+ client_rectangle = value;
+ }
+ }
+
+ public IntPtr Cursor {
+ get {
+ return cursor;
+ }
+
+ set {
+ cursor = value;
+ }
+ }
+
+ public IntPtr ClientWindow {
+ get {
+ return client_window;
+ }
+
+ set {
+ client_window = value;
+ handle = value;
+
+ zombie = false;
+
+ if (client_window != IntPtr.Zero) {
+ lock (windows) {
+ if (windows[client_window] == null) {
+ windows[client_window] = this;
+ }
+ }
+ }
+ }
+ }
+
+ public Region UserClip {
+ get {
+ return user_clip;
+ }
+
+ set {
+ user_clip = value;
+ }
+ }
+
+ public Rectangle DefaultClientRect {
+ get {
+ // We pass a Zero for the menu handle so the menu size is
+ // not computed this is done via an WM_NCCALC
+ CreateParams cp = new CreateParams ();
+ Rectangle rect;
+
+ cp.WindowStyle = initial_style;
+ cp.WindowExStyle = initial_ex_style;
+
+ rect = GetClientRectangle (cp, null, width, height);
+
+ return rect;
+ }
+ }
+
+ public bool ExposePending {
+ get {
+ return expose_pending;
+ }
+ }
+
+ public IntPtr Handle {
+ get {
+ if (handle == IntPtr.Zero) {
+ throw new ArgumentNullException("Handle", "Handle is not yet assigned, need a ClientWindow");
+ }
+ return handle;
+ }
+ }
+
+ public int Height {
+ get {
+ return height;
+ }
+
+ set {
+ height = value;
+ }
+ }
+
+ public Menu Menu {
+ get {
+ return menu;
+ }
+
+ set {
+ menu = value;
+ }
+ }
+
+ public bool Reparented {
+ get {
+ return reparented;
+ }
+
+ set {
+ reparented = value;
+ }
+ }
+
+ public uint Opacity {
+ get {
+ return opacity;
+ }
+
+ set {
+ opacity = value;
+ }
+ }
+
+ public XEventQueue Queue {
+ get {
+ return queue;
+ }
+
+ set {
+ queue = value;
+ }
+ }
+
+ public bool Enabled {
+ get {
+ if (!enabled) {
+ return false;
+ }
+
+ if (parent != null) {
+ return parent.Enabled;
+ }
+
+ return true;
+ }
+
+ set {
+ enabled = value;
+ }
+ }
+
+ public IntPtr EnabledHwnd {
+ get {
+ if (Enabled || parent == null) {
+ return Handle;
+ }
+
+ return parent.EnabledHwnd;
+ }
+ }
+
+ public Point MenuOrigin {
+ get {
+ Form frm = Widget.FromHandle (handle) as Form;
+ if (frm != null && frm.window_manager != null)
+ return frm.window_manager.GetMenuOrigin ();
+
+ Point pt;
+ Size border_3D_size = ThemeEngine.Current.Border3DSize;
+
+ pt = new Point(0, 0);
+
+ if (border_style == FormBorderStyle.Fixed3D) {
+ pt.X += border_3D_size.Width;
+ pt.Y += border_3D_size.Height;
+ } else if (border_style == FormBorderStyle.FixedSingle) {
+ pt.X += 1;
+ pt.Y += 1;
+ }
+
+ if (this.title_style == TitleStyle.Normal) {
+ pt.Y += caption_height;
+ } else if (this.title_style == TitleStyle.Normal) {
+ pt.Y += tool_caption_height;
+ }
+
+ return pt;
+ }
+ }
+
+ public Rectangle Invalid {
+ get {
+ if (invalid_list.Count == 0)
+ return Rectangle.Empty;
+
+ Rectangle result = (Rectangle)invalid_list[0];
+ for (int i = 1; i < invalid_list.Count; i ++) {
+ result = Rectangle.Union (result, (Rectangle)invalid_list[i]);
+ }
+ return result;
+ }
+ }
+
+ public Rectangle[] ClipRectangles {
+ get {
+ return (Rectangle[]) invalid_list.ToArray (typeof (Rectangle));
+ }
+ }
+
+ public Rectangle NCInvalid {
+ get { return nc_invalid; }
+ set { nc_invalid = value; }
+
+ }
+
+ public bool NCExposePending {
+ get {
+ return nc_expose_pending;
+ }
+ }
+
+ public Hwnd Parent {
+ get {
+ return parent;
+ }
+
+ set {
+ if (parent != null)
+ parent.children.Remove (this);
+ parent = value;
+ if (parent != null)
+ parent.children.Add (this);
+ }
+ }
+
+ public bool Mapped {
+ get {
+ if (!mapped) {
+ return false;
+ }
+
+ if (parent != null) {
+ return parent.Mapped;
+ }
+
+ return true;
+ }
+
+ set {
+ mapped = value;
+ }
+ }
+
+ public int CaptionHeight {
+ get { return caption_height; }
+ set { caption_height = value; }
+ }
+
+ public int ToolCaptionHeight {
+ get { return tool_caption_height; }
+ set { tool_caption_height = value; }
+ }
+
+ public TitleStyle TitleStyle {
+ get {
+ return title_style;
+ }
+
+ set {
+ title_style = value;
+ }
+ }
+
+ public object UserData {
+ get {
+ return user_data;
+ }
+
+ set {
+ user_data = value;
+ }
+ }
+
+ public IntPtr WholeWindow {
+ get {
+ return whole_window;
+ }
+
+ set {
+ whole_window = value;
+
+ zombie = false;
+
+ if (whole_window != IntPtr.Zero) {
+ lock (windows) {
+ if (windows[whole_window] == null) {
+ windows[whole_window] = this;
+ }
+ }
+ }
+ }
+ }
+
+ public int Width {
+ get {
+ return width;
+ }
+
+ set {
+ width = value;
+ }
+ }
+
+ public bool Visible {
+ get {
+ return visible;
+ }
+
+ set {
+ visible = value;
+ }
+ }
+
+ public int X {
+ get {
+ return x;
+ }
+
+ set {
+ x = value;
+ }
+ }
+
+ public int Y {
+ get {
+ return y;
+ }
+
+ set {
+ y = value;
+ }
+ }
+
+ #endregion // Instance properties
+
+ #region Methods
+ public void AddInvalidArea(int x, int y, int width, int height) {
+ AddInvalidArea(new Rectangle(x, y, width, height));
+ }
+
+ public void AddInvalidArea(Rectangle rect) {
+ ArrayList tmp = new ArrayList ();
+ foreach (Rectangle r in invalid_list) {
+ if (!rect.Contains (r)) {
+ tmp.Add (r);
+ }
+ }
+ tmp.Add (rect);
+ invalid_list = tmp;
+ }
+
+ public void ClearInvalidArea() {
+ invalid_list.Clear();
+ expose_pending = false;
+ }
+
+ public void AddNcInvalidArea(int x, int y, int width, int height) {
+ if (nc_invalid == Rectangle.Empty) {
+ nc_invalid = new Rectangle (x, y, width, height);
+ return;
+ }
+
+ int right, bottom;
+ right = Math.Max (nc_invalid.Right, x + width);
+ bottom = Math.Max (nc_invalid.Bottom, y + height);
+ nc_invalid.X = Math.Min (nc_invalid.X, x);
+ nc_invalid.Y = Math.Min (nc_invalid.Y, y);
+
+ nc_invalid.Width = right - nc_invalid.X;
+ nc_invalid.Height = bottom - nc_invalid.Y;
+ }
+
+ public void AddNcInvalidArea(Rectangle rect) {
+ if (nc_invalid == Rectangle.Empty) {
+ nc_invalid = rect;
+ return;
+ }
+ nc_invalid = Rectangle.Union (nc_invalid, rect);
+ }
+
+ public void ClearNcInvalidArea() {
+ nc_invalid = Rectangle.Empty;
+ nc_expose_pending = false;
+ }
+
+ public override string ToString() {
+ 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);
+ }
+
+ public static Point GetNextStackedFormLocation (CreateParams cp, Hwnd parent_hwnd)
+ {
+ if (cp.control == null)
+ return Point.Empty;
+
+ int X = cp.X;
+ int Y = cp.Y;
+ Point previous, next;
+ Rectangle within;
+
+ if (parent_hwnd != null) {
+ Widget parent = cp.control.Parent;
+ previous = parent_hwnd.previous_child_startup_location;
+ if (parent_hwnd.client_rectangle == Rectangle.Empty && parent != null) {
+ within = parent.ClientRectangle;
+ } else {
+ within = parent_hwnd.client_rectangle;
+ }
+ } else {
+ previous = Hwnd.previous_main_startup_location;
+ within = ShiftUI.Screen.PrimaryScreen.WorkingArea;
+ }
+
+ if (previous.X == int.MinValue || previous.Y == int.MinValue) {
+ next = Point.Empty;
+ } else {
+ next = new Point (previous.X + 22, previous.Y + 22);
+ }
+
+ if (!within.Contains (next.X * 3, next.Y * 3)) {
+ next = Point.Empty;
+ }
+
+ if (next == Point.Empty && cp.Parent == IntPtr.Zero) {
+ next = new Point (22, 22);
+ }
+
+ if (parent_hwnd != null) {
+ parent_hwnd.previous_child_startup_location = next;
+ } else {
+ Hwnd.previous_main_startup_location = next;
+ }
+
+ if (X == int.MinValue && Y == int.MinValue) {
+ X = next.X;
+ Y = next.Y;
+ }
+
+ return new Point (X, Y);
+ }
+
+ #endregion // Methods
+
+ internal struct Borders
+ {
+ public int top;
+ public int bottom;
+ public int left;
+ public int right;
+
+ public void Inflate (Size size)
+ {
+ left += size.Width;
+ right += size.Width;
+ top += size.Height;
+ bottom += size.Height;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format("{{top={0}, bottom={1}, left={2}, right={3}}}", top, bottom, left, right);
+ }
+
+ public static bool operator == (Borders a, Borders b)
+ {
+ return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
+ }
+
+ public static bool operator != (Borders a, Borders b)
+ {
+ return !(a == b);
+ }
+
+ public override bool Equals (object obj)
+ {
+ return base.Equals (obj);
+ }
+
+ public override int GetHashCode ()
+ {
+ return base.GetHashCode ();
+ }
+ }
+ }
+}