2 * Module ID: hyperlink.cpp
3 * Title : CHyperLink definition.
5 * Author : Olivier Langlois <olanglois@sympatico.ca>
6 * Date : November 15, 2005
8 * To read the article describing this class, visit
9 * http://www3.sympatico.ca/olanglois/hyperlinkdemo.htm
11 * Note: Strongly inspired by Neal Stublen code
12 * Minor ideas come from Chris Maunder and Paul DiLascia code
16 * 001 26-Nov-2005 - Olivier Langlois
17 * - Added changes to make CHyperLink compatible with UNICODE
18 * - Use dynamic memory allocation for the URL string
19 * - Use of the MAKEINTATOM macro
22 #include "hyperlink_base.h"
26 * If you do not wish to link with the windebug module,
27 * comment out the next include directive and uncomment the
30 //#include "win/windebug.h"
31 #define LASTERRORDISPLAYR(a) (a)
37 #define IDC_HAND MAKEINTRESOURCE(32649)
40 #define PROP_OBJECT_PTR MAKEINTATOM(ga.atom)
41 #define PROP_ORIGINAL_PROC MAKEINTATOM(ga.atom)
50 { atom
= GlobalAddAtom(TEXT("_Hyperlink_Object_Pointer_")
51 TEXT("\\{AFEED740-CC6D-47c5-831D-9848FD916EEF}")); }
61 static CGlobalAtom ga
;
64 * Static members initialization
66 COLORREF
CHyperLink::g_crLinkColor
= RGB( 0, 0, 255); // Blue;
67 COLORREF
CHyperLink::g_crVisitedColor
= RGB( 128, 0, 128); // Purple;
68 HCURSOR
CHyperLink::g_hLinkCursor
= NULL
;
69 HFONT
CHyperLink::g_UnderlineFont
= NULL
;
70 int CHyperLink::g_counter
= 0;
73 * Macros and inline functions
75 inline bool PTINRECT( LPCRECT r
, const POINT
&pt
)
76 { return ( pt
.x
>= r
->left
&& pt
.x
< r
->right
&& pt
.y
>= r
->top
&& pt
.y
< r
->bottom
); }
77 inline void INFLATERECT( PRECT r
, int dx
, int dy
)
78 { r
->left
-= dx
; r
->right
+= dx
; r
->top
-= dy
; r
->bottom
+= dy
; }
80 /////////////////////////////////////////////////////////////////////////////
83 CHyperLink::CHyperLink(void)
85 m_bOverControl
= FALSE
; // Cursor not yet over control
86 m_bVisited
= FALSE
; // Hasn't been visited yet.
88 m_pfnOrigCtlProc
= NULL
;
92 CHyperLink::~CHyperLink(void)
97 /*-----------------------------------------------------------------------------
102 * Function CHyperLink::ConvertStaticToHyperlink
104 BOOL
CHyperLink::ConvertStaticToHyperlink(HWND hwndCtl
, LPCTSTR strURL
)
106 if( !(setURL(strURL
)) )
109 // Subclass the parent so we can color the controls as we desire.
111 HWND hwndParent
= GetParent(hwndCtl
);
112 if (NULL
!= hwndParent
)
114 WNDPROC pfnOrigProc
= (WNDPROC
) GetWindowLongPtr(hwndParent
, GWLP_WNDPROC
);
115 if (pfnOrigProc
!= _HyperlinkParentProc
)
117 SetProp( hwndParent
, PROP_ORIGINAL_PROC
, (HANDLE
)pfnOrigProc
);
118 SetWindowLongPtr( hwndParent
, GWLP_WNDPROC
,
119 (LONG
) (WNDPROC
) _HyperlinkParentProc
);
123 // Make sure the control will send notifications.
125 LONG_PTR Style
= GetWindowLongPtr(hwndCtl
, GWL_STYLE
);
126 SetWindowLongPtr(hwndCtl
, GWL_STYLE
, Style
| SS_NOTIFY
);
128 // Create an updated font by adding an underline.
130 m_StdFont
= (HFONT
) SendMessage(hwndCtl
, WM_GETFONT
, 0, 0);
132 if( g_counter
++ == 0 )
134 createGlobalResources();
137 // Subclass the existing control.
139 m_pfnOrigCtlProc
= (WNDPROC
) GetWindowLongPtr(hwndCtl
, GWLP_WNDPROC
);
140 SetProp(hwndCtl
, PROP_OBJECT_PTR
, (HANDLE
) this);
141 SetWindowLongPtr(hwndCtl
, GWLP_WNDPROC
, (LONG
) (WNDPROC
) _HyperlinkProc
);
147 * Function CHyperLink::ConvertStaticToHyperlink
149 BOOL
CHyperLink::ConvertStaticToHyperlink(HWND hwndParent
, UINT uiCtlId
,
152 return ConvertStaticToHyperlink(GetDlgItem(hwndParent
, uiCtlId
), strURL
);
156 * Function CHyperLink::setURL
158 BOOL
CHyperLink::setURL(LPCTSTR strURL
)
164 if( (m_strURL
= new TCHAR
[lstrlen(strURL
)+1])==0 )
169 lstrcpy(m_strURL
, strURL
);
174 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
179 * Function CHyperLink::_HyperlinkParentProc
181 LRESULT CALLBACK
CHyperLink::_HyperlinkParentProc(HWND hwnd
, UINT message
,
182 WPARAM wParam
, LPARAM lParam
)
184 WNDPROC pfnOrigProc
= (WNDPROC
) GetProp(hwnd
, PROP_ORIGINAL_PROC
);
188 case WM_CTLCOLORSTATIC
:
190 HDC hdc
= (HDC
) wParam
;
191 HWND hwndCtl
= (HWND
) lParam
;
192 CHyperLink
*pHyperLink
= (CHyperLink
*)GetProp(hwndCtl
,
197 LRESULT lr
= CallWindowProc(pfnOrigProc
, hwnd
, message
,
199 if (!pHyperLink
->m_bVisited
)
201 // This is the most common case for static branch prediction
203 SetTextColor(hdc
, CHyperLink::g_crLinkColor
);
207 SetTextColor(hdc
, CHyperLink::g_crVisitedColor
);
215 SetWindowLongPtr(hwnd
, GWLP_WNDPROC
, (LONG
) pfnOrigProc
);
216 RemoveProp(hwnd
, PROP_ORIGINAL_PROC
);
220 return CallWindowProc(pfnOrigProc
, hwnd
, message
, wParam
, lParam
);
224 * Function CHyperLink::Navigate
226 inline void CHyperLink::Navigate(void)
228 SHELLEXECUTEINFO sei
;
229 ::SecureZeroMemory(&sei
,sizeof(SHELLEXECUTEINFO
));
230 sei
.cbSize
= sizeof( SHELLEXECUTEINFO
); // Set Size
231 sei
.lpVerb
= TEXT( "open" ); // Set Verb
232 sei
.lpFile
= m_strURL
; // Set Target To Open
233 sei
.nShow
= SW_SHOWNORMAL
; // Show Normal
235 LASTERRORDISPLAYR(ShellExecuteEx(&sei
));
240 * Function CHyperLink::DrawFocusRect
242 inline void CHyperLink::DrawFocusRect(HWND hwnd
)
244 HWND hwndParent
= ::GetParent(hwnd
);
248 // calculate where to draw focus rectangle, in screen coords
250 GetWindowRect(hwnd
, &rc
);
252 INFLATERECT(&rc
,1,1); // add one pixel all around
253 // convert to parent window client coords
254 ::ScreenToClient(hwndParent
, (LPPOINT
)&rc
);
255 ::ScreenToClient(hwndParent
, ((LPPOINT
)&rc
)+1);
256 HDC dcParent
= GetDC(hwndParent
); // parent window's DC
257 ::DrawFocusRect(dcParent
, &rc
); // draw it!
258 ReleaseDC(hwndParent
,dcParent
);
263 * Function CHyperLink::_HyperlinkProc
265 * Note: Processed messages are not passed back to the static control
266 * procedure. It does work fine but be aware that it could cause
267 * some problems if the static control is already subclassed.
268 * Consider the example where the static control would be already
269 * subclassed with the ToolTip control that needs to process mouse
270 * messages. In that situation, the ToolTip control would not work
273 LRESULT CALLBACK
CHyperLink::_HyperlinkProc(HWND hwnd
, UINT message
,
274 WPARAM wParam
, LPARAM lParam
)
276 CHyperLink
*pHyperLink
= (CHyperLink
*)GetProp(hwnd
, PROP_OBJECT_PTR
);
282 if ( pHyperLink
->m_bOverControl
)
284 // This is the most common case for static branch prediction
287 GetClientRect(hwnd
,&rect
);
289 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
291 if (!PTINRECT(&rect
,pt
))
298 pHyperLink
->m_bOverControl
= TRUE
;
299 SendMessage(hwnd
, WM_SETFONT
,
300 (WPARAM
)CHyperLink::g_UnderlineFont
, FALSE
);
301 InvalidateRect(hwnd
, NULL
, FALSE
);
302 pHyperLink
->OnSelect();
309 SetCursor(CHyperLink::g_hLinkCursor
);
312 case WM_CAPTURECHANGED
:
314 pHyperLink
->m_bOverControl
= FALSE
;
315 pHyperLink
->OnDeselect();
316 SendMessage(hwnd
, WM_SETFONT
,
317 (WPARAM
)pHyperLink
->m_StdFont
, FALSE
);
318 InvalidateRect(hwnd
, NULL
, FALSE
);
323 if( wParam
!= VK_SPACE
)
331 pHyperLink
->Navigate();
334 case WM_SETFOCUS
: // Fall through
337 if( message
== WM_SETFOCUS
)
339 pHyperLink
->OnSelect();
343 pHyperLink
->OnDeselect();
345 CHyperLink::DrawFocusRect(hwnd
);
350 SetWindowLongPtr(hwnd
, GWLP_WNDPROC
,
351 (LONG
) pHyperLink
->m_pfnOrigCtlProc
);
353 SendMessage(hwnd
, WM_SETFONT
, (WPARAM
) pHyperLink
->m_StdFont
, 0);
355 if( --CHyperLink::g_counter
<= 0 )
357 destroyGlobalResources();
360 RemoveProp(hwnd
, PROP_OBJECT_PTR
);
365 return CallWindowProc(pHyperLink
->m_pfnOrigCtlProc
, hwnd
, message
,
370 * Function CHyperLink::createUnderlineFont
372 void CHyperLink::createUnderlineFont(void)
375 GetObject(m_StdFont
, sizeof(lf
), &lf
);
376 lf
.lfUnderline
= TRUE
;
378 g_UnderlineFont
= CreateFontIndirect(&lf
);
382 * Function CHyperLink::createLinkCursor
384 void CHyperLink::createLinkCursor(void)
386 g_hLinkCursor
= ::LoadCursor(NULL
, IDC_HAND
); // Load Windows' hand cursor
387 if( !g_hLinkCursor
) // if not available, use the standard Arrow cursor
390 * There exist an alternative way to get the IDC_HAND by loading winhlp32.exe but I
391 * estimated that it didn't worth the trouble as IDC_HAND is supported since Win98.
392 * I consider that if a user is happy with 10 years old OS, he won't bother to have
395 g_hLinkCursor
= ::LoadCursor(NULL
, IDC_ARROW
);