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
= nullptr;
69 HFONT
CHyperLink::g_UnderlineFont
= nullptr;
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()
87 CHyperLink::~CHyperLink()
91 /*-----------------------------------------------------------------------------
96 * Function CHyperLink::ConvertStaticToHyperlink
98 BOOL
CHyperLink::ConvertStaticToHyperlink(HWND hwndCtl
, LPCWSTR strURL
)
100 if( !(setURL(strURL
)) )
103 // Subclass the parent so we can color the controls as we desire.
105 HWND hwndParent
= GetParent(hwndCtl
);
108 auto pfnOrigProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hwndParent
, GWLP_WNDPROC
));
109 if (pfnOrigProc
!= _HyperlinkParentProc
)
111 SetProp(hwndParent
, PROP_ORIGINAL_PROC
, static_cast<HANDLE
>(pfnOrigProc
));
112 SetWindowLongPtr(hwndParent
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(static_cast<WNDPROC
>(_HyperlinkParentProc
)));
116 // Make sure the control will send notifications.
118 LONG_PTR Style
= GetWindowLongPtr(hwndCtl
, GWL_STYLE
);
119 SetWindowLongPtr(hwndCtl
, GWL_STYLE
, Style
| SS_NOTIFY
);
121 // Create an updated font by adding an underline.
123 m_StdFont
= reinterpret_cast<HFONT
>(SendMessage(hwndCtl
, WM_GETFONT
, 0, 0));
125 if( g_counter
++ == 0 )
127 createGlobalResources();
130 // Subclass the existing control.
132 m_pfnOrigCtlProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hwndCtl
, GWLP_WNDPROC
));
133 SetProp(hwndCtl
, PROP_OBJECT_PTR
, static_cast<HANDLE
>(this));
134 SetWindowLongPtr(hwndCtl
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(static_cast<WNDPROC
>(_HyperlinkParentProc
)));
140 * Function CHyperLink::ConvertStaticToHyperlink
142 BOOL
CHyperLink::ConvertStaticToHyperlink(HWND hwndParent
, UINT uiCtlId
,
145 return ConvertStaticToHyperlink(GetDlgItem(hwndParent
, uiCtlId
), strURL
);
149 * Function CHyperLink::setURL
151 BOOL
CHyperLink::setURL(LPCWSTR strURL
)
158 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
163 * Function CHyperLink::_HyperlinkParentProc
165 LRESULT CALLBACK
CHyperLink::_HyperlinkParentProc(HWND hwnd
, UINT message
,
166 WPARAM wParam
, LPARAM lParam
)
168 auto pfnOrigProc
= reinterpret_cast<WNDPROC
>(GetProp(hwnd
, PROP_ORIGINAL_PROC
));
172 case WM_CTLCOLORSTATIC
:
174 auto hdc
= reinterpret_cast<HDC
>(wParam
);
175 auto hwndCtl
= reinterpret_cast<HWND
>(lParam
);
176 auto pHyperLink
= reinterpret_cast<CHyperLink
*>(GetProp(hwndCtl
, PROP_OBJECT_PTR
));
180 LRESULT lr
= CallWindowProc(pfnOrigProc
, hwnd
, message
,
182 if (!pHyperLink
->m_bVisited
)
184 // This is the most common case for static branch prediction
186 SetTextColor(hdc
, CHyperLink::g_crLinkColor
);
190 SetTextColor(hdc
, CHyperLink::g_crVisitedColor
);
198 SetWindowLongPtr(hwnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(pfnOrigProc
));
199 RemoveProp(hwnd
, PROP_ORIGINAL_PROC
);
203 return CallWindowProc(pfnOrigProc
, hwnd
, message
, wParam
, lParam
);
207 * Function CHyperLink::Navigate
209 inline void CHyperLink::Navigate()
211 SHELLEXECUTEINFO sei
= { 0 };
212 sei
.cbSize
= sizeof( SHELLEXECUTEINFO
); // Set Size
213 sei
.lpVerb
= TEXT( "open" ); // Set Verb
214 sei
.lpFile
= m_strURL
.GetBuffer(); // Set Target To Open
215 sei
.nShow
= SW_SHOWNORMAL
; // Show Normal
217 LASTERRORDISPLAYR(ShellExecuteEx(&sei
));
222 * Function CHyperLink::DrawFocusRect
224 inline void CHyperLink::DrawFocusRect(HWND hwnd
)
226 HWND hwndParent
= ::GetParent(hwnd
);
230 // calculate where to draw focus rectangle, in screen coords
232 GetWindowRect(hwnd
, &rc
);
234 INFLATERECT(&rc
,1,1); // add one pixel all around
235 // convert to parent window client coords
236 ::ScreenToClient(hwndParent
, reinterpret_cast<LPPOINT
>(&rc
));
237 ::ScreenToClient(hwndParent
, reinterpret_cast<LPPOINT
>(&rc
) + 1);
238 HDC dcParent
= GetDC(hwndParent
); // parent window's DC
239 ::DrawFocusRect(dcParent
, &rc
); // draw it!
240 ReleaseDC(hwndParent
,dcParent
);
245 * Function CHyperLink::_HyperlinkProc
247 * Note: Processed messages are not passed back to the static control
248 * procedure. It does work fine but be aware that it could cause
249 * some problems if the static control is already subclassed.
250 * Consider the example where the static control would be already
251 * subclassed with the ToolTip control that needs to process mouse
252 * messages. In that situation, the ToolTip control would not work
255 LRESULT CALLBACK
CHyperLink::_HyperlinkProc(HWND hwnd
, UINT message
,
256 WPARAM wParam
, LPARAM lParam
)
258 auto pHyperLink
= reinterpret_cast<CHyperLink
*>(GetProp(hwnd
, PROP_OBJECT_PTR
));
264 if ( pHyperLink
->m_bOverControl
)
266 // This is the most common case for static branch prediction
269 GetClientRect(hwnd
,&rect
);
271 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
273 if (!PTINRECT(&rect
,pt
))
280 pHyperLink
->m_bOverControl
= TRUE
;
281 SendMessage(hwnd
, WM_SETFONT
, reinterpret_cast<WPARAM
>(CHyperLink::g_UnderlineFont
), FALSE
);
282 InvalidateRect(hwnd
, nullptr, FALSE
);
283 pHyperLink
->OnSelect();
290 SetCursor(CHyperLink::g_hLinkCursor
);
293 case WM_CAPTURECHANGED
:
295 pHyperLink
->m_bOverControl
= FALSE
;
296 pHyperLink
->OnDeselect();
297 SendMessage(hwnd
, WM_SETFONT
, reinterpret_cast<WPARAM
>(pHyperLink
->m_StdFont
), FALSE
);
298 InvalidateRect(hwnd
, nullptr, FALSE
);
303 if( wParam
!= VK_SPACE
)
311 pHyperLink
->Navigate();
318 if( message
== WM_SETFOCUS
)
320 pHyperLink
->OnSelect();
324 pHyperLink
->OnDeselect();
326 CHyperLink::DrawFocusRect(hwnd
);
331 SetWindowLongPtr(hwnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(pHyperLink
->m_pfnOrigCtlProc
));
333 SendMessage(hwnd
, WM_SETFONT
, reinterpret_cast<WPARAM
>(pHyperLink
->m_StdFont
), 0);
335 if( --CHyperLink::g_counter
<= 0 )
337 destroyGlobalResources();
340 RemoveProp(hwnd
, PROP_OBJECT_PTR
);
345 return CallWindowProc(pHyperLink
->m_pfnOrigCtlProc
, hwnd
, message
,
350 * Function CHyperLink::createUnderlineFont
352 void CHyperLink::createUnderlineFont()
355 GetObject(m_StdFont
, sizeof(lf
), &lf
);
356 lf
.lfUnderline
= TRUE
;
358 g_UnderlineFont
= CreateFontIndirect(&lf
);
362 * Function CHyperLink::createLinkCursor
364 void CHyperLink::createLinkCursor()
366 g_hLinkCursor
= ::LoadCursor(nullptr, IDC_HAND
); // Load Windows' hand cursor
367 if( !g_hLinkCursor
) // if not available, use the standard Arrow cursor
370 * There exist an alternative way to get the IDC_HAND by loading winhlp32.exe but I
371 * estimated that it didn't worth the trouble as IDC_HAND is supported since Win98.
372 * I consider that if a user is happy with 10 years old OS, he won't bother to have
375 g_hLinkCursor
= ::LoadCursor(nullptr, IDC_ARROW
);