context: // 
  // WindowBackend.cs
  //  
  // Author:
  //       Lluis Sanchez <lluis@xamarin.com>
  //       Andres G. Aragoneses <andres.aragoneses@7digital.com>
  //       Konrad M. Kruczynski <kkruczynski@antmicro.com>
  // 
  // Copyright (c) 2011 Xamarin Inc
  // Copyright (c) 2012 7Digital Media Ltd
  // Copyright (c) 2016 Antmicro Ltd
  // 
  // 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.
  
  using System;
  using System.Linq;
  using AppKit;
  using CoreGraphics;
  using Foundation;
  using ObjCRuntime;
  using Xwt.Backends;
  using Xwt.Drawing;
  
  namespace Xwt.Mac
  {
  	public class WindowBackend: NSWindow, IWindowBackend
  	{
  		WindowBackendController controller;
  		IWindowFrameEventSink eventSink;
  		Window frontend;
  		ViewBackend child;
  		NSView childView;
  		bool sensitive = true;
  		
  		public WindowBackend (IntPtr ptr): base (ptr)
  		{
  		}
  		
  		public WindowBackend ()
  		{
  			this.controller = new WindowBackendController ();
  			controller.Window = this;
  			StyleMask |= NSWindowStyle.Resizable | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable;
  			AutorecalculatesKeyViewLoop = true;
  
  			ContentView.AutoresizesSubviews = true;
  			ContentView.Hidden = true;
  
  			// TODO: do it only if mouse move events are enabled in a widget
  			AcceptsMouseMovedEvents = true;
  
  			WillClose += delegate {
  				OnClosed ();
  			};
  
  			Center ();
  		}
  
  		object IWindowFrameBackend.Window {
  			get { return this; }
  		}
  
  		public IntPtr NativeHandle {
  			get { return Handle; }
  		}
  
  		public IWindowFrameEventSink EventSink {
  			get { return (IWindowFrameEventSink)eventSink; }
  		}
  
  		public virtual void InitializeBackend (object frontend, ApplicationContext context)
  		{
  			this.ApplicationContext = context;
  			this.frontend = (Window) frontend;
  		}
  		
  		public void Initialize (IWindowFrameEventSink eventSink)
  		{
  			this.eventSink = eventSink;
  		}
  		
  		public ApplicationContext ApplicationContext {
  			get;
  			private set;
  		}
  		
  		public object NativeWidget {
  			get {
  				return this;
  			}
  		}
  
  		public string Name { get; set; }
  		
  		internal void InternalShow ()
  		{
  			MakeKeyAndOrderFront (MacEngine.App);
  			if (ParentWindow != null)
  			{
  				if (!ParentWindow.ChildWindows.Contains(this))
  					ParentWindow.AddChildWindow(this, NSWindowOrderingMode.Above);
  
  				// always use NSWindow for alignment when running in guest mode and
  				// don't rely on AddChildWindow to position the window correctly
  				if (frontend.InitialLocation == WindowLocation.CenterParent && !(ParentWindow is WindowBackend))
  				{
  					var parentBounds = MacDesktopBackend.ToDesktopRect(ParentWindow.ContentRectFor(ParentWindow.Frame));
  					var bounds = ((IWindowFrameBackend)this).Bounds;
  					bounds.X = parentBounds.Center.X - (Frame.Width / 2);
  					bounds.Y = parentBounds.Center.Y - (Frame.Height / 2);
  					((IWindowFrameBackend)this).Bounds = bounds;
  				}
  			}
  		}
  		
  		public void Present ()
  		{
  			InternalShow();
  		}
  
  		public bool Visible {
  			get {
  				return IsVisible;
  			}
  			set {
  				if (value)
  					MacEngine.App.ShowWindow(this);
  				ContentView.Hidden = !value; // handle shown/hidden events
  				IsVisible = value;
  			}
  		}
  
  		public double Opacity {
  			get { return AlphaValue; }
  			set { AlphaValue = (float)value; }
  		}
  
  		Color IWindowBackend.BackgroundColor {
  			get {
  				return BackgroundColor.ToXwtColor ();
  			}
  			set {
  				BackgroundColor = value.ToNSColor ();
  			}
  		}
  
  		public bool Sensitive {
  			get {
  				return sensitive;
  			}
  			set {
  				sensitive = value;
  				if (child != null)
  					child.UpdateSensitiveStatus (child.Widget, sensitive);
  			}
  		}
  
  		public bool HasFocus {
  			get {
  				return IsKeyWindow;
  			}
  		}
  
  		public bool FullScreen {
  			get {
  				if (MacSystemInformation.OsVersion < MacSystemInformation.Lion)
  					return false;
  
  				return (StyleMask & NSWindowStyle.FullScreenWindow) != 0;
  
  			}
  			set {
  				if (MacSystemInformation.OsVersion < MacSystemInformation.Lion)
  					return;
  
  				if (value != ((StyleMask & NSWindowStyle.FullScreenWindow) != 0))
  					ToggleFullScreen (null);
  			}
  		}
  
  		object IWindowFrameBackend.Screen {
  			get {
  				return Screen;
  			}
  		}
  
  		#region IWindowBackend implementation
  		void IBackend.EnableEvent (object eventId)
  		{
  			if (eventId is WindowFrameEvent) {
  				var @event = (WindowFrameEvent)eventId;
  				switch (@event) {
  				case WindowFrameEvent.BoundsChanged:
  					DidResize += HandleDidResize;
  					DidMove += HandleDidResize;
  					break;
  				case WindowFrameEvent.Hidden:
  					EnableVisibilityEvent (@event);
  					this.WillClose += OnWillClose;
  					break;
  				case WindowFrameEvent.Shown:
  					EnableVisibilityEvent (@event);
  					break;
  				case WindowFrameEvent.CloseRequested:
  					WindowShouldClose = OnShouldClose;
  					break;
  				}
  			}
  		}
  		
  		void OnWillClose (object sender, EventArgs args) {
  			OnHidden ();
  		}
  
  		bool OnShouldClose (NSObject ob)
  		{
  			return closePerformed = RequestClose ();
  		}
  
  		internal bool RequestClose ()
  		{
  			bool res = true;
  			ApplicationContext.InvokeUserCode (() => res = eventSink.OnCloseRequested ());
  			return res;
  		}
  
  		protected virtual void OnClosed ()
  		{
  			if (!disposing)
  				ApplicationContext.InvokeUserCode (eventSink.OnClosed);
  		}
  
  		bool closePerformed;
  
  		bool IWindowFrameBackend.Close ()
  		{
  			closePerformed = true;
  			if ((StyleMask & NSWindowStyle.Titled) != 0 && (StyleMask & NSWindowStyle.Closable) != 0)
  				PerformClose(this);
  			else
  				Close ();
  			if (ParentWindow != null)
  				ParentWindow.RemoveChildWindow(this);
  			return closePerformed;
  		}
  		
  		bool VisibilityEventsEnabled ()
  		{
  			return eventsEnabled != WindowFrameEvent.BoundsChanged;
  		}
  		WindowFrameEvent eventsEnabled = WindowFrameEvent.BoundsChanged;
  
  		NSString HiddenProperty {
  			get { return new NSString ("hidden"); }
  		}
  		
  		void EnableVisibilityEvent (WindowFrameEvent ev)
  		{
  			if (!VisibilityEventsEnabled ()) {
  				ContentView.AddObserver (this, HiddenProperty, NSKeyValueObservingOptions.New, IntPtr.Zero);
  			}
  			if (!eventsEnabled.HasFlag (ev)) {
  				eventsEnabled |= ev;
  			}
  		}
  
  		public override void ObserveValue (NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
  		{
  			if (keyPath.ToString () == HiddenProperty.ToString () && ofObject.Equals (ContentView)) {
  				if (ContentView.Hidden) {
  					if (eventsEnabled.HasFlag (WindowFrameEvent.Hidden)) {
  						OnHidden ();
  					}
  				} else {
  					if (eventsEnabled.HasFlag (WindowFrameEvent.Shown)) {
  						OnShown ();
  					}
  				}
  			}
  		}
  
  		void OnHidden () {
  			ApplicationContext.InvokeUserCode (delegate ()
  			{
  				eventSink.OnHidden ();
  			});
  		}
  
  		void OnShown () {
  			ApplicationContext.InvokeUserCode (delegate ()
  			{
  				eventSink.OnShown ();
  			});
  		}
  
  		void DisableVisibilityEvent (WindowFrameEvent ev)
  		{
  			if (eventsEnabled.HasFlag (ev)) {
  				eventsEnabled ^= ev;
  				if (!VisibilityEventsEnabled ()) {
  					ContentView.RemoveObserver (this, HiddenProperty);
  				}
  			}
  		}
  
  		void IBackend.DisableEvent (object eventId)
  		{
  			if (eventId is WindowFrameEvent) {
  				var @event = (WindowFrameEvent)eventId;
  				switch (@event) {
  					case WindowFrameEvent.BoundsChanged:
  						DidResize -= HandleDidResize;
  						DidMove -= HandleDidResize;
  						break;
  					case WindowFrameEvent.Hidden:
  						this.WillClose -= OnWillClose;
  						DisableVisibilityEvent (@event);
  						break;
  					case WindowFrameEvent.Shown:
  						DisableVisibilityEvent (@event);
  						break;
  				}
  			}
  		}
  
  		void HandleDidResize (object sender, EventArgs e)
  		{
  			OnBoundsChanged ();
  		}
  
  		protected virtual void OnBoundsChanged ()
  		{
  			LayoutWindow ();
  			ApplicationContext.InvokeUserCode (delegate {
  				eventSink.OnBoundsChanged (((IWindowBackend)this).Bounds);
  			});
  		}
  
  		void IWindowBackend.SetChild (IWidgetBackend child)
  		{
  			if (this.child != null) {
  				ViewBackend.RemoveChildPlacement (this.child.Widget);
  				this.child.Widget.RemoveFromSuperview ();
  				childView = null;
  			}
  			this.child = (ViewBackend) child;
  			if (child != null) {
  				childView = ViewBackend.GetWidgetWithPlacement (child);
  				ContentView.AddSubview (childView);
  				LayoutWindow ();
  				childView.AutoresizingMask = NSViewResizingMask.HeightSizable | NSViewResizingMask.WidthSizable;
  			}
  		}
  		
  		public virtual void UpdateChildPlacement (IWidgetBackend childBackend)
  		{
  			var w = ViewBackend.SetChildPlacement (childBackend);
  			LayoutWindow ();
  			w.AutoresizingMask = NSViewResizingMask.HeightSizable | NSViewResizingMask.WidthSizable;
  		}
  
  		bool IWindowFrameBackend.Decorated {
  			get {
  				return (StyleMask & NSWindowStyle.Titled) != 0;
  			}
  			set {
  				if (value)
  					StyleMask |= NSWindowStyle.Titled;
  				else
  					StyleMask &= ~(NSWindowStyle.Titled | NSWindowStyle.Borderless);
  			}
  		}
  		
  		bool IWindowFrameBackend.ShowInTaskbar {
  			get {
  				return false;
  			}
  			set {
  			}
  		}
  
  		void IWindowFrameBackend.SetTransientFor (IWindowFrameBackend window)
  		{
  			if (!((IWindowFrameBackend)this).ShowInTaskbar)
  				StyleMask &= ~NSWindowStyle.Miniaturizable;
  
  			var win = window as NSWindow ?? ApplicationContext.Toolkit.GetNativeWindow(window) as NSWindow;
  
  			if (ParentWindow != win) {
  				// remove from the previous parent
  				if (ParentWindow != null)
  					ParentWindow.RemoveChildWindow(this);
  
  				ParentWindow = win;
  				// A window must be visible to be added to a parent. See InternalShow().
  				if (Visible)
  					ParentWindow.AddChildWindow(this, NSWindowOrderingMode.Above);
  			}
  		}
  
  		bool IWindowFrameBackend.Resizable {
  			get {
  				return (StyleMask & NSWindowStyle.Resizable) != 0;
  			}
  			set {
  				if (value)
  					StyleMask |= NSWindowStyle.Resizable;
  				else
  					StyleMask &= ~NSWindowStyle.Resizable;
  			}
  		}
  		
  		public void SetPadding (double left, double top, double right, double bottom)
  		{
  			LayoutWindow ();
  		}
  
  		void IWindowFrameBackend.Move (double x, double y)
  		{
  			var r = FrameRectFor (new CGRect ((nfloat)x, (nfloat)y, Frame.Width, Frame.Height));
  			SetFrame (r, true);
  		}
  		
  		void IWindowFrameBackend.SetSize (double width, double height)
  		{
  			var cr = ContentRectFor (Frame);
  			if (width == -1)
  				width = cr.Width;
  			if (height == -1)
  				height = cr.Height;
  			var r = FrameRectFor (new CGRect ((nfloat)cr.X, (nfloat)cr.Y, (nfloat)width, (nfloat)height));
  			SetFrame (r, true);
  			LayoutWindow ();
  		}
  		
  		Rectangle IWindowFrameBackend.Bounds {
  			get {
  				var b = ContentRectFor (Frame);
  				var r = MacDesktopBackend.ToDesktopRect (b);
  				return new Rectangle ((int)r.X, (int)r.Y, (int)r.Width, (int)r.Height);
  			}
  			set {
  				var r = MacDesktopBackend.FromDesktopRect (value);
  				var fr = FrameRectFor (r);
  				SetFrame (fr, true);
  			}
  		}
  		
  		public void SetMainMenu (IMenuBackend menu)
  		{
  			var m = (MenuBackend) menu;
  			m.SetMainMenuMode ();
  			NSApplication.SharedApplication.Menu = m;
  			
  //			base.Menu = m;
  		}
  		
  		#endregion
  
  		static Selector closeSel = new Selector ("close");
  
  		static readonly bool XamMacDangerousDispose = Version.Parse(Constants.Version) < new Version(5, 6);
  
  		bool disposing, disposed;
  
  		protected override void Dispose(bool disposing)
  		{
  
gt: 			if (!disposed && disposing)