using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace SelectionOverlayWrap
{
/// <summary>
/// The supported Mouse Buttons
/// </summary>
public enum VKKeys: uint
{
LBUTTON = 1,
RBUTTON = 2,
MBUTTON = 4,
XBUTTON1 = 5,
XBUTTON2 = 6
};
/// <summary>
/// Base interface, which makes it possible to narrow our Listener down to somethin' known
/// </summary>
public interface ISelectionOverlay
{
}
/// <summary>
/// OnMove interface, inherits from ISelectionOverlay
/// </summary>
public interface ISelectionOverlayOnMove: ISelectionOverlay
{
/// <summary>
/// Will be sent when:
/// On each mouse movement which causes an selection overlay window resizement
/// </summary>
/// <param name="Tag">Originally supplied Tag</param>
/// <param name="Rectangle">The square of the overlay</param>
void OnMove(object Tag, Rectangle Rectangle);
}
/// <summary>
/// OnEnd interface, inherits from ISelectionOverlay
/// </summary>
public interface ISelectionOverlayOnEnd: ISelectionOverlay
{
/// <summary>
/// Will be sent when:
/// After the mouse button went up and the selection overlay is closing
///
/// Note:
/// On no movement the rect will contain alls nills
/// </summary>
/// <param name="Tag">Originally supplied Tag</param>
/// <param name="Rectangle">The square of the overlay</param>
void OnEnd(object Tag, Rectangle Rectangle);
}
/// <summary>
/// OnEnd interface, inherits from ISelectionOverlay
/// </summary>
public interface ISelectionOverlayOnFillAlpha: ISelectionOverlay
{
/// <summary>
/// Will be sent when:
/// After the mouse button went up and the selection overlay is closing
///
/// Note:
/// On no movement the rect will contain alls nills
/// </summary>
/// <param name="Tag">Originally supplied Tag</param>
/// <param name="Rectangle">The square of the overlay</param>
bool OnFillAlpha(object Tag, IntPtr BitmapDIBHandle);
}
/// <summary>
/// OnEnd interface, inherits from ISelectionOverlay
/// </summary>
public interface ISelectionOverlayOnFillRGB: ISelectionOverlay
{
/// <summary>
/// Will be sent when:
/// After the mouse button went up and the selection overlay is closing
///
/// Note:
/// On no movement the rect will contain alls nills
/// </summary>
/// <param name="Tag">Originally supplied Tag</param>
/// <param name="Rectangle">The square of the overlay</param>
bool OnFillRGB(object Tag, IntPtr hDC, int Width, int Height);
}
/// <summary>
/// Selection Overlay C# wrapper class
/// </summary>
public class SelectionOverlay
{
#region API
/// <summary>
/// lParam of our incoming notifications
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public static implicit operator Rectangle(RECT input)
{
return new Rectangle(input.Left, input.Top, input.Right - input.Left, input.Bottom - input.Top);
}
public static implicit operator RECT(Rectangle input)
{
return new RECT() { Left = input.Left, Top = input.Top, Right = input.Right, Bottom = input.Bottom };
}
}
/// <summary>
/// Our function prototype
/// </summary>
/// <param name="Tag">Tag specified by caller</param>
/// <param name="Overlay">RECT of the overlay</param>
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void OnMove(int Tag, RECT Overlay);
/// <summary>
/// Our function prototype
/// </summary>
/// <param name="Tag">Tag specified by caller</param>
/// <param name="Overlay">RECT of the overlay</param>
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate uint OnFillAlpha(int Tag, IntPtr BitmapDIBHandle);
/// <summary>
/// Our function prototype
/// </summary>
/// <param name="Tag">Tag specified by caller</param>
/// <param name="Overlay">RECT of the overlay</param>
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate uint OnFillRGB(int Tag, IntPtr hDC, int Width, int Height);
[StructLayout(LayoutKind.Sequential)]
private struct SELECTIONOVERLAYDATA_CB
{
public int Parent; //HWND where the selection overlay should stay above
public int OverlayColor; //Color of the main square
public int BorderColor; //Color of the border
public int BorderWidth; //Width (in pixels) of the border
public byte AlphaLevel; //Transparency alpha level of the main square (in case alpha blending per pixel fails will also be applied on border)
public byte BorderAlphaLevel; //Transparency alpha level of the border
public int Tag; //Tag, will be passed through onto the WM_ONMOVE and WM_ONEND notification
public VKKeys ButtonLead; //Which mouse button we operate with (on start this mouse button must be in down state), if 0 will be directed to LBUTTON
public int Dummy; //Unused in our callback version, implemented to be compatible for our normal variant
[MarshalAs(UnmanagedType.FunctionPtr)]
public OnMove Move; //Function callback may be 0
[MarshalAs(UnmanagedType.FunctionPtr)]
public OnMove EndCall; //Function callback MANDATORY
public OnFillAlpha FillAlpha; //Function callback to draw the 32 alpha bitmap yourself
public OnFillRGB FillRgb; //Function callback to draw the hdc yourself in case 32 alpha bitmap drawing fails
};
[StructLayout(LayoutKind.Sequential)]
private struct SELECTIONOVERLAYRECT_CB
{
public RECT ClientRect; //Rectangle on the desktop we have to stay between
public int Parent; //HWND where the selection overlay should stay above
public int OverlayColor; //Color of the main square
public int BorderColor; //Color of the border
public int BorderWidth; //Width (in pixels) of the border
public byte AlphaLevel; //Transparency alpha level of the main square (in case alpha blending per pixel fails will also be applied on border)
public byte BorderAlphaLevel; //Transparency alpha level of the border
public int Tag; //Tag, will be passed through onto the WM_ONMOVE and WM_ONEND notification
public VKKeys ButtonLead; //Which mouse button we operate with (on start this mouse button must be in down state), if 0 will be directed to LBUTTON
public int Dummy; //Unused in our callback version, implemented to be compatible for our normal variant
[MarshalAs(UnmanagedType.FunctionPtr)]
public OnMove Move; //Function callback may be 0
[MarshalAs(UnmanagedType.FunctionPtr)]
public OnMove EndCall; //Function callback MANDATORY
public OnFillAlpha FillAlpha; //Function callback to draw the 32 alpha bitmap yourself
public OnFillRGB FillRgb; //Function callback to draw the hdc yourself in case 32 alpha bitmap drawing fails
};
/// <summary>
/// The actual api call to make
/// </summary>
/// <param name="Data">The data to kickstart our friend with</param>
[DllImport("SelectionOverlay.dll", EntryPoint = "?SelectionOverlay@@YGXAAUtagSELECTIONOVERLAYDATA_CB@@@Z")]
private static extern void SelectionOverlayApi(ref SELECTIONOVERLAYDATA_CB Data);
/// <summary>
/// The actual api call to make
/// </summary>
/// <param name="Data">The data to kickstart our friend with</param>
[DllImport("SelectionOverlay.dll", EntryPoint = "?SelectionOverlay@@YGXAAUtagSELECTIONOVERLAYRECT_CB@@@Z")]
private static extern void SelectionOverlayApi(ref SELECTIONOVERLAYRECT_CB Data);
[DllImport("SelectionOverlay.dll", EntryPoint = "?CreateAlphaColor@@YGKKE@Z")]
private static extern int CreateAlphaColorApi(int RGB, byte Alpha);
#endregion
private static SELECTIONOVERLAYDATA_CB SendData = new SELECTIONOVERLAYDATA_CB(); //Create an new data set
private static SELECTIONOVERLAYRECT_CB SendRect = new SELECTIONOVERLAYRECT_CB(); //Create an new data set
private static object _Tag = null; //Remember the tag to pass on
private static ISelectionOverlayOnEnd _OnEnd = null; //Remmeber the interfaces we need to call back to
private static ISelectionOverlayOnMove _OnMove = null;
private static ISelectionOverlayOnFillAlpha _OnFillAlpha = null;
private static ISelectionOverlayOnFillRGB _OnFillRGB = null;
/// <summary>
/// Assignment of static variables appear before entering a static method
/// This way we've been assured delegates are in place
/// (You can see this as an parameterless static Constructor)
/// </summary>
private static bool Initialised = Init(); //Load our delegates
/// <summary>
/// We're obligated to keep the delegates alive
/// So we kept our structure static and place in our functions
/// As long as we don't override them nor new the SendData
/// The delegates stay alive
/// </summary>
/// <returns>Always true</returns>
private static bool Init()
{
SendRect.FillAlpha = On_FillAlpha;
SendRect.FillRgb = On_FillRGB;
SendRect.EndCall = On_End;
SendRect.Move = On_Move;
SendRect.Dummy = 0;
SendData.FillAlpha = On_FillAlpha;
SendData.FillRgb = On_FillRGB;
SendData.EndCall = On_End; //Remember our On End callback routine
SendData.Move = On_Move; //Remember our On Move callback routine
SendData.Dummy = 0; //Just for the readin'
return true; //Just return somethin'
}
/// <summary>
/// Creates an RGB colorcode from supplied red, green and blue value
/// </summary>
/// <param name="r">Red</param>
/// <param name="g">Green</param>
/// <param name="b">Blue</param>
/// <returns>RGB colorcode</returns>
public static int RGB(byte r, byte g, byte b)
{
return (((byte)(r) | ((short)((byte)(g)) << 8)) | (((int)(byte)(b)) << 16)); //Create an RGB value (direct conversion of the old RGB macro in windows.h)
}
/// <summary>
/// Shows the selection overlay
/// </summary>
/// <param name="OverlayColor">
/// <para>Alpha Color value</para>
/// <para>The alpha of the color will be used on the alphablending,</para>
/// <para>so make sure u give it an value if u want to be able to see through the overlay.</para>
/// <para>�</para>
/// <para>In rare occations it ain't possible to create an bitmap, drawing directly on the DC will be done in that case,</para>
/// <para>the alpha value of the overlay color in this case will be used for both colors.</para>
/// <para>�</para>
/// </param>
/// <param name="BorderColor">
/// <para>Alpha Color value</para>
/// <para>The alpha of the color will be used on the alphablending,</para>
/// <para>so make sure u give it an value if u want to be able to see through the border.</para>
/// <para>�</para>
/// </param>
/// <param name="BorderWidth">
/// <para>Width of the border in pixels</para>
/// <para>�</para>
/// </param>
/// <param name="Parent">
/// <para>Listener on the notifications</para>
/// <para>ISelectionOverlayOnEnd implementation is Mandatory,</para>
/// <para>ISelectionOverlayOnMove implementation is Optional.</para>
/// <para>ISelectionOverlayOnFillAlpha implementation is Optional.</para>
/// <para>ISelectionOverlayOnFillRGB implementation is Optional.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Hwnd">
/// <para>Window to show the Selection Overlay above, value is mandatory.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Key">
/// <para>Mouse key we're ment to watch to</para>
/// <para>�</para>
/// <para>Meanings:</para>
/// <para>LBUTTON - Left Mouse button</para>
/// <para>RBUTTON - Right Mouse button</para>
/// <para>MBUTTON - Middle Mouse button</para>
/// <para>XBUTTON1 - The first x mouse button (five-button mouse)</para>
/// <para>XBUTTON2 - The second x mouse button (five-button mouse)</para>
/// <para>�</para>
/// <para>The selected button must be in a down state on opening the selection overlay.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Tag">Optional value which will be passed on within the notifications</param>
/// <param name="OuterLimits">
/// <para>Screen limits where the overlay has to stay between,</para>
/// <para>if default(rectangle) Hwnd will be used for these limits.</para></param>
public static void Show(Color OverlayColor,
Color BorderColor,
uint BorderWidth,
ISelectionOverlay Parent,
IntPtr Hwnd,
VKKeys Key = VKKeys.LBUTTON,
object Tag = null,
Rectangle OuterLimits = default(Rectangle))
{
if (OuterLimits == default(Rectangle))
{
ShowNormal(OverlayColor, BorderColor, BorderWidth, Parent, Hwnd, Key, Tag);
}
else
{
ShowRect(OuterLimits, OverlayColor, BorderColor, BorderWidth, Parent, Hwnd, Key, Tag);
}
}
/// <summary>
/// Shows the selection overlay
/// </summary>
/// <param name="OverlayColor">
/// <para>Alpha Color value</para>
/// <para>The alpha of the color will be used on the alphablending,</para>
/// <para>so make sure u give it an value if u want to be able to see through the overlay.</para>
/// <para>�</para>
/// <para>In rare occations it ain't possible to create an bitmap, drawing directly on the DC will be done in that case,</para>
/// <para>the alpha value of the overlay color in this case will be used for both colors.</para>
/// <para>�</para>
/// </param>
/// <param name="BorderColor">
/// <para>Alpha Color value</para>
/// <para>The alpha of the color will be used on the alphablending,</para>
/// <para>so make sure u give it an value if u want to be able to see through the border.</para>
/// <para>�</para>
/// </param>
/// <param name="BorderWidth">
/// <para>Width of the border in pixels</para>
/// <para>�</para>
/// </param>
/// <param name="Parent">
/// <para>Listener on the notifications</para>
/// <para>ISelectionOverlayOnEnd implementation is Mandatory,</para>
/// <para>ISelectionOverlayOnMove implementation is Optional.</para>
/// <para>ISelectionOverlayOnFillAlpha implementation is Optional.</para>
/// <para>ISelectionOverlayOnFillRGB implementation is Optional.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Hwnd">
/// <para>Window to show the Selection Overlay above, value is mandatory.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Key">
/// <para>Mouse key we're ment to watch to</para>
/// <para>�</para>
/// <para>Meanings:</para>
/// <para>LBUTTON - Left Mouse button</para>
/// <para>RBUTTON - Right Mouse button</para>
/// <para>MBUTTON - Middle Mouse button</para>
/// <para>XBUTTON1 - The first x mouse button (five-button mouse)</para>
/// <para>XBUTTON2 - The second x mouse button (five-button mouse)</para>
/// <para>�</para>
/// <para>The selected button must be in a down state on opening the selection overlay.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Tag">Optional value which will be passed on within the notifications</param>
private static void ShowNormal(Color OverlayColor,
Color BorderColor,
uint BorderWidth,
ISelectionOverlay Parent,
IntPtr Hwnd,
VKKeys Key = VKKeys.LBUTTON,
object Tag = null)
{
SendData.AlphaLevel = OverlayColor.A; //Place the alpha's
SendData.BorderAlphaLevel = BorderColor.A;
SendData.BorderWidth = (int)BorderWidth; //Set border width
SendData.OverlayColor = RGB(OverlayColor.R, OverlayColor.G, OverlayColor.B); //Create RGBs from the alpha colors and place them
SendData.BorderColor = RGB(BorderColor.R, BorderColor.G, BorderColor.B); ;
SendData.Parent = Hwnd.ToInt32(); //Handle to show above
SendData.ButtonLead = Key; //Mouse Button we need to focus on
_Tag = Tag; //Remember tag
_OnFillAlpha = Parent as ISelectionOverlayOnFillAlpha; //Remember the interfaces to call back to
_OnFillRGB = Parent as ISelectionOverlayOnFillRGB; //Remember the interfaces to call back to
_OnEnd = Parent as ISelectionOverlayOnEnd; //Remember the interfaces to call back to
_OnMove = Parent as ISelectionOverlayOnMove;
if (_OnEnd != null) //If we at least have an OnEnd interface
{
SelectionOverlayApi(ref SendData); //Show the overlay
}
}
/// <summary>
/// Shows the selection overlay
/// </summary>
/// <param name="OverlayColor">
/// <para>Alpha Color value</para>
/// <para>The alpha of the color will be used on the alphablending,</para>
/// <para>so make sure u give it an value if u want to be able to see through the overlay.</para>
/// <para>�</para>
/// <para>In rare occations it ain't possible to create an bitmap, drawing directly on the DC will be done in that case,</para>
/// <para>the alpha value of the overlay color in this case will be used for both colors.</para>
/// <para>�</para>
/// </param>
/// <param name="BorderColor">
/// <para>Alpha Color value</para>
/// <para>The alpha of the color will be used on the alphablending,</para>
/// <para>so make sure u give it an value if u want to be able to see through the border.</para>
/// <para>�</para>
/// </param>
/// <param name="BorderWidth">
/// <para>Width of the border in pixels</para>
/// <para>�</para>
/// </param>
/// <param name="Parent">
/// <para>Listener on the notifications</para>
/// <para>ISelectionOverlayOnEnd implementation is Mandatory,</para>
/// <para>ISelectionOverlayOnMove implementation is Optional.</para>
/// <para>ISelectionOverlayOnFillAlpha implementation is Optional.</para>
/// <para>ISelectionOverlayOnFillRGB implementation is Optional.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Hwnd">
/// <para>Window to show the Selection Overlay above, value is mandatory.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Key">
/// <para>Mouse key we're ment to watch to</para>
/// <para>�</para>
/// <para>Meanings:</para>
/// <para>LBUTTON - Left Mouse button</para>
/// <para>RBUTTON - Right Mouse button</para>
/// <para>MBUTTON - Middle Mouse button</para>
/// <para>XBUTTON1 - The first x mouse button (five-button mouse)</para>
/// <para>XBUTTON2 - The second x mouse button (five-button mouse)</para>
/// <para>�</para>
/// <para>The selected button must be in a down state on opening the selection overlay.</para>
/// <para>If this requirement is not met, the selection overlay will not be shown.</para>
/// <para>�</para>
/// </param>
/// <param name="Tag">Optional value which will be passed on within the notifications</param>
/// <param name="OuterLimits">
/// <para>Screen limits where the overlay has to stay between,</para>
/// <para>if default(rectangle) Hwnd will be used for these limits.</para></param>
private static void ShowRect(Rectangle OuterLimits,
Color OverlayColor,
Color BorderColor,
uint BorderWidth,
ISelectionOverlay Parent,
IntPtr Hwnd,
VKKeys Key = VKKeys.LBUTTON,
object Tag = null)
{
SendRect.AlphaLevel = OverlayColor.A; //Place the alpha's
SendRect.BorderAlphaLevel = BorderColor.A;
SendRect.BorderWidth = (int)BorderWidth; //Set border width
SendRect.OverlayColor = RGB(OverlayColor.R, OverlayColor.G, OverlayColor.B); //Create RGBs from the alpha colors and place them
SendRect.BorderColor = RGB(BorderColor.R, BorderColor.G, BorderColor.B); ;
SendRect.Parent = Hwnd.ToInt32(); //Handle to show above
SendRect.ButtonLead = Key; //Mouse Button we need to focus on
SendRect.ClientRect = OuterLimits;
_Tag = Tag; //Remember tag
_OnFillAlpha = Parent as ISelectionOverlayOnFillAlpha; //Remember the interfaces to call back to
_OnFillRGB = Parent as ISelectionOverlayOnFillRGB; //Remember the interfaces to call back to
_OnEnd = Parent as ISelectionOverlayOnEnd; //Remember the interfaces to call back to
_OnMove = Parent as ISelectionOverlayOnMove;
if (_OnEnd != null) //If we at least have an OnEnd interface
{
SelectionOverlayApi(ref SendRect); //Show the overlay
}
}
/// <summary>
/// Converts RGB value to alpha weighted color
/// </summary>
/// <param name="RGB">RGB Color</param>
/// <param name="Alpha">Weight</param>
/// <returns>Alpha weighted color</returns>
public static int CreateAlphaColor(int RGB, byte Alpha)
{
return CreateAlphaColorApi(RGB, Alpha);
}
/// <summary>
/// Will be called on OnMove notification
/// </summary>
/// <param name="Tag">ignored</param>
/// <param name="Rectangle">The square of the overlay</param>
private static void On_Move(int Tag, RECT Rectangle)
{
if (_OnMove != null)
{
try
{
_OnMove.OnMove(_Tag, Rectangle);
}
catch { } //I don't want to crash for anything going wrong outside where i've code influence
}
}
/// <summary>
/// Will be called on OnEnd notification
/// </summary>
/// <param name="Tag">ignored</param>
/// <param name="Rectangle">The square of the overlay</param>
private static void On_End(int Tag, RECT Rectangle)
{
if (_OnEnd != null)
{
try
{
_OnEnd.OnEnd(_Tag, Rectangle);
}
catch { } //I don't want to crash for anything going wrong outside where i've code influence
}
}
/// <summary>
/// Will be called on OnFillAlpha notification
/// </summary>
/// <param name="Tag">ignored</param>
/// <param name="BitmapDIBHandle">hBitmap of the 32 bit created bitmap to fill</param>
private static uint On_FillAlpha(int Tag, IntPtr BitmapDIBHandle)
{
bool Result = false;
uint iResult = 0;
if (_OnFillAlpha != null)
{
try
{
Result = _OnFillAlpha.OnFillAlpha(_Tag, BitmapDIBHandle);
}
catch { } //I don't want to crash for anything going wrong outside where i've code influence
}
if (Result)
{
iResult = 1;
}
return iResult;
}
/// <summary>
/// Will be called on On_FillRGB notification
/// </summary>
/// <param name="Tag">ignored</param>
/// <param name="hDC">DC To Fill</param>
/// <param name="Width">Width of DC</param>
/// <param name="Height">Height of DC</param>
private static uint On_FillRGB(int Tag, IntPtr hDC, int Width, int Height)
{
bool Result = false;
uint iResult = 0;
if (_OnFillRGB != null)
{
try
{
Result = _OnFillRGB.OnFillRGB(_Tag, hDC, Width, Height);
}
catch { } //I don't want to crash for anything going wrong outside where i've code influence
}
if (Result)
{
iResult = 1;
}
return iResult;
}
}
}