// hyperlinks.c
#include <windows.h>
#ifdef UNICODE
#define LONGSTRING_PREFIX L
#else // !UNICODE
#define LONGSTRING_PREFIX
#endif // UNICODE
#define PROP_HYPERLINK_STRUCT LONGSTRING_PREFIX"_HyperlinkStruct"
#define PROP_HYPERLINKPARENT_STRUCT LONGSTRING_PREFIX"_HyperlinkParentStruct"
// standard hand cursor definition
#ifndef IDC_HAND
#define IDC_HAND 147
#endif
// hyperlink'ed static text control structure
typedef struct hyperlink_s
{
HWND hWnd; // static text control window handle
WNDPROC pfnOrigProc; // original static text control window procedure
HFONT font; // underlined hypertext font used for this control
} hyperlink_t;
// hyperlink parent window structure
typedef struct hyperlinkparent_s
{
HWND hWnd; // hyperlink parent window handle
WNDPROC pfnOrigProc; // original static text control window parent procedure
} hyperlinkparent_t;
// hyperlink cursor
static HCURSOR hyperlink_cursor = NULL;
static LOGFONT hyperlink_logicalfont;
// hyperlinks.c function prototypes
void ConvertStaticToHyperlink (HWND hWndStatic);
static int WINAPI _HyperlinkParentProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
static int WINAPI _HyperlinkProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
void ConvertStaticToHyperlink (HWND hWndStatic)
{
// subclass the parent so we can color the controls as we desire
hyperlinkparent_t *hyperlinkparent;
hyperlink_t *hyperlink;
// first off, if the hand cursor isn't loaded yet, do it (this part is done once)
if (hyperlink_cursor == NULL)
{
// since IDC_HAND is not available on all operating systems, we will load the arrow cursor if IDC_HAND is not present.
hyperlink_cursor = LoadCursor (NULL, IDC_HAND);
if (hyperlink_cursor == NULL)
hyperlink_cursor = LoadCursor (NULL, IDC_ARROW); // fallback cursor
}
// does this control belong to a window ?
if (GetParent (hWndStatic) != NULL)
{
// mallocate space for a new hyperlink parent
hyperlinkparent = (hyperlinkparent_t *) malloc (sizeof (hyperlinkparent_t));
if (hyperlinkparent == NULL)
return; // on failure, just return
hyperlinkparent->hWnd = GetParent (hWndStatic); // save away the hyperlink parent's window handle
hyperlinkparent->pfnOrigProc = (WNDPROC) GetWindowLong (GetParent (hWndStatic), GWL_WNDPROC); // get its current window procedure
// has this parent NOT been already subclassed by another hyperlink ?
if (hyperlinkparent->pfnOrigProc != (WNDPROC) _HyperlinkParentProc)
{
// save a pointer to the hyperlink structure in the window's properties and subclass it by replacing its window procedure by our own
SetProp (GetParent (hWndStatic), PROP_HYPERLINKPARENT_STRUCT, (HANDLE) hyperlinkparent);
SetWindowLongPtr (GetParent (hWndStatic), GWL_WNDPROC, (long) (WNDPROC) _HyperlinkParentProc);
}
}
// mallocate space for a new hyperlink
hyperlink = (hyperlink_t *) malloc (sizeof (hyperlink_t));
if (hyperlink == NULL)
return; // on failure, just return
hyperlink->hWnd = hWndStatic; // get the static control's window handle
hyperlink->pfnOrigProc = (WNDPROC) GetWindowLong (hyperlink->hWnd, GWL_WNDPROC); // get the static control's window procedure
// create an updated font by adding an underline
hyperlink->font = (HFONT) SendMessage (hyperlink->hWnd, WM_GETFONT, 0, 0);
memset (&hyperlink_logicalfont, 0, sizeof (hyperlink_logicalfont));
GetObject (hyperlink->font, sizeof (hyperlink_logicalfont), &hyperlink_logicalfont);
hyperlink_logicalfont.lfUnderline = 1;
hyperlink->font = CreateFontIndirect (&hyperlink_logicalfont);
SendMessage (hyperlink->hWnd, WM_SETFONT, (WPARAM) hyperlink->font, 0);
// make sure the static control will send event notifications to its window procedure
SetWindowLong (hyperlink->hWnd, GWL_STYLE, GetWindowLong (hyperlink->hWnd, GWL_STYLE) | SS_NOTIFY);
// save a pointer to the hyperlink structure in the window's properties and subclass it by replacing its window procedure by our own
SetProp (hyperlink->hWnd, PROP_HYPERLINK_STRUCT, (HANDLE) hyperlink);
SetWindowLongPtr (hyperlink->hWnd, GWL_WNDPROC, (long) (WNDPROC) _HyperlinkProc);
return; // finished transforming the static text control
}
static int WINAPI _HyperlinkParentProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
// this function hooks the hyperlink parent's control drawing messages so as to draw hyperlinks differently from normal static controls
hyperlinkparent_t *hyperlinkparent;
LRESULT result;
// get a pointer to the hyperlink structure
hyperlinkparent = (hyperlinkparent_t *) GetProp (hWnd, PROP_HYPERLINKPARENT_STRUCT);
if (hyperlinkparent == NULL)
return (DefWindowProc (hWnd, message, wParam, lParam)); // consistency check
// are we drawing a static control ?
if (message == WM_CTLCOLORSTATIC)
{
result = CallWindowProc (hyperlinkparent->pfnOrigProc, hWnd, message, wParam, lParam); // call the window procedure normally
if ((WNDPROC) GetWindowLong ((HWND) lParam, GWL_WNDPROC) == (WNDPROC) _HyperlinkProc)
SetTextColor ((HDC) wParam, RGB (0, 0, 192)); // but notify the parent to draw this text in blue if it's one of our hyperlinks
return (result); // and return the original window proc return result
}
// now call the original window procedure to let things go normally
return (CallWindowProc (hyperlinkparent->pfnOrigProc, hWnd, message, wParam, lParam));
}
static int WINAPI _HyperlinkProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
// this function hooks the messages directed to the static text control that we turned into a hyperlink
hyperlink_t *hyperlink;
// get a pointer to the hyperlink structure
hyperlink = (hyperlink_t *) GetProp (hWnd, PROP_HYPERLINK_STRUCT);
if (hyperlink == NULL)
return (DefWindowProc (hWnd, message, wParam, lParam)); // consistency check
// is the mouse moving inside the static control ?
if (message == WM_SETCURSOR)
{
if (hyperlink_cursor != NULL)
SetCursor (hyperlink_cursor); // set the static control cursor to be the pointing hand, if we have it
return (1); // halt further processing on this message
}
// else are we destroying the static control ?
else if (message == WM_DESTROY)
{
if (hyperlink != NULL)
{
SetWindowLongPtr (hWnd, GWL_WNDPROC, (long) hyperlink->pfnOrigProc); // restore the original static control procedure
RemoveProp (hWnd, PROP_HYPERLINK_STRUCT); // and remove the static control property where we used to save it
free (hyperlink); // free the hyperlink structure
hyperlink = NULL;
}
return (DefWindowProc (hWnd, message, wParam, lParam)); // consistency fallback
}
// now call the original static control procedure to let things go normally
return (CallWindowProc (hyperlink->pfnOrigProc, hWnd, message, wParam, lParam));
}