High DPI optimizations
[TortoiseGit.git] / src / Utils / MiscUI / HyperLink.cpp
blob20641f6f9ef64b14db87295c397a342441f62925
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2016 - TortoiseGit
4 // Copyright (C) 2003-2006,2008, 2011, 2018 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "HyperLink.h"
22 #include "SmartHandle.h"
24 #ifdef _DEBUG
25 #define new DEBUG_NEW
26 #undef THIS_FILE
27 static char THIS_FILE[] = __FILE__;
28 #endif
30 #define TOOLTIP_ID 1
33 CHyperLink::CHyperLink()
34 : m_hLinkCursor(nullptr)
35 , m_crLinkColor(GetSysColor(COLOR_HOTLIGHT))
36 , m_crHoverColor(RGB(255, 0, 0)) // Red
37 , m_bOverControl(FALSE)
38 , m_nUnderline(ulHover)
39 , m_nTimerID(100)
43 CHyperLink::~CHyperLink()
45 m_UnderlineFont.DeleteObject();
48 BOOL CHyperLink::DestroyWindow()
50 KillTimer(m_nTimerID);
52 return CStatic::DestroyWindow();
55 BOOL CHyperLink::PreTranslateMessage(MSG* pMsg)
57 m_ToolTip.RelayEvent(pMsg);
58 return CStatic::PreTranslateMessage(pMsg);
62 void CHyperLink::PreSubclassWindow()
64 // Enable notifications - CStatic has this disabled by default
65 ModifyStyle(0, SS_NOTIFY);
67 // By default use the label text as the URL
68 if (m_strURL.IsEmpty())
69 GetWindowText(m_strURL);
71 CString strWndText;
72 GetWindowText(strWndText);
73 if (strWndText.IsEmpty())
75 SetWindowText(m_strURL);
78 LOGFONT lf;
79 CFont* pFont = GetFont();
80 if (pFont)
81 pFont->GetObject(sizeof(lf), &lf);
82 else
84 NONCLIENTMETRICS metrics = { 0 };
85 metrics.cbSize = sizeof(NONCLIENTMETRICS);
86 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
87 memcpy_s(&lf, sizeof(LOGFONT), &metrics.lfMessageFont, sizeof(LOGFONT));
89 m_StdFont.CreateFontIndirect(&lf);
90 lf.lfUnderline = (BYTE) TRUE;
91 m_UnderlineFont.CreateFontIndirect(&lf);
93 SetDefaultCursor(); // try loading a "hand" cursor
94 SetUnderline();
96 CRect rect;
97 GetClientRect(rect);
98 m_ToolTip.Create(this);
99 m_ToolTip.AddTool(this, m_strURL, rect, TOOLTIP_ID);
101 CStatic::PreSubclassWindow();
104 BEGIN_MESSAGE_MAP(CHyperLink, CStatic)
105 ON_WM_CTLCOLOR_REFLECT()
106 ON_WM_SETCURSOR()
107 ON_WM_MOUSEMOVE()
108 ON_WM_TIMER()
109 ON_WM_ERASEBKGND()
110 ON_CONTROL_REFLECT(STN_CLICKED, OnClicked)
111 ON_WM_SYSCOLORCHANGE()
112 END_MESSAGE_MAP()
115 void CHyperLink::OnClicked()
117 if(!m_strURL.IsEmpty())
119 GotoURL(m_strURL);
121 else
123 ::SendMessage(this->GetParent()->m_hWnd,WM_COMMAND,this->GetDlgCtrlID(),0);
127 HBRUSH CHyperLink::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
129 if (m_bOverControl)
130 pDC->SetTextColor(m_crHoverColor);
131 else
132 pDC->SetTextColor(m_crLinkColor);
134 // draw transparent
135 pDC->SetBkMode(TRANSPARENT);
136 return (HBRUSH)GetStockObject(NULL_BRUSH);
139 void CHyperLink::OnMouseMove(UINT nFlags, CPoint point)
141 if (!m_bOverControl)
143 m_bOverControl = TRUE;
145 if (m_nUnderline == ulHover)
146 SetFont(&m_UnderlineFont);
147 Invalidate();
149 SetTimer(m_nTimerID, 100, nullptr);
151 CStatic::OnMouseMove(nFlags, point);
154 void CHyperLink::OnTimer(UINT_PTR nIDEvent)
156 CPoint p(GetMessagePos());
157 ScreenToClient(&p);
159 CRect rect;
160 GetClientRect(rect);
161 if (!rect.PtInRect(p))
163 m_bOverControl = FALSE;
164 KillTimer(m_nTimerID);
166 if (m_nUnderline != ulAlways)
167 SetFont(&m_StdFont);
168 rect.bottom+=10;
169 InvalidateRect(rect);
172 CStatic::OnTimer(nIDEvent);
175 BOOL CHyperLink::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/)
177 if (m_hLinkCursor)
179 ::SetCursor(m_hLinkCursor);
180 return TRUE;
182 return FALSE;
185 BOOL CHyperLink::OnEraseBkgnd(CDC* pDC)
187 CRect rect;
188 GetClientRect(rect);
189 pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
191 return TRUE;
194 void CHyperLink::SetURL(CString strURL)
196 m_strURL = strURL;
198 if (::IsWindow(GetSafeHwnd()))
200 m_ToolTip.UpdateTipText(strURL, this, TOOLTIP_ID);
204 CString CHyperLink::GetURL() const
206 return m_strURL;
209 void CHyperLink::SetColors(COLORREF crLinkColor, COLORREF crHoverColor)
211 m_crLinkColor = crLinkColor;
213 if (crHoverColor == -1)
214 m_crHoverColor = ::GetSysColor(COLOR_HIGHLIGHT);
215 else
216 m_crHoverColor = crHoverColor;
218 if (::IsWindow(m_hWnd))
219 Invalidate();
222 COLORREF CHyperLink::GetLinkColor() const
224 return m_crLinkColor;
227 COLORREF CHyperLink::GetHoverColor() const
229 return m_crHoverColor;
232 void CHyperLink::SetUnderline(int nUnderline /*=ulHover*/)
234 if (m_nUnderline == nUnderline)
235 return;
237 if (::IsWindow(GetSafeHwnd()))
239 if (nUnderline == ulAlways)
240 SetFont(&m_UnderlineFont);
241 else
242 SetFont(&m_StdFont);
244 Invalidate();
247 m_nUnderline = nUnderline;
250 int CHyperLink::GetUnderline() const
252 return m_nUnderline;
255 // The following appeared in Paul DiLascia's Jan 1998 MSJ articles.
256 // It loads a "hand" cursor from the winhlp32.exe module
257 void CHyperLink::SetDefaultCursor()
259 if (!m_hLinkCursor)
261 // first try the windows hand cursor (not available on NT4)
262 #ifndef OCR_HAND
263 # define OCR_HAND 32649
264 #endif
265 HCURSOR hHandCursor = (HCURSOR)::LoadImage(nullptr, MAKEINTRESOURCE(OCR_HAND), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
266 if (hHandCursor)
268 m_hLinkCursor = hHandCursor;
269 return;
271 // windows cursor not available, so try to load it from winhlp32.exe
272 CString strWndDir;
273 GetWindowsDirectory(CStrBuf(strWndDir, MAX_PATH), MAX_PATH); // Explorer can't handle paths longer than MAX_PATH.
274 strWndDir += L"\\winhlp32.exe";
275 // This retrieves cursor #106 from winhlp32.exe, which is a hand pointer
276 CAutoLibrary hModule = LoadLibrary(strWndDir);
277 if (hModule) {
278 HCURSOR hHandCursor2 = (HCURSOR)::LoadImage(hModule, MAKEINTRESOURCE(106), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
279 if (hHandCursor2)
280 m_hLinkCursor = CopyCursor(hHandCursor2);
285 HINSTANCE CHyperLink::GotoURL(LPCTSTR url)
287 return ShellExecute(nullptr, L"open", url, nullptr, nullptr, SW_SHOW);
290 void CHyperLink::OnSysColorChange()
292 __super::OnSysColorChange();
293 m_crLinkColor = GetSysColor(COLOR_HOTLIGHT);
294 Invalidate();