// 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));
 
}