Fix typos
[TortoiseGit.git] / src / Utils / MiscUI / hyperlink_base.cpp
blob8c698beac79634d92ecaad7229a2efbc35f60dd2
1 /*
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
14 * Revision :
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
21 #include "stdafx.h"
22 #include "hyperlink_base.h"
23 #include <shellapi.h>
26 * If you do not wish to link with the windebug module,
27 * comment out the next include directive and uncomment the
28 * define directive.
30 //#include "win/windebug.h"
31 #define LASTERRORDISPLAYR(a) (a)
34 * Defines
36 #ifndef IDC_HAND
37 #define IDC_HAND MAKEINTRESOURCE(32649)
38 #endif
40 #define PROP_OBJECT_PTR MAKEINTATOM(ga.atom)
41 #define PROP_ORIGINAL_PROC MAKEINTATOM(ga.atom)
44 * typedefs
46 class CGlobalAtom
48 public:
49 CGlobalAtom()
50 { atom = GlobalAddAtom(TEXT("_Hyperlink_Object_Pointer_")
51 TEXT("\\{AFEED740-CC6D-47c5-831D-9848FD916EEF}")); }
52 ~CGlobalAtom()
53 { DeleteAtom(atom); }
55 ATOM atom;
59 * Local variables
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 /////////////////////////////////////////////////////////////////////////////
81 // CHyperLink
83 CHyperLink::CHyperLink()
87 CHyperLink::~CHyperLink()
91 /*-----------------------------------------------------------------------------
92 * Public functions
96 * Function CHyperLink::ConvertStaticToHyperlink
98 BOOL CHyperLink::ConvertStaticToHyperlink(HWND hwndCtl, LPCWSTR strURL)
100 if( !(setURL(strURL)) )
101 return FALSE;
103 // Subclass the parent so we can color the controls as we desire.
105 HWND hwndParent = GetParent(hwndCtl);
106 if (hwndParent)
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)));
136 return TRUE;
140 * Function CHyperLink::ConvertStaticToHyperlink
142 BOOL CHyperLink::ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId,
143 LPCWSTR strURL)
145 return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId), strURL);
149 * Function CHyperLink::setURL
151 BOOL CHyperLink::setURL(LPCWSTR strURL)
153 m_strURL = strURL;
155 return TRUE;
158 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
159 * Private functions
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));
170 switch (message)
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));
178 if(pHyperLink)
180 LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message,
181 wParam, lParam);
182 if (!pHyperLink->m_bVisited)
184 // This is the most common case for static branch prediction
185 // optimization
186 SetTextColor(hdc, CHyperLink::g_crLinkColor);
188 else
190 SetTextColor(hdc, CHyperLink::g_crVisitedColor);
192 return lr;
194 break;
196 case WM_DESTROY:
198 SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(pfnOrigProc));
199 RemoveProp(hwnd, PROP_ORIGINAL_PROC);
200 break;
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));
218 m_bVisited = TRUE;
222 * Function CHyperLink::DrawFocusRect
224 inline void CHyperLink::DrawFocusRect(HWND hwnd)
226 HWND hwndParent = ::GetParent(hwnd);
228 if( hwndParent )
230 // calculate where to draw focus rectangle, in screen coords
231 RECT rc;
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
253 * as expected.
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));
260 switch (message)
262 case WM_MOUSEMOVE:
264 if ( pHyperLink->m_bOverControl )
266 // This is the most common case for static branch prediction
267 // optimization
268 RECT rect;
269 GetClientRect(hwnd,&rect);
271 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
273 if (!PTINRECT(&rect,pt))
275 ReleaseCapture();
278 else
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();
284 SetCapture(hwnd);
286 return 0;
288 case WM_SETCURSOR:
290 SetCursor(CHyperLink::g_hLinkCursor);
291 return TRUE;
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);
299 return 0;
301 case WM_KEYUP:
303 if( wParam != VK_SPACE )
305 break;
308 [[fallthrough]];
309 case WM_LBUTTONUP:
311 pHyperLink->Navigate();
312 return 0;
314 case WM_SETFOCUS:
315 [[fallthrough]];
316 case WM_KILLFOCUS:
318 if( message == WM_SETFOCUS )
320 pHyperLink->OnSelect();
322 else // WM_KILLFOCUS
324 pHyperLink->OnDeselect();
326 CHyperLink::DrawFocusRect(hwnd);
327 return 0;
329 case WM_DESTROY:
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);
341 break;
345 return CallWindowProc(pHyperLink->m_pfnOrigCtlProc, hwnd, message,
346 wParam, lParam);
350 * Function CHyperLink::createUnderlineFont
352 void CHyperLink::createUnderlineFont()
354 LOGFONT lf;
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
373 * an arrow cursor.
375 g_hLinkCursor = ::LoadCursor(nullptr, IDC_ARROW);