1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012, 2014, 2016-2017 - TortoiseGit
4 // Copyright (C) 2007, 2012-2014, 2017 - 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.
21 #include "FilterEdit.h"
23 IMPLEMENT_DYNAMIC(CFilterEdit
, CEdit
)
25 CFilterEdit::CFilterEdit()
26 : m_hIconCancelNormal(nullptr)
27 , m_hIconCancelPressed(nullptr)
28 , m_hIconInfo(nullptr)
30 , m_bShowCancelButtonAlways(FALSE
)
31 , m_iButtonClickedMessageId(WM_FILTEREDIT_INFOCLICKED
)
32 , m_iCancelClickedMessageId(WM_FILTEREDIT_CANCELCLICKED
)
33 , m_pValidator(nullptr)
34 , m_backColor(GetSysColor(COLOR_WINDOW
))
36 m_rcEditArea
.SetRect(0, 0, 0, 0);
37 m_rcButtonArea
.SetRect(0, 0, 0, 0);
38 m_rcInfoArea
.SetRect(0, 0, 0, 0);
39 m_sizeInfoIcon
.SetSize(0, 0);
40 m_sizeCancelIcon
.SetSize(0, 0);
43 CFilterEdit::~CFilterEdit()
45 if (m_hIconCancelNormal
)
46 DestroyIcon(m_hIconCancelNormal
);
47 if (m_hIconCancelPressed
)
48 DestroyIcon(m_hIconCancelPressed
);
50 DestroyIcon(m_hIconInfo
);
53 BEGIN_MESSAGE_MAP(CFilterEdit
, CEdit
)
55 ON_MESSAGE(WM_SETFONT
, OnSetFont
)
63 ON_CONTROL_REFLECT_EX(EN_CHANGE
, &CFilterEdit::OnEnChange
)
64 ON_WM_CTLCOLOR_REFLECT()
66 ON_CONTROL_REFLECT(EN_KILLFOCUS
, &CFilterEdit::OnEnKillfocus
)
67 ON_CONTROL_REFLECT(EN_SETFOCUS
, &CFilterEdit::OnEnSetfocus
)
68 ON_MESSAGE(WM_PASTE
, &CFilterEdit::OnPaste
)
69 ON_WM_SYSCOLORCHANGE()
74 // CFilterEdit message handlers
76 void CFilterEdit::PreSubclassWindow( )
78 // We must have a multi line edit
79 // to be able to set the edit rect
80 ASSERT( GetStyle() & ES_MULTILINE
);
85 BOOL
CFilterEdit::PreTranslateMessage( MSG
* pMsg
)
87 return CEdit::PreTranslateMessage(pMsg
);
90 BOOL
CFilterEdit::SetCancelBitmaps(UINT uCancelNormal
, UINT uCancelPressed
, int cx96dpi
, int cy96dpi
, BOOL bShowAlways
)
92 m_bShowCancelButtonAlways
= bShowAlways
;
94 if (m_hIconCancelNormal
)
95 DestroyIcon(m_hIconCancelNormal
);
96 if (m_hIconCancelPressed
)
97 DestroyIcon(m_hIconCancelPressed
);
99 m_hIconCancelNormal
= LoadDpiScaledIcon(uCancelNormal
, cx96dpi
, cy96dpi
);
100 m_hIconCancelPressed
= LoadDpiScaledIcon(uCancelPressed
, cx96dpi
, cy96dpi
);
102 if (!m_hIconCancelNormal
|| !m_hIconCancelPressed
)
105 m_sizeCancelIcon
= GetIconSize(m_hIconCancelNormal
);
111 BOOL
CFilterEdit::SetInfoIcon(UINT uInfo
, int cx96dpi
, int cy96dpi
)
114 DestroyIcon(m_hIconInfo
);
116 m_hIconInfo
= LoadDpiScaledIcon(uInfo
, cx96dpi
, cy96dpi
);
121 m_sizeInfoIcon
= GetIconSize(m_hIconInfo
);
127 BOOL
CFilterEdit::SetCueBanner(LPCWSTR lpcwText
)
131 m_sCueBanner
= lpcwText
;
132 InvalidateRect(nullptr, TRUE
);
135 m_sCueBanner
.Empty();
136 InvalidateRect(nullptr, TRUE
);
140 void CFilterEdit::ResizeWindow()
142 if (!::IsWindow(m_hWnd
))
148 editrc
.left
= rc
.left
+ 4;
149 editrc
.top
= rc
.top
+ 1;
150 editrc
.right
= rc
.right
- 4;
151 editrc
.bottom
= rc
.bottom
- 4;
154 HGDIOBJ oldFont
= dc
.SelectObject(GetFont()->GetSafeHandle());
155 TEXTMETRIC tm
= { 0 };
156 dc
.GetTextMetrics(&tm
);
157 dc
.SelectObject(oldFont
);
159 m_rcEditArea
.left
= editrc
.left
+ m_sizeInfoIcon
.cx
;
160 m_rcEditArea
.right
= editrc
.right
- m_sizeCancelIcon
.cx
- 5;
161 m_rcEditArea
.top
= (rc
.Height() - tm
.tmHeight
) / 2;
162 m_rcEditArea
.bottom
= m_rcEditArea
.top
+ tm
.tmHeight
;
164 m_rcButtonArea
.left
= m_rcEditArea
.right
+ 5;
165 m_rcButtonArea
.right
= rc
.right
;
166 m_rcButtonArea
.top
= (((rc
.bottom
)-m_sizeCancelIcon
.cy
)/2);
167 m_rcButtonArea
.bottom
= m_rcButtonArea
.top
+ m_sizeCancelIcon
.cy
;
169 m_rcInfoArea
.left
= 0;
170 m_rcInfoArea
.right
= m_rcEditArea
.left
;
171 m_rcInfoArea
.top
= (((rc
.bottom
)-m_sizeInfoIcon
.cy
)/2);
172 m_rcInfoArea
.bottom
= m_rcInfoArea
.top
+ m_sizeInfoIcon
.cy
;
174 SetRect(&m_rcEditArea
);
177 void CFilterEdit::SetButtonClickedMessageId(UINT iButtonClickedMessageId
, UINT iCancelClickedMessageId
)
179 m_iButtonClickedMessageId
= iButtonClickedMessageId
;
180 m_iCancelClickedMessageId
= iCancelClickedMessageId
;
183 CSize
CFilterEdit::GetIconSize(HICON hIcon
)
187 if (GetIconInfo(hIcon
, &iconinfo
))
190 if (GetObject(iconinfo
.hbmColor
, sizeof(BITMAP
), &bmp
))
192 size
.cx
= bmp
.bmWidth
;
193 size
.cy
= bmp
.bmHeight
;
199 BOOL
CFilterEdit::OnEraseBkgnd(CDC
* pDC
)
203 pDC
->FillSolidRect(&rc
, m_backColor
);
205 if (GetWindowTextLength() || m_bShowCancelButtonAlways
)
209 DrawIconEx(pDC
->GetSafeHdc(), m_rcButtonArea
.left
, m_rcButtonArea
.top
, m_hIconCancelNormal
,
210 m_sizeCancelIcon
.cx
, m_sizeCancelIcon
.cy
, 0, nullptr, DI_NORMAL
);
214 DrawIconEx(pDC
->GetSafeHdc(), m_rcButtonArea
.left
, m_rcButtonArea
.top
, m_hIconCancelPressed
,
215 m_sizeCancelIcon
.cx
, m_sizeCancelIcon
.cy
, 0, nullptr, DI_NORMAL
);
220 DrawIconEx(pDC
->GetSafeHdc(), m_rcInfoArea
.left
, m_rcInfoArea
.top
, m_hIconInfo
,
221 m_sizeInfoIcon
.cx
, m_sizeInfoIcon
.cy
, 0, nullptr, DI_NORMAL
);
227 void CFilterEdit::OnLButtonUp(UINT nFlags
, CPoint point
)
230 InvalidateRect(nullptr);
231 if (m_rcButtonArea
.PtInRect(point
))
234 CWnd
*pOwner
= GetOwner();
237 pOwner
->SendMessage(m_iCancelClickedMessageId
, (WPARAM
)GetSafeHwnd(), 0);
241 if (m_rcInfoArea
.PtInRect(point
))
243 CWnd
*pOwner
= GetOwner();
246 RECT rc
= m_rcInfoArea
;
248 pOwner
->SendMessage(m_iButtonClickedMessageId
, (WPARAM
)GetSafeHwnd(), (LPARAM
)(LPRECT
)&rc
);
252 CEdit::OnLButtonUp(nFlags
, point
);
255 void CFilterEdit::OnLButtonDown(UINT nFlags
, CPoint point
)
257 m_bPressed
= m_rcButtonArea
.PtInRect(point
);
258 //InvalidateRect(nullptr);
259 CEdit::OnLButtonDown(nFlags
, point
);
262 int CFilterEdit::OnCreate(LPCREATESTRUCT lpCreateStruct
)
264 if (CEdit::OnCreate(lpCreateStruct
) == -1)
272 LRESULT
CFilterEdit::OnSetFont( WPARAM wParam
, LPARAM lParam
)
274 DefWindowProc( WM_SETFONT
, wParam
, lParam
);
281 void CFilterEdit::OnSize( UINT nType
, int cx
, int cy
)
283 CEdit::OnSize( nType
, cx
, cy
);
287 BOOL
CFilterEdit::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
290 GetCursorPos(&pntCursor
);
291 ScreenToClient(&pntCursor
);
292 // if the cursor is not in the edit area, show the normal arrow cursor
293 if (!m_rcEditArea
.PtInRect(pntCursor
))
295 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW
));
299 return CEdit::OnSetCursor(pWnd
, nHitTest
, message
);
302 BOOL
CFilterEdit::OnEnChange()
304 // check whether the entered text is valid
306 InvalidateRect(nullptr);
310 HBRUSH
CFilterEdit::CtlColor(CDC
* pDC
, UINT
/*nCtlColor*/)
312 if (m_backColor
!= GetSysColor(COLOR_WINDOW
))
314 pDC
->SetBkColor(m_backColor
);
320 void CFilterEdit::ValidateAndRedraw()
325 void CFilterEdit::Validate()
331 m_backColor
= GetSysColor(COLOR_WINDOW
);
332 if (!m_pValidator
->Validate(text
))
334 // Use a background color slightly shifted to red.
335 // We do this by increasing red component and decreasing green and blue.
336 const int SHIFT_PRECENTAGE
= 10;
337 int r
= GetRValue(m_backColor
);
338 int g
= GetGValue(m_backColor
);
339 int b
= GetBValue(m_backColor
);
341 r
= min(r
* (100 + SHIFT_PRECENTAGE
) / 100, 255);
342 // Ensure that there is at least some redness.
343 r
= max(r
, 255 * SHIFT_PRECENTAGE
/ 100);
344 g
= g
* (100 - SHIFT_PRECENTAGE
) / 100;
345 b
= b
* (100 - SHIFT_PRECENTAGE
) / 100;
346 m_backColor
= RGB(r
, g
, b
);
347 m_brBack
.DeleteObject();
348 m_brBack
.CreateSolidBrush(m_backColor
);
353 void CFilterEdit::OnPaint()
355 LRESULT defres
= Default();
360 // the Default() call did not process the WM_PAINT message!
361 // Validate the update region ourselves to avoid
362 // an endless loop repainting
364 GetUpdateRect(&rc
, FALSE
);
365 if (!rc
.IsRectEmpty())
372 void CFilterEdit::DrawDimText()
374 if (m_sCueBanner
.IsEmpty())
376 if (GetWindowTextLength())
378 if (GetFocus() == this)
381 CClientDC
dcDraw(this);
382 int iState
= dcDraw
.SaveDC();
384 dcDraw
.SelectObject((*GetFont()));
385 dcDraw
.SetTextColor(GetSysColor(COLOR_GRAYTEXT
));
386 dcDraw
.SetBkColor(GetSysColor(COLOR_WINDOW
));
387 dcDraw
.DrawText(m_sCueBanner
, m_sCueBanner
.GetLength(), &m_rcEditArea
, DT_CENTER
| DT_VCENTER
);
388 dcDraw
.RestoreDC(iState
);
392 HICON
CFilterEdit::LoadDpiScaledIcon(UINT resourceId
, int cx96dpi
, int cy96dpi
)
396 int cx
= MulDiv(cx96dpi
, dc
.GetDeviceCaps(LOGPIXELSX
), 96);
397 int cy
= MulDiv(cy96dpi
, dc
.GetDeviceCaps(LOGPIXELSY
), 96);
399 return (HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(resourceId
), IMAGE_ICON
, cx
, cy
, LR_DEFAULTCOLOR
);
402 void CFilterEdit::OnEnKillfocus()
404 InvalidateRect(nullptr);
407 void CFilterEdit::OnEnSetfocus()
409 InvalidateRect(nullptr);
412 LRESULT
CFilterEdit::OnPaste(WPARAM
, LPARAM
)
416 HANDLE hData
= GetClipboardData (CF_TEXT
);
417 CString
toInsert((const char*)GlobalLock(hData
));
421 // elimate control chars, especially newlines
422 toInsert
.Replace(L
'\t', L
' ');
424 // only insert first line
425 toInsert
.Replace(L
'\r', L
'\n');
427 toInsert
= toInsert
.Tokenize(L
"\n", pos
);
430 // get the current text
434 // construct the new text
437 text
.Delete(from
, to
- from
);
438 text
.Insert(from
, toInsert
);
439 from
+= toInsert
.GetLength();
441 // update & notify controls
443 SetSel(from
, from
, FALSE
);
446 GetParent()->SendMessage(WM_COMMAND
, MAKEWPARAM(GetDlgCtrlID(), EN_CHANGE
), (LPARAM
)GetSafeHwnd());
451 void CFilterEdit::OnSysColorChange()
453 __super::OnSysColorChange();
454 m_backColor
= GetSysColor(COLOR_WINDOW
);
458 ULONG
CFilterEdit::GetGestureStatus(CPoint
/*ptTouch*/)