Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

324 lines
8.8 KiB
C#

// 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) 2004-2008 Novell, Inc.
//
// Authors:
// Peter Dennis Bartok pbartok@novell.com
// Ivan N. Zlatev <contact@i-nz.net>
//
// COMPLETE
//#define ExternalExceptionHandler
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
namespace System.Windows.Forms
{
public class NativeWindow : MarshalByRefObject, IWin32Window
{
IntPtr window_handle = IntPtr.Zero;
static Hashtable window_collection = new Hashtable();
[ThreadStatic]
static NativeWindow WindowCreating;
#region Public Constructors
public NativeWindow()
{
window_handle=IntPtr.Zero;
}
#endregion // Public Constructors
#region Public Instance Properties
public IntPtr Handle {
get {
return window_handle;
}
}
#endregion // Public Instance Properties
#region Public Static Methods
public static NativeWindow FromHandle(IntPtr handle)
{
return FindFirstInTable (handle);
}
#endregion // Public Static Methods
#region Private and Internal Methods
internal void InvalidateHandle()
{
RemoveFromTable (this);
window_handle = IntPtr.Zero;
}
#endregion
#region Public Instance Methods
public void AssignHandle(IntPtr handle)
{
RemoveFromTable (this);
window_handle = handle;
AddToTable (this);
OnHandleChange();
}
private static void AddToTable (NativeWindow window)
{
IntPtr handle = window.Handle;
if (handle == IntPtr.Zero)
return;
lock (window_collection) {
object current = window_collection[handle];
if (current == null) {
window_collection.Add (handle, window);
} else {
NativeWindow currentWindow = current as NativeWindow;
if (currentWindow != null) {
if (currentWindow != window) {
ArrayList windows = new ArrayList ();
windows.Add (currentWindow);
windows.Add (window);
window_collection[handle] = windows;
}
} else { // list of windows
ArrayList windows = (ArrayList) window_collection[handle];
if (!windows.Contains (window))
windows.Add (window);
}
}
}
}
private static void RemoveFromTable (NativeWindow window)
{
IntPtr handle = window.Handle;
if (handle == IntPtr.Zero)
return;
lock (window_collection) {
object current = window_collection[handle];
if (current != null) {
NativeWindow currentWindow = current as NativeWindow;
if (currentWindow != null) {
window_collection.Remove (handle);
} else { // list of windows
ArrayList windows = (ArrayList) window_collection[handle];
windows.Remove (window);
if (windows.Count == 0)
window_collection.Remove (handle);
else if (windows.Count == 1)
window_collection[handle] = windows[0];
}
}
}
}
private static NativeWindow FindFirstInTable (IntPtr handle)
{
if (handle == IntPtr.Zero)
return null;
NativeWindow window = null;
lock (window_collection) {
object current = window_collection[handle];
if (current != null) {
window = current as NativeWindow;
if (window == null) {
ArrayList windows = (ArrayList) current;
if (windows.Count > 0)
window = (NativeWindow) windows[0];
}
}
}
return window;
}
public virtual void CreateHandle(CreateParams cp)
{
if (cp != null) {
WindowCreating = this;
window_handle=XplatUI.CreateWindow(cp);
WindowCreating = null;
if (window_handle != IntPtr.Zero)
AddToTable (this);
}
}
public void DefWndProc(ref Message m)
{
m.Result=XplatUI.DefWndProc(ref m);
}
public virtual void DestroyHandle()
{
if (window_handle != IntPtr.Zero) {
XplatUI.DestroyWindow(window_handle);
}
}
public virtual void ReleaseHandle()
{
RemoveFromTable (this);
window_handle=IntPtr.Zero;
OnHandleChange();
}
#endregion // Public Instance Methods
#region Protected Instance Methods
~NativeWindow()
{
}
protected virtual void OnHandleChange()
{
}
protected virtual void OnThreadException(Exception e)
{
Application.OnThreadException(e);
}
protected virtual void WndProc(ref Message m)
{
DefWndProc(ref m);
}
internal static IntPtr WndProc(IntPtr hWnd, Msg msg, IntPtr wParam, IntPtr lParam)
{
IntPtr result = IntPtr.Zero;
Message m = new Message();
m.HWnd = hWnd;
m.Msg = (int)msg;
m.WParam = wParam;
m.LParam = lParam;
m.Result = IntPtr.Zero;
#if debug
Console.WriteLine("NativeWindow.cs ({0}, {1}, {2}, {3}): result {4}", hWnd, msg, wParam, lParam, m.Result);
#endif
NativeWindow window = null;
try {
object current = null;
lock (window_collection) {
current = window_collection[hWnd];
}
window = current as NativeWindow;
if (current == null)
window = EnsureCreated (window, hWnd);
if (window != null) {
window.WndProc (ref m);
result = m.Result;
} else if (current is ArrayList) {
ArrayList windows = (ArrayList) current;
lock (windows) {
if (windows.Count > 0) {
window = EnsureCreated ((NativeWindow)windows[0], hWnd);
window.WndProc (ref m);
// the first one is the control's one. all others are synthetic,
// so we want only the result from the control
result = m.Result;
for (int i=1; i < windows.Count; i++)
((NativeWindow)windows[i]).WndProc (ref m);
}
}
} else {
result = XplatUI.DefWndProc (ref m);
}
}
catch (Exception ex) {
#if !ExternalExceptionHandler
if (window != null) {
if (msg == Msg.WM_PAINT && window is Control.ControlNativeWindow) {
// Replace control with a red cross
var control = ((Control.ControlNativeWindow)window).Owner;
control.Hide ();
var redCross = new Control (control.Parent, string.Empty);
redCross.BackColor = Color.White;
redCross.ForeColor = Color.Red;
redCross.Bounds = control.Bounds;
redCross.Paint += HandleRedCrossPaint;
}
window.OnThreadException (ex);
}
#else
throw;
#endif
}
#if debug
Console.WriteLine("NativeWindow.cs: Message {0}, result {1}", msg, m.Result);
#endif
return result;
}
private static void HandleRedCrossPaint (object sender, PaintEventArgs e)
{
var control = sender as Control;
using (var pen = new Pen (control.ForeColor, 2)) {
var paintRect = control.DisplayRectangle;
e.Graphics.DrawRectangle (pen, paintRect.Left + 1,
paintRect.Top + 1, paintRect.Width - 1, paintRect.Height - 1);
// NOTE: .NET's drawing of the red cross seems to have a bug
// that draws the bottom and right of the rectangle only 1 pixel
// wide. We would get a nicer rectangle using the following code,
// but that runs into a problem with libgdiplus.
//var paintRect = control.DisplayRectangle;
//paintRect.Inflate (-1, -1);
//e.Graphics.DrawRectangle (pen, paintRect);
e.Graphics.DrawLine (pen, paintRect.Location,
paintRect.Location + paintRect.Size);
e.Graphics.DrawLine (pen, new Point (paintRect.Left, paintRect.Bottom),
new Point (paintRect.Right, paintRect.Top));
}
}
private static NativeWindow EnsureCreated (NativeWindow window, IntPtr hWnd)
{
// we need to do this AssignHandle here instead of relying on
// Control.WndProc to do it, because subclasses can override
// WndProc, install their own WM_CREATE block, and look at
// this.Handle, and it needs to be set. Otherwise, we end up
// recursively creating windows and emitting WM_CREATE.
if (window == null && WindowCreating != null) {
window = WindowCreating;
WindowCreating = null;
if (window.Handle == IntPtr.Zero)
window.AssignHandle (hWnd);
}
return window;
}
#endregion // Protected Instance Methods
}
}