Navigate selection history via log jump up/down button
[TortoiseGit.git] / src / Utils / MiscUI / LinkControl.cpp
blob5f181c274f23d83ea61a55a5bd743b690ebf4172
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009, 2012-2013 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "LinkControl.h"
23 #define PROP_OBJECT_PTR MAKEINTATOM(ga.atom)
24 #define PROP_ORIGINAL_PROC MAKEINTATOM(ga.atom)
26 class CGlobalAtom
28 public:
29 CGlobalAtom(void)
30 { atom = GlobalAddAtom(_T("_LinkControl_Object_Pointer_")
31 _T("\\{62671D58-E5E8-46e0-A818-FD6547EC60B8}")); }
32 ~CGlobalAtom(void)
33 { DeleteAtom(atom); }
35 ATOM atom;
38 static CGlobalAtom ga;
40 const UINT CLinkControl::LK_LINKITEMCLICKED
41 = ::RegisterWindowMessage(_T("LK_LINKITEMCLICKED"));
44 HCURSOR CLinkControl::g_hLinkCursor = NULL;
45 HFONT CLinkControl::g_UnderlineFont = NULL;
46 HFONT CLinkControl::g_NormalFont = NULL;
47 int CLinkControl::g_counter = 0;
50 CLinkControl::CLinkControl(void)
51 : m_bOverControl(false)
52 , m_bMouseDownPressed(false)
53 , m_StdFont(NULL)
54 , m_pfnOrigCtlProc(NULL)
58 CLinkControl::~CLinkControl(void)
62 bool CLinkControl::ConvertStaticToLink(HWND hwndCtl)
64 // Subclass the parent so we can draw the controls ourselves
65 HWND hwndParent = GetParent(hwndCtl);
66 if (hwndParent)
68 WNDPROC pfnOrigProc = (WNDPROC) GetWindowLongPtr(hwndParent, GWLP_WNDPROC);
69 if (pfnOrigProc != _HyperlinkParentProc)
71 if (SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE)pfnOrigProc))
72 SetWindowLongPtr(hwndParent, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)_HyperlinkParentProc);
76 // Make sure the control will send notifications.
78 LONG_PTR Style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
79 SetWindowLongPtr(hwndCtl, GWL_STYLE, Style | SS_NOTIFY);
81 // Create an updated font by adding an underline.
83 m_StdFont = (HFONT) SendMessage(hwndCtl, WM_GETFONT, 0, 0);
85 if (g_counter++ == 0)
87 createGlobalResources();
89 SendMessage(hwndCtl, WM_SETFONT, (WPARAM)CLinkControl::g_NormalFont, FALSE);
91 // Subclass the existing control.
93 m_pfnOrigCtlProc = (WNDPROC)GetWindowLongPtr(hwndCtl, GWLP_WNDPROC);
94 if (SetProp(hwndCtl, PROP_OBJECT_PTR, (HANDLE)this))
95 SetWindowLongPtr(hwndCtl, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)_HyperlinkProc);
97 return true;
100 bool CLinkControl::ConvertStaticToLink(HWND hwndParent, UINT uiCtlId)
102 return ConvertStaticToLink(GetDlgItem(hwndParent, uiCtlId));
105 LRESULT CALLBACK CLinkControl::_HyperlinkParentProc(HWND hwnd, UINT message,
106 WPARAM wParam, LPARAM lParam)
108 WNDPROC pfnOrigProc = (WNDPROC)GetProp(hwnd, PROP_ORIGINAL_PROC);
110 switch (message)
112 case WM_CTLCOLORSTATIC:
114 //HDC hdc = (HDC)wParam;
115 HWND hwndCtl = (HWND)lParam;
116 CLinkControl *pHyperLink = (CLinkControl*)GetProp(hwndCtl, PROP_OBJECT_PTR);
118 if (pHyperLink)
120 LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
121 //::SetTextColor(hdc, GetSysColor(COLOR_HOTLIGHT));
122 return lr;
125 break;
126 case WM_DESTROY:
128 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pfnOrigProc);
129 RemoveProp(hwnd, PROP_ORIGINAL_PROC);
131 break;
133 return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
136 void CLinkControl::DrawFocusRect(HWND hwnd)
138 HWND hwndParent = ::GetParent(hwnd);
140 if (hwndParent)
142 // calculate where to draw focus rectangle, in screen coords
143 RECT rc;
144 GetWindowRect(hwnd, &rc);
146 InflateRect(&rc, 1, 1); // add one pixel all around
147 ::ScreenToClient(hwndParent, (LPPOINT)&rc);
148 ::ScreenToClient(hwndParent, ((LPPOINT)&rc) + 1);
149 HDC dcParent = GetDC(hwndParent);
150 ::DrawFocusRect(dcParent, &rc);
151 ReleaseDC(hwndParent, dcParent);
155 LRESULT CALLBACK CLinkControl::_HyperlinkProc(HWND hwnd, UINT message,
156 WPARAM wParam, LPARAM lParam)
158 CLinkControl *pHyperLink = (CLinkControl *)GetProp(hwnd, PROP_OBJECT_PTR);
160 switch (message)
162 case WM_MOUSEMOVE:
164 if (pHyperLink->m_bOverControl)
166 RECT rect;
167 GetClientRect(hwnd, &rect);
169 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
171 if (!PtInRect(&rect, pt))
172 ReleaseCapture();
174 else
176 pHyperLink->m_bOverControl = TRUE;
177 SendMessage(hwnd, WM_SETFONT, (WPARAM)CLinkControl::g_UnderlineFont, FALSE);
178 InvalidateRect(hwnd, NULL, FALSE);
179 SetCapture(hwnd);
182 break;
183 case WM_SETCURSOR:
185 SetCursor(CLinkControl::g_hLinkCursor);
186 return TRUE;
188 break;
189 case WM_CAPTURECHANGED:
191 pHyperLink->m_bOverControl = FALSE;
192 SendMessage(hwnd, WM_SETFONT, (WPARAM)CLinkControl::g_NormalFont, FALSE);
193 InvalidateRect(hwnd, NULL, FALSE);
195 break;
196 case WM_KEYDOWN:
198 if ((wParam != VK_SPACE)&&(wParam != VK_RETURN))
199 break;
200 PostMessage(::GetParent(hwnd), LK_LINKITEMCLICKED, (WPARAM)hwnd, (LPARAM)0);
201 return 0;
203 break;
204 case BM_CLICK:
205 PostMessage(::GetParent(hwnd), LK_LINKITEMCLICKED, (WPARAM)hwnd, (LPARAM)0);
206 break;
207 case WM_LBUTTONDOWN:
208 pHyperLink->m_bMouseDownPressed = true;
209 // fallthrough
210 case WM_LBUTTONUP:
212 if (pHyperLink->m_bMouseDownPressed)
213 PostMessage(::GetParent(hwnd), LK_LINKITEMCLICKED, (WPARAM)hwnd, (LPARAM)0);
214 pHyperLink->m_bMouseDownPressed = false;
216 break;
217 case WM_GETDLGCODE:
219 LRESULT lres = CallWindowProc(pHyperLink->m_pfnOrigCtlProc, hwnd, message, wParam, lParam);
220 // we want all keys to get the return key
221 lres |= DLGC_WANTALLKEYS;
222 lres |= DLGC_BUTTON;
223 // but we don't want the tab key since that should be used in dialogs
224 // to switch the focus
225 lres &= ~DLGC_WANTTAB;
226 lres &= ~DLGC_STATIC;
227 if (lParam &&
228 ((MSG *)lParam)->message == WM_KEYDOWN &&
229 ((MSG *)lParam)->wParam == VK_TAB)
231 lres &= ~DLGC_WANTMESSAGE;
233 return lres;
235 break;
236 case WM_SETFOCUS:
237 // Fall through
238 case WM_KILLFOCUS:
240 CLinkControl::DrawFocusRect(hwnd);
242 break;
243 case WM_DESTROY:
245 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pHyperLink->m_pfnOrigCtlProc);
247 SendMessage(hwnd, WM_SETFONT, (WPARAM)pHyperLink->m_StdFont, 0);
249 if (--CLinkControl::g_counter <= 0)
250 destroyGlobalResources();
252 RemoveProp(hwnd, PROP_OBJECT_PTR);
254 break;
257 return CallWindowProc(pHyperLink->m_pfnOrigCtlProc, hwnd, message,
258 wParam, lParam);
261 void CLinkControl::createUnderlineFont(void)
263 LOGFONT lf;
264 GetObject(m_StdFont, sizeof(lf), &lf);
265 lf.lfWeight = FW_BOLD;
266 g_NormalFont = CreateFontIndirect(&lf);
268 lf.lfUnderline = TRUE;
269 g_UnderlineFont = CreateFontIndirect(&lf);
272 void CLinkControl::createLinkCursor(void)
274 g_hLinkCursor = ::LoadCursor(NULL, IDC_HAND); // Load Windows' hand cursor
275 if (!g_hLinkCursor) // if not available, use the standard Arrow cursor
277 g_hLinkCursor = ::LoadCursor(NULL, IDC_ARROW);