High DPI optimizations
[TortoiseGit.git] / src / Utils / MiscUI / LinkControl.cpp
blob9f70d4a73b3c87308dd26966ca096adb37e89fc2
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009, 2012-2016, 2018 - 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"
21 #include "CommonAppUtils.h"
23 const UINT CLinkControl::LK_LINKITEMCLICKED = ::RegisterWindowMessage(L"LK_LINKITEMCLICKED");
25 CLinkControl::CLinkControl(void)
26 : m_bOverControl(false)
27 , m_hLinkCursor(nullptr)
31 CLinkControl::~CLinkControl(void)
34 * No need to call DestroyCursor() for cursors acquired through
35 * LoadCursor().
37 m_NormalFont.DeleteObject();
38 m_UnderlineFont.DeleteObject();
41 void CLinkControl::PreSubclassWindow()
43 CStatic::PreSubclassWindow();
45 ModifyStyle(0, SS_NOTIFY);
47 m_hLinkCursor = ::LoadCursor(nullptr, IDC_HAND); // Load Windows' hand cursor
48 if (!m_hLinkCursor) // if not available, use the standard Arrow cursor
50 m_hLinkCursor = ::LoadCursor(nullptr, IDC_ARROW);
53 // Create an updated font by adding an underline.
54 LOGFONT lf;
55 CFont* pFont = GetFont();
56 if (pFont)
57 pFont->GetObject(sizeof(lf), &lf);
58 else
60 NONCLIENTMETRICS metrics = { 0 };
61 metrics.cbSize = sizeof(NONCLIENTMETRICS);
62 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
63 memcpy_s(&lf, sizeof(LOGFONT), &metrics.lfMessageFont, sizeof(LOGFONT));
66 lf.lfWeight = FW_BOLD;
67 m_NormalFont.CreateFontIndirect(&lf);
69 lf.lfUnderline = TRUE;
70 m_UnderlineFont.CreateFontIndirect(&lf);
72 SetFont(&m_NormalFont, FALSE);
75 BEGIN_MESSAGE_MAP(CLinkControl, CStatic)
76 ON_WM_SETCURSOR()
77 ON_WM_MOUSEMOVE()
78 ON_WM_CAPTURECHANGED()
79 ON_WM_ERASEBKGND()
80 ON_WM_SETFOCUS()
81 ON_WM_KILLFOCUS()
82 ON_WM_GETDLGCODE()
83 ON_WM_KEYDOWN()
84 ON_CONTROL_REFLECT(STN_CLICKED, OnClicked)
85 ON_WM_ENABLE()
86 END_MESSAGE_MAP()
88 void CLinkControl::OnMouseMove(UINT /*nFlags*/, CPoint pt)
90 if (m_bOverControl)
92 RECT rect;
93 GetClientRect(&rect);
95 if (!PtInRect(&rect, pt))
96 ReleaseCapture();
98 else
100 m_bOverControl = TRUE;
101 SetFont(&m_UnderlineFont, FALSE);
102 InvalidateRect(nullptr, FALSE);
103 SetCapture();
107 BOOL CLinkControl::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/)
109 ::SetCursor(m_hLinkCursor);
110 return TRUE;
113 void CLinkControl::OnCaptureChanged(CWnd * /*pWnd*/)
115 m_bOverControl = FALSE;
116 SetFont(&m_NormalFont, FALSE);
117 InvalidateRect(nullptr, FALSE);
120 void CLinkControl::OnSetFocus(CWnd* pOldWnd)
122 DrawFocusRect();
123 __super::OnSetFocus(pOldWnd);
126 void CLinkControl::OnKillFocus(CWnd* pOldWnd)
128 ClearFocusRect();
129 __super::OnKillFocus(pOldWnd);
132 void CLinkControl::DrawFocusRect()
134 CWnd *parent = GetParent();
136 if (parent)
138 // calculate where to draw focus rectangle, in screen coords
139 RECT rc;
140 GetWindowRect(&rc);
142 InflateRect(&rc, 1, 1); // add one pixel all around
143 parent->ScreenToClient(&rc);
145 CClientDC dcParent(parent);
146 dcParent.DrawFocusRect(&rc);
150 void CLinkControl::ClearFocusRect()
152 CWnd *parent = GetParent();
154 if (parent)
156 // calculate where to draw focus rectangle, in screen coords
157 RECT rc;
158 GetWindowRect(&rc);
160 InflateRect(&rc, 1, 1); // add one pixel all around
161 parent->ScreenToClient(&rc);
162 // Ask parent to redraw area where we rendered focus rect before.
163 parent->InvalidateRect(&rc);
167 void CLinkControl::NotifyParent(UINT msg)
169 ::PostMessage(GetParent()->GetSafeHwnd(), msg, (WPARAM)GetSafeHwnd(), (LPARAM) 0);
172 UINT CLinkControl::OnGetDlgCode()
174 UINT dlgCode = CStatic::OnGetDlgCode();
175 const MSG *pMsg = CWnd::GetCurrentMessage();
177 // we want all keys to get the return key
178 dlgCode |= DLGC_WANTALLKEYS;
179 dlgCode |= DLGC_BUTTON;
180 // but we don't want the tab key since that should be used in dialogs
181 // to switch the focus
182 dlgCode &= ~DLGC_WANTTAB;
183 dlgCode &= ~DLGC_STATIC;
185 if (pMsg->lParam &&
186 ((MSG *)pMsg->lParam)->message == WM_KEYDOWN &&
187 ((MSG *)pMsg->lParam)->wParam == VK_TAB)
189 dlgCode &= ~DLGC_WANTMESSAGE;
192 return dlgCode;
195 void CLinkControl::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
197 if (nChar == VK_SPACE || nChar == VK_RETURN)
199 NotifyParent(LK_LINKITEMCLICKED);
203 void CLinkControl::OnClicked()
205 NotifyParent(LK_LINKITEMCLICKED);
208 void CLinkControl::OnEnable(BOOL enabled)
210 CStatic::OnEnable(enabled);
213 BOOL CLinkControl::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
215 switch (message)
217 case BM_CLICK:
218 NotifyParent(LK_LINKITEMCLICKED);
219 break;
221 return CStatic::OnWndMsg(message, wParam, lParam, pResult);