Cleanup
[TortoiseGit.git] / src / Utils / MiscUI / hyperlink_base.cpp
blob9bd03c37fd91b2837b934feda38132abc2016e1c
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(void)
50 { atom = GlobalAddAtom(TEXT("_Hyperlink_Object_Pointer_")
51 TEXT("\\{AFEED740-CC6D-47c5-831D-9848FD916EEF}")); }
52 ~CGlobalAtom(void)
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 = 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 /////////////////////////////////////////////////////////////////////////////
81 // CHyperLink
83 CHyperLink::CHyperLink(void)
85 m_bOverControl = FALSE; // Cursor not yet over control
86 m_bVisited = FALSE; // Hasn't been visited yet.
87 m_StdFont = NULL;
88 m_pfnOrigCtlProc = NULL;
89 m_strURL = NULL;
92 CHyperLink::~CHyperLink(void)
94 delete [] m_strURL;
97 /*-----------------------------------------------------------------------------
98 * Public functions
102 * Function CHyperLink::ConvertStaticToHyperlink
104 BOOL CHyperLink::ConvertStaticToHyperlink(HWND hwndCtl, LPCTSTR strURL)
106 if( !(setURL(strURL)) )
107 return FALSE;
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);
143 return TRUE;
147 * Function CHyperLink::ConvertStaticToHyperlink
149 BOOL CHyperLink::ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId,
150 LPCTSTR strURL)
152 return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId), strURL);
156 * Function CHyperLink::setURL
158 BOOL CHyperLink::setURL(LPCTSTR strURL)
160 if( m_strURL )
162 delete [] m_strURL;
164 if( (m_strURL = new TCHAR[lstrlen(strURL)+1])==0 )
166 return FALSE;
169 lstrcpy(m_strURL, strURL);
171 return TRUE;
174 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
175 * Private functions
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);
186 switch (message)
188 case WM_CTLCOLORSTATIC:
190 HDC hdc = (HDC) wParam;
191 HWND hwndCtl = (HWND) lParam;
192 CHyperLink *pHyperLink = (CHyperLink *)GetProp(hwndCtl,
193 PROP_OBJECT_PTR);
195 if(pHyperLink)
197 LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message,
198 wParam, lParam);
199 if (!pHyperLink->m_bVisited)
201 // This is the most common case for static branch prediction
202 // optimization
203 SetTextColor(hdc, CHyperLink::g_crLinkColor);
205 else
207 SetTextColor(hdc, CHyperLink::g_crVisitedColor);
209 return lr;
211 break;
213 case WM_DESTROY:
215 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG) pfnOrigProc);
216 RemoveProp(hwnd, PROP_ORIGINAL_PROC);
217 break;
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));
236 m_bVisited = TRUE;
240 * Function CHyperLink::DrawFocusRect
242 inline void CHyperLink::DrawFocusRect(HWND hwnd)
244 HWND hwndParent = ::GetParent(hwnd);
246 if( hwndParent )
248 // calculate where to draw focus rectangle, in screen coords
249 RECT rc;
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
271 * as expected.
273 LRESULT CALLBACK CHyperLink::_HyperlinkProc(HWND hwnd, UINT message,
274 WPARAM wParam, LPARAM lParam)
276 CHyperLink *pHyperLink = (CHyperLink *)GetProp(hwnd, PROP_OBJECT_PTR);
278 switch (message)
280 case WM_MOUSEMOVE:
282 if ( pHyperLink->m_bOverControl )
284 // This is the most common case for static branch prediction
285 // optimization
286 RECT rect;
287 GetClientRect(hwnd,&rect);
289 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
291 if (!PTINRECT(&rect,pt))
293 ReleaseCapture();
296 else
298 pHyperLink->m_bOverControl = TRUE;
299 SendMessage(hwnd, WM_SETFONT,
300 (WPARAM)CHyperLink::g_UnderlineFont, FALSE);
301 InvalidateRect(hwnd, NULL, FALSE);
302 pHyperLink->OnSelect();
303 SetCapture(hwnd);
305 return 0;
307 case WM_SETCURSOR:
309 SetCursor(CHyperLink::g_hLinkCursor);
310 return TRUE;
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);
319 return 0;
321 case WM_KEYUP:
323 if( wParam != VK_SPACE )
325 break;
328 // Fall through
329 case WM_LBUTTONUP:
331 pHyperLink->Navigate();
332 return 0;
334 case WM_SETFOCUS: // Fall through
335 case WM_KILLFOCUS:
337 if( message == WM_SETFOCUS )
339 pHyperLink->OnSelect();
341 else // WM_KILLFOCUS
343 pHyperLink->OnDeselect();
345 CHyperLink::DrawFocusRect(hwnd);
346 return 0;
348 case WM_DESTROY:
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);
361 break;
365 return CallWindowProc(pHyperLink->m_pfnOrigCtlProc, hwnd, message,
366 wParam, lParam);
370 * Function CHyperLink::createUnderlineFont
372 void CHyperLink::createUnderlineFont(void)
374 LOGFONT lf;
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
393 * an arrow cursor.
395 g_hLinkCursor = ::LoadCursor(NULL, IDC_ARROW);