Sync TortoiseIDiff and TortoiseUDiff from TortoiseSVN
[TortoiseGit.git] / src / Utils / MiscUI / hyperlink_base.cpp
blobb71bf959ad638b769714f5f13b7bf35bb1301b91
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, (LONG_PTR)(WNDPROC) _HyperlinkParentProc );
122 // Make sure the control will send notifications.
124 LONG_PTR Style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
125 SetWindowLongPtr(hwndCtl, GWL_STYLE, Style | SS_NOTIFY);
127 // Create an updated font by adding an underline.
129 m_StdFont = (HFONT) SendMessage(hwndCtl, WM_GETFONT, 0, 0);
131 if( g_counter++ == 0 )
133 createGlobalResources();
136 // Subclass the existing control.
138 m_pfnOrigCtlProc = (WNDPROC) GetWindowLongPtr(hwndCtl, GWLP_WNDPROC);
139 SetProp(hwndCtl, PROP_OBJECT_PTR, (HANDLE) this);
140 SetWindowLongPtr(hwndCtl, GWLP_WNDPROC, (LONG_PTR)(WNDPROC) _HyperlinkProc);
142 return TRUE;
146 * Function CHyperLink::ConvertStaticToHyperlink
148 BOOL CHyperLink::ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId,
149 LPCTSTR strURL)
151 return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId), strURL);
155 * Function CHyperLink::setURL
157 BOOL CHyperLink::setURL(LPCTSTR strURL)
159 if( m_strURL )
161 delete [] m_strURL;
163 if( (m_strURL = new TCHAR[lstrlen(strURL)+1])==0 )
165 return FALSE;
168 lstrcpy(m_strURL, strURL);
170 return TRUE;
173 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
174 * Private functions
178 * Function CHyperLink::_HyperlinkParentProc
180 LRESULT CALLBACK CHyperLink::_HyperlinkParentProc(HWND hwnd, UINT message,
181 WPARAM wParam, LPARAM lParam)
183 WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
185 switch (message)
187 case WM_CTLCOLORSTATIC:
189 HDC hdc = (HDC) wParam;
190 HWND hwndCtl = (HWND) lParam;
191 CHyperLink *pHyperLink = (CHyperLink *)GetProp(hwndCtl,
192 PROP_OBJECT_PTR);
194 if(pHyperLink)
196 LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message,
197 wParam, lParam);
198 if (!pHyperLink->m_bVisited)
200 // This is the most common case for static branch prediction
201 // optimization
202 SetTextColor(hdc, CHyperLink::g_crLinkColor);
204 else
206 SetTextColor(hdc, CHyperLink::g_crVisitedColor);
208 return lr;
210 break;
212 case WM_DESTROY:
214 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pfnOrigProc);
215 RemoveProp(hwnd, PROP_ORIGINAL_PROC);
216 break;
219 return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
223 * Function CHyperLink::Navigate
225 inline void CHyperLink::Navigate(void)
227 SHELLEXECUTEINFO sei;
228 ::SecureZeroMemory(&sei,sizeof(SHELLEXECUTEINFO));
229 sei.cbSize = sizeof( SHELLEXECUTEINFO ); // Set Size
230 sei.lpVerb = TEXT( "open" ); // Set Verb
231 sei.lpFile = m_strURL; // Set Target To Open
232 sei.nShow = SW_SHOWNORMAL; // Show Normal
234 LASTERRORDISPLAYR(ShellExecuteEx(&sei));
235 m_bVisited = TRUE;
239 * Function CHyperLink::DrawFocusRect
241 inline void CHyperLink::DrawFocusRect(HWND hwnd)
243 HWND hwndParent = ::GetParent(hwnd);
245 if( hwndParent )
247 // calculate where to draw focus rectangle, in screen coords
248 RECT rc;
249 GetWindowRect(hwnd, &rc);
251 INFLATERECT(&rc,1,1); // add one pixel all around
252 // convert to parent window client coords
253 ::ScreenToClient(hwndParent, (LPPOINT)&rc);
254 ::ScreenToClient(hwndParent, ((LPPOINT)&rc)+1);
255 HDC dcParent = GetDC(hwndParent); // parent window's DC
256 ::DrawFocusRect(dcParent, &rc); // draw it!
257 ReleaseDC(hwndParent,dcParent);
262 * Function CHyperLink::_HyperlinkProc
264 * Note: Processed messages are not passed back to the static control
265 * procedure. It does work fine but be aware that it could cause
266 * some problems if the static control is already subclassed.
267 * Consider the example where the static control would be already
268 * subclassed with the ToolTip control that needs to process mouse
269 * messages. In that situation, the ToolTip control would not work
270 * as expected.
272 LRESULT CALLBACK CHyperLink::_HyperlinkProc(HWND hwnd, UINT message,
273 WPARAM wParam, LPARAM lParam)
275 CHyperLink *pHyperLink = (CHyperLink *)GetProp(hwnd, PROP_OBJECT_PTR);
277 switch (message)
279 case WM_MOUSEMOVE:
281 if ( pHyperLink->m_bOverControl )
283 // This is the most common case for static branch prediction
284 // optimization
285 RECT rect;
286 GetClientRect(hwnd,&rect);
288 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
290 if (!PTINRECT(&rect,pt))
292 ReleaseCapture();
295 else
297 pHyperLink->m_bOverControl = TRUE;
298 SendMessage(hwnd, WM_SETFONT,
299 (WPARAM)CHyperLink::g_UnderlineFont, FALSE);
300 InvalidateRect(hwnd, NULL, FALSE);
301 pHyperLink->OnSelect();
302 SetCapture(hwnd);
304 return 0;
306 case WM_SETCURSOR:
308 SetCursor(CHyperLink::g_hLinkCursor);
309 return TRUE;
311 case WM_CAPTURECHANGED:
313 pHyperLink->m_bOverControl = FALSE;
314 pHyperLink->OnDeselect();
315 SendMessage(hwnd, WM_SETFONT,
316 (WPARAM)pHyperLink->m_StdFont, FALSE);
317 InvalidateRect(hwnd, NULL, FALSE);
318 return 0;
320 case WM_KEYUP:
322 if( wParam != VK_SPACE )
324 break;
327 // Fall through
328 case WM_LBUTTONUP:
330 pHyperLink->Navigate();
331 return 0;
333 case WM_SETFOCUS: // Fall through
334 case WM_KILLFOCUS:
336 if( message == WM_SETFOCUS )
338 pHyperLink->OnSelect();
340 else // WM_KILLFOCUS
342 pHyperLink->OnDeselect();
344 CHyperLink::DrawFocusRect(hwnd);
345 return 0;
347 case WM_DESTROY:
349 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pHyperLink->m_pfnOrigCtlProc);
351 SendMessage(hwnd, WM_SETFONT, (WPARAM) pHyperLink->m_StdFont, 0);
353 if( --CHyperLink::g_counter <= 0 )
355 destroyGlobalResources();
358 RemoveProp(hwnd, PROP_OBJECT_PTR);
359 break;
363 return CallWindowProc(pHyperLink->m_pfnOrigCtlProc, hwnd, message,
364 wParam, lParam);
368 * Function CHyperLink::createUnderlineFont
370 void CHyperLink::createUnderlineFont(void)
372 LOGFONT lf;
373 GetObject(m_StdFont, sizeof(lf), &lf);
374 lf.lfUnderline = TRUE;
376 g_UnderlineFont = CreateFontIndirect(&lf);
380 * Function CHyperLink::createLinkCursor
382 void CHyperLink::createLinkCursor(void)
384 g_hLinkCursor = ::LoadCursor(NULL, IDC_HAND); // Load Windows' hand cursor
385 if( !g_hLinkCursor ) // if not available, use the standard Arrow cursor
388 * There exist an alternative way to get the IDC_HAND by loading winhlp32.exe but I
389 * estimated that it didn't worth the trouble as IDC_HAND is supported since Win98.
390 * I consider that if a user is happy with 10 years old OS, he won't bother to have
391 * an arrow cursor.
393 g_hLinkCursor = ::LoadCursor(NULL, IDC_ARROW);