Fixed issue #3047: When Log Messages window is narrow, Filter box placeholder text...
[TortoiseGit.git] / src / Utils / MiscUI / FilterEdit.cpp
blob79ebec225c5266a88057318e4c42faeebabd13d8
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.
20 #include "stdafx.h"
21 #include "FilterEdit.h"
23 IMPLEMENT_DYNAMIC(CFilterEdit, CEdit)
25 CFilterEdit::CFilterEdit()
26 : m_hIconCancelNormal(nullptr)
27 , m_hIconCancelPressed(nullptr)
28 , m_hIconInfo(nullptr)
29 , m_bPressed(FALSE)
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);
49 if (m_hIconInfo)
50 DestroyIcon(m_hIconInfo);
53 BEGIN_MESSAGE_MAP(CFilterEdit, CEdit)
55 ON_MESSAGE(WM_SETFONT, OnSetFont)
56 ON_WM_SIZE()
57 ON_WM_ERASEBKGND()
58 ON_WM_KEYDOWN()
59 ON_WM_LBUTTONDOWN()
60 ON_WM_LBUTTONUP()
61 ON_WM_CREATE()
62 ON_WM_SETCURSOR()
63 ON_CONTROL_REFLECT_EX(EN_CHANGE, &CFilterEdit::OnEnChange)
64 ON_WM_CTLCOLOR_REFLECT()
65 ON_WM_PAINT()
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()
70 END_MESSAGE_MAP()
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 );
82 ResizeWindow();
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)
103 return FALSE;
105 m_sizeCancelIcon = GetIconSize(m_hIconCancelNormal);
107 ResizeWindow();
108 return TRUE;
111 BOOL CFilterEdit::SetInfoIcon(UINT uInfo, int cx96dpi, int cy96dpi)
113 if (m_hIconInfo)
114 DestroyIcon(m_hIconInfo);
116 m_hIconInfo = LoadDpiScaledIcon(uInfo, cx96dpi, cy96dpi);
118 if (!m_hIconInfo)
119 return FALSE;
121 m_sizeInfoIcon = GetIconSize(m_hIconInfo);
123 ResizeWindow();
124 return TRUE;
127 BOOL CFilterEdit::SetCueBanner(LPCWSTR lpcwText)
129 if (lpcwText)
131 m_sCueBanner = lpcwText;
132 InvalidateRect(nullptr, TRUE);
133 return TRUE;
135 m_sCueBanner.Empty();
136 InvalidateRect(nullptr, TRUE);
137 return FALSE;
140 void CFilterEdit::ResizeWindow()
142 if (!::IsWindow(m_hWnd))
143 return;
145 CRect editrc, rc;
146 GetRect(&editrc);
147 GetClientRect(&rc);
148 editrc.left = rc.left + 4;
149 editrc.top = rc.top + 1;
150 editrc.right = rc.right - 4;
151 editrc.bottom = rc.bottom - 4;
153 CWindowDC dc(this);
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)
185 CSize size(0, 0);
186 ICONINFO iconinfo;
187 if (GetIconInfo(hIcon, &iconinfo))
189 BITMAP bmp;
190 if (GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bmp))
192 size.cx = bmp.bmWidth;
193 size.cy = bmp.bmHeight;
196 return size;
199 BOOL CFilterEdit::OnEraseBkgnd(CDC* pDC)
201 RECT rc;
202 GetClientRect(&rc);
203 pDC->FillSolidRect(&rc, m_backColor);
205 if (GetWindowTextLength() || m_bShowCancelButtonAlways)
207 if (!m_bPressed)
209 DrawIconEx(pDC->GetSafeHdc(), m_rcButtonArea.left, m_rcButtonArea.top, m_hIconCancelNormal,
210 m_sizeCancelIcon.cx, m_sizeCancelIcon.cy, 0, nullptr, DI_NORMAL);
212 else
214 DrawIconEx(pDC->GetSafeHdc(), m_rcButtonArea.left, m_rcButtonArea.top, m_hIconCancelPressed,
215 m_sizeCancelIcon.cx, m_sizeCancelIcon.cy, 0, nullptr, DI_NORMAL);
218 if (m_hIconInfo)
220 DrawIconEx(pDC->GetSafeHdc(), m_rcInfoArea.left, m_rcInfoArea.top, m_hIconInfo,
221 m_sizeInfoIcon.cx, m_sizeInfoIcon.cy, 0, nullptr, DI_NORMAL);
224 return TRUE;
227 void CFilterEdit::OnLButtonUp(UINT nFlags, CPoint point)
229 m_bPressed = FALSE;
230 InvalidateRect(nullptr);
231 if (m_rcButtonArea.PtInRect(point))
233 SetWindowText(L"");
234 CWnd *pOwner = GetOwner();
235 if (pOwner)
237 pOwner->SendMessage(m_iCancelClickedMessageId, (WPARAM)GetSafeHwnd(), 0);
239 Validate();
241 if (m_rcInfoArea.PtInRect(point))
243 CWnd *pOwner = GetOwner();
244 if (pOwner)
246 RECT rc = m_rcInfoArea;
247 ClientToScreen(&rc);
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)
265 return -1;
267 ResizeWindow();
269 return 0;
272 LRESULT CFilterEdit::OnSetFont( WPARAM wParam, LPARAM lParam )
274 DefWindowProc( WM_SETFONT, wParam, lParam );
276 ResizeWindow();
278 return 0;
281 void CFilterEdit::OnSize( UINT nType, int cx, int cy )
283 CEdit::OnSize( nType, cx, cy );
284 ResizeWindow();
287 BOOL CFilterEdit::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
289 CPoint pntCursor;
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));
296 return TRUE;
299 return CEdit::OnSetCursor(pWnd, nHitTest, message);
302 BOOL CFilterEdit::OnEnChange()
304 // check whether the entered text is valid
305 Validate();
306 InvalidateRect(nullptr);
307 return FALSE;
310 HBRUSH CFilterEdit::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
312 if (m_backColor != GetSysColor(COLOR_WINDOW))
314 pDC->SetBkColor(m_backColor);
315 return m_brBack;
317 return nullptr;
320 void CFilterEdit::ValidateAndRedraw()
322 OnEnChange();
325 void CFilterEdit::Validate()
327 if (m_pValidator)
329 CString text;
330 GetWindowText(text);
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();
357 DrawDimText();
358 if (defres)
360 // the Default() call did not process the WM_PAINT message!
361 // Validate the update region ourselves to avoid
362 // an endless loop repainting
363 CRect rc;
364 GetUpdateRect(&rc, FALSE);
365 if (!rc.IsRectEmpty())
366 ValidateRect(rc);
369 return;
372 void CFilterEdit::DrawDimText()
374 if (m_sCueBanner.IsEmpty())
375 return;
376 if (GetWindowTextLength())
377 return;
378 if (GetFocus() == this)
379 return;
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);
389 return;
392 HICON CFilterEdit::LoadDpiScaledIcon(UINT resourceId, int cx96dpi, int cy96dpi)
394 CWindowDC dc(this);
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)
414 if (OpenClipboard())
416 HANDLE hData = GetClipboardData (CF_TEXT);
417 CString toInsert((const char*)GlobalLock(hData));
418 GlobalUnlock(hData);
419 CloseClipboard();
421 // elimate control chars, especially newlines
422 toInsert.Replace(L'\t', L' ');
424 // only insert first line
425 toInsert.Replace(L'\r', L'\n');
426 int pos = 0;
427 toInsert = toInsert.Tokenize(L"\n", pos);
428 toInsert.Trim();
430 // get the current text
431 CString text;
432 GetWindowText(text);
434 // construct the new text
435 int from, to;
436 GetSel(from, to);
437 text.Delete(from, to - from);
438 text.Insert(from, toInsert);
439 from += toInsert.GetLength();
441 // update & notify controls
442 SetWindowText(text);
443 SetSel(from, from, FALSE);
444 SetModify(TRUE);
446 GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), EN_CHANGE), (LPARAM)GetSafeHwnd());
448 return 0;
451 void CFilterEdit::OnSysColorChange()
453 __super::OnSysColorChange();
454 m_backColor = GetSysColor(COLOR_WINDOW);
455 Invalidate();
458 ULONG CFilterEdit::GetGestureStatus(CPoint /*ptTouch*/)
460 return 0;