-
March 8th, 2009, 11:08 AM
#1
Multiple unassigned tooltips question
My idea was to create a simple function to add/relocate/remove up to 30 unassigned tooltips. My experience in C# is about a month and the only idea how to make it was taken from autohotkey code and translated to C#. The code below is able to create and remove tooltips, but width of each tooltip equal ONE character. How to fix this bug?
Code:
using System;
using System.Runtime.InteropServices;
namespace ToolTipTest
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
ToolTip.CoordMode(false); //we can define screen coordinates or active window relative coordinates, false is screen coordinates
ToolTip.Show("111", 0, 0, 1); //adding tooltip number 1 with "111" at x=0,y=0 of a screen
System.Threading.Thread.Sleep(1000);
ToolTip.Show("222", 20, 20, 2); //adding tooltip number 2 with "222" at x=20,y=20 of a screen
System.Threading.Thread.Sleep(1000);
ToolTip.Remove(1); //removing tooltip 1
System.Threading.Thread.Sleep(1000);
ToolTip.Show("", null, null, 2); //removing tooltip 2 (another method)
System.Threading.Thread.Sleep(3000);
}
}
public struct ToolTip
{
#region API
[DllImport("User32.dll", SetLastError = true)]
private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref TOOLINFO lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
[DllImport("User32.dll", SetLastError = true)]
private static extern int GetClientRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("User32.dll", SetLastError = true)]
private static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
int uFlags);
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
private static extern int GetSystemMetrics(int sysMetric);
[DllImport("user32.dll", EntryPoint = "IsWindow")]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetWindowRect")]
private static extern bool GetWindowRect(IntPtr handle, out RECT rect);
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", EntryPoint = "GetCursorPos")]
private static extern int GetCursorPos(out POINTAPI point);
[DllImport("user32.dll", EntryPoint = "GetForegroundWindow")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", EntryPoint = "CreateWindowEx")]
private static extern IntPtr CreateWindowEx(
int dwExStyle,
string lpClassName,
string lpWindowName,
uint dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
private const ushort WM_USER = 0x0400;
private const byte WS_EX_TOPMOST = 0x0008;
private const int CW_USEDEFAULT = unchecked((int)0x80000000);
private const byte TTS_ALWAYSTIP = 0x01;
private const byte TTS_NOPREFIX = 0x02;
private const byte TTF_TRACK = 0x0020;
private const ushort TTM_ADDTOOLA = (WM_USER + 4);
private const ushort TTM_UPDATETIPTEXT = (WM_USER + 12);
private const ushort TTM_TRACKACTIVATE = (WM_USER + 17);
private const ushort TTM_TRACKPOSITION = (WM_USER + 18);
private const ushort TTM_SETMAXTIPWIDTH = (WM_USER + 24);
private const int SM_CXVIRTUALSCREEN = 78;
private const int SM_XVIRTUALSCREEN = 76;
private const int SM_YVIRTUALSCREEN = 77;
private const int SM_CYVIRTUALSCREEN = 79;
private const int SM_CXSCREEN = 0;
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINTAPI
{
public int x;
public int y;
}
#endregion
private static bool winClose(IntPtr hWnd)
{
if (hWnd != IntPtr.Zero)
{
//WM_CLOSE = 0x10
SendMessage(hWnd, 0x10, 0, 0);
return true;
}
return false;
}
private static void GetVirtualDesktopRect(out RECT aRect)
{
aRect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
if (aRect.right != 0) // A non-zero value indicates the OS supports multiple monitors or at least SM_CXVIRTUALSCREEN.
{
aRect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); // Might be negative or greater than zero.
aRect.right += aRect.left;
aRect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); // Might be negative or greater than zero.
aRect.bottom = aRect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
}
else // Win95/NT do not support SM_CXVIRTUALSCREEN and such, so zero was returned.
GetWindowRect(GetDesktopWindow(), out aRect);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct TOOLINFO
{
public int cbSize;
public int uFlags;
public IntPtr hwnd;
public IntPtr uId;
public RECT rect;
public IntPtr hinst;
[MarshalAs(UnmanagedType.LPTStr)]
public String lpszText;
public IntPtr lParam;
}
private static bool ToolTipCoordMode = true; //default coord mode - relative
public static void CoordMode(bool if_screen_then_false)
{
ToolTipCoordMode = if_screen_then_false;
}
private static int MAKELONG(int wLow, int wHigh)
{
int low = (int)LOWORD(wLow);
short high = LOWORD(wHigh);
int product = 0x00010000 * (int)high;
int makeLong = (int)(low | product);
return makeLong;
}
private static short LOWORD(int dw)
{
short loWord = 0;
ushort andResult = (ushort)(dw & 0x00007FFF);
ushort mask = 0x8000;
if ((dw & 0x8000) != 0)
{
loWord = (short)(mask | andResult);
}
else
{
loWord = (short)andResult;
}
return loWord;
}
private static IntPtr[] g_hWndToolTip = new IntPtr[30]; //pointers to 30 tooltips
public static bool Remove(int aID) // remove tooltip with aID
{
return Show("", null, null, aID);
}
public static bool Show(string aText, System.Nullable<int> aX, System.Nullable<int> aY, System.Nullable<int> aID) // create or modify tooltip
{
int window_index = 0;
if (aID.HasValue) { window_index = (int)(aID) - 1; }
if (window_index < 0 | window_index >= 30) return false;
IntPtr tip_hwnd = g_hWndToolTip[window_index];
if (aText == "")
{
if (tip_hwnd != IntPtr.Zero & IsWindow(tip_hwnd))
{
winClose(tip_hwnd);
}
g_hWndToolTip[window_index] = IntPtr.Zero;
return true;
}
RECT dtw = new RECT();
GetVirtualDesktopRect(out dtw);
bool one_or_both_coords_unspecified = (!aX.HasValue | !aY.HasValue);
POINTAPI pt = new POINTAPI();
POINTAPI pt_cursor = new POINTAPI();
if (one_or_both_coords_unspecified)
{
GetCursorPos(out pt_cursor);
pt.x = pt_cursor.x + 16; // Set default spot to be near the mouse cursor.
pt.y = pt_cursor.y + 16; // Use 16 to prevent the tooltip from overlapping large cursors.
}
RECT rect = new RECT();
if ((aX.HasValue | aY.HasValue) & ToolTipCoordMode)
{
if (!GetWindowRect(GetForegroundWindow(), out rect))
return true;
}
// This will convert from relative to screen coordinates if rect contains non-zero values:
if (aX.HasValue)
pt.x = (int)aX + (int)rect.left;
if (aY.HasValue)
pt.y = (int)aY + (int)rect.top;
TOOLINFO ti = new TOOLINFO();
//ti.cbSize = Marshal.SizeOf(ti) - Marshal.SizeOf(typeof(void*)); //as in original autohotkey code
ti.cbSize = Marshal.SizeOf(ti); //another version which is also working
ti.uFlags = TTF_TRACK;
ti.lpszText = aText;
if (tip_hwnd == IntPtr.Zero | !IsWindow(tip_hwnd))
{
tip_hwnd = g_hWndToolTip[window_index] = CreateWindowEx(WS_EX_TOPMOST, "tooltips_class32", "", TTS_NOPREFIX | TTS_ALWAYSTIP
, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
SendMessage(tip_hwnd, TTM_ADDTOOLA, 0, ref ti);
// v1.0.21: GetSystemMetrics(SM_CXSCREEN) is used for the maximum width because even on a
// multi-monitor system, most users would not want a tip window to stretch across multiple monitors:
SendMessage(tip_hwnd, TTM_SETMAXTIPWIDTH, 0, GetSystemMetrics(SM_CXSCREEN));
// Must do these next two when the window is first created, otherwise GetWindowRect() below will retrieve
// a tooltip window size that is quite a bit taller than it winds up being:
SendMessage(tip_hwnd, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
SendMessage(tip_hwnd, TTM_TRACKACTIVATE, 1, ref ti);
}
SendMessage(tip_hwnd, TTM_UPDATETIPTEXT, 0, ref ti);
RECT ttw = new RECT();
GetWindowRect(tip_hwnd, out ttw); // Must be called this late to ensure the tooltip has been created by above.
int tt_width = ttw.right - ttw.left;
int tt_height = ttw.bottom - ttw.top;
// v1.0.21: Revised for multi-monitor support. I read somewhere that dtw.left can be negative (perhaps
// if the secondary monitor is to the left of the primary). So it seems best to assume it is possible:
if (pt.x + tt_width >= dtw.right)
pt.x = dtw.right - tt_width - 1;
if (pt.y + tt_height >= dtw.bottom)
pt.y = dtw.bottom - tt_height - 1;
if (one_or_both_coords_unspecified)
{
// Since Tooltip is being shown at the cursor's coordinates, try to ensure that the above
// adjustment doesn't result in the cursor being inside the tooltip's window boundaries,
// since that tends to cause problems such as blocking the tray area (which can make a
// tootip script impossible to terminate). Normally, that can only happen in this case
// (one_or_both_coords_unspecified == true) when the cursor is near the buttom-right
// corner of the screen (unless the mouse is moving more quickly than the script's
// ToolTip update-frequency can cope with, but that seems inconsequential since it
// will adjust when the cursor slows down):
ttw.left = pt.x;
ttw.top = pt.y;
ttw.right = ttw.left + tt_width;
ttw.bottom = ttw.top + tt_height;
if ((pt_cursor.x >= ttw.left) & (pt_cursor.x <= ttw.right) & (pt_cursor.y >= ttw.top) & (pt_cursor.y <= ttw.bottom))
{
// Push the tool tip to the upper-left side, since normally the only way the cursor can
// be inside its boundaries (when one_or_both_coords_unspecified == true) is when the
// cursor is near the bottom right corner of the screen.
pt.x = pt_cursor.x - tt_width - 3; // Use a small offset since it can't overlap the cursor
pt.y = pt_cursor.y - tt_height - 3; // when pushed to the the upper-left side of it.
}
}
SendMessage(tip_hwnd, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
// And do a TTM_TRACKACTIVATE even if the tooltip window already existed upon entry to this function,
// so that in case it was hidden or dismissed while its HWND still exists, it will be shown again:
SendMessage(tip_hwnd, TTM_TRACKACTIVATE, 1, ref ti);
return true;
}
}
}
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|