Validate the drawing area after the custom painting of the control
[TortoiseGit.git] / src / Utils / MiscUI / FilterEdit.cpp
blob3827301853fc6d36d74f4e7922ae3ea2690573d1
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012 - TortoiseGit
4 // Copyright (C) 2007, 2012-2013 - 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() : m_hIconCancelNormal(NULL)
26 , m_hIconCancelPressed(NULL)
27 , m_hIconInfo(NULL)
28 , m_bPressed(FALSE)
29 , m_bShowCancelButtonAlways(FALSE)
30 , m_iButtonClickedMessageId(WM_FILTEREDIT_INFOCLICKED)
31 , m_iCancelClickedMessageId(WM_FILTEREDIT_CANCELCLICKED)
32 , m_pValidator(NULL)
33 , m_backColor(GetSysColor(COLOR_WINDOW))
34 , m_brBack(NULL)
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);
51 if (m_brBack)
52 DeleteObject(m_brBack);
55 BEGIN_MESSAGE_MAP(CFilterEdit, CEdit)
57 ON_MESSAGE(WM_SETFONT, OnSetFont)
58 ON_WM_SIZE()
59 ON_WM_ERASEBKGND()
60 ON_WM_KEYDOWN()
61 ON_WM_LBUTTONDOWN()
62 ON_WM_LBUTTONUP()
63 ON_WM_CREATE()
64 ON_WM_SETCURSOR()
65 ON_CONTROL_REFLECT_EX(EN_CHANGE, &CFilterEdit::OnEnChange)
66 ON_WM_CTLCOLOR_REFLECT()
67 ON_WM_PAINT()
68 ON_CONTROL_REFLECT(EN_KILLFOCUS, &CFilterEdit::OnEnKillfocus)
69 ON_CONTROL_REFLECT(EN_SETFOCUS, &CFilterEdit::OnEnSetfocus)
70 ON_MESSAGE(WM_PASTE, &CFilterEdit::OnPaste)
71 END_MESSAGE_MAP()
75 // CFilterEdit message handlers
77 void CFilterEdit::PreSubclassWindow( )
79 // We must have a multi line edit
80 // to be able to set the edit rect
81 ASSERT( GetStyle() & ES_MULTILINE );
83 ResizeWindow();
86 BOOL CFilterEdit::PreTranslateMessage( MSG* pMsg )
88 return CEdit::PreTranslateMessage(pMsg);
91 BOOL CFilterEdit::SetCancelBitmaps(UINT uCancelNormal, UINT uCancelPressed, BOOL bShowAlways)
93 m_bShowCancelButtonAlways = bShowAlways;
95 if (m_hIconCancelNormal)
96 DestroyIcon(m_hIconCancelNormal);
97 if (m_hIconCancelPressed)
98 DestroyIcon(m_hIconCancelPressed);
100 m_hIconCancelNormal = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(uCancelNormal), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
101 m_hIconCancelPressed = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(uCancelPressed), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
103 if ((m_hIconCancelNormal == 0) || (m_hIconCancelPressed == 0))
104 return FALSE;
106 m_sizeCancelIcon = GetIconSize(m_hIconCancelNormal);
108 ResizeWindow();
109 return TRUE;
112 BOOL CFilterEdit::SetInfoIcon(UINT uInfo)
114 if (m_hIconInfo)
115 DestroyIcon(m_hIconInfo);
117 m_hIconInfo = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(uInfo), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
119 if (m_hIconInfo == 0)
120 return FALSE;
122 m_sizeInfoIcon = GetIconSize(m_hIconInfo);
124 ResizeWindow();
125 return TRUE;
128 BOOL CFilterEdit::SetCueBanner(LPCWSTR lpcwText)
130 if (lpcwText)
132 size_t len = _tcslen(lpcwText);
133 m_pCueBanner.reset(new TCHAR[len + 1]);
134 _tcscpy_s(m_pCueBanner.get(), len + 1, lpcwText);
135 InvalidateRect(NULL, TRUE);
136 return TRUE;
138 return FALSE;
141 void CFilterEdit::ResizeWindow()
143 if (!::IsWindow(m_hWnd))
144 return;
146 RECT editrc, rc;
147 GetRect(&editrc);
148 GetClientRect(&rc);
149 editrc.left = rc.left + 4;
150 editrc.top = rc.top + 1;
151 editrc.right = rc.right - 4;
152 editrc.bottom = rc.bottom - 4;
154 m_rcEditArea.left = editrc.left + m_sizeInfoIcon.cx;
155 m_rcEditArea.right = editrc.right - m_sizeCancelIcon.cx - 5;
156 m_rcEditArea.top = editrc.top;
157 m_rcEditArea.bottom = editrc.bottom;
159 m_rcButtonArea.left = m_rcEditArea.right + 5;
160 m_rcButtonArea.right = rc.right;
161 m_rcButtonArea.top = (((rc.bottom)-m_sizeCancelIcon.cy)/2);
162 m_rcButtonArea.bottom = m_rcButtonArea.top + m_sizeCancelIcon.cy;
164 m_rcInfoArea.left = 0;
165 m_rcInfoArea.right = m_rcEditArea.left;
166 m_rcInfoArea.top = (((rc.bottom)-m_sizeInfoIcon.cy)/2);
167 m_rcInfoArea.bottom = m_rcInfoArea.top + m_sizeInfoIcon.cy;
169 SetRect(&m_rcEditArea);
172 void CFilterEdit::SetButtonClickedMessageId(UINT iButtonClickedMessageId, UINT iCancelClickedMessageId)
174 m_iButtonClickedMessageId = iButtonClickedMessageId;
175 m_iCancelClickedMessageId = iCancelClickedMessageId;
178 CSize CFilterEdit::GetIconSize(HICON hIcon)
180 CSize size(0, 0);
181 ICONINFO iconinfo;
182 if (GetIconInfo(hIcon, &iconinfo))
184 BITMAP bmp;
185 if (GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bmp))
187 size.cx = bmp.bmWidth;
188 size.cy = bmp.bmHeight;
191 return size;
194 BOOL CFilterEdit::OnEraseBkgnd(CDC* pDC)
196 RECT rc;
197 GetClientRect(&rc);
198 pDC->FillSolidRect(&rc, m_backColor);
200 if (GetWindowTextLength() || m_bShowCancelButtonAlways)
202 if (!m_bPressed)
204 DrawIconEx(pDC->GetSafeHdc(), m_rcButtonArea.left, m_rcButtonArea.top, m_hIconCancelNormal,
205 m_sizeCancelIcon.cx, m_sizeCancelIcon.cy, 0, NULL, DI_NORMAL);
207 else
209 DrawIconEx(pDC->GetSafeHdc(), m_rcButtonArea.left, m_rcButtonArea.top, m_hIconCancelPressed,
210 m_sizeCancelIcon.cx, m_sizeCancelIcon.cy, 0, NULL, DI_NORMAL);
213 if (m_hIconInfo)
215 DrawIconEx(pDC->GetSafeHdc(), m_rcInfoArea.left, m_rcInfoArea.top, m_hIconInfo,
216 m_sizeInfoIcon.cx, m_sizeInfoIcon.cy, 0, NULL, DI_NORMAL);
219 return TRUE;
222 void CFilterEdit::OnLButtonUp(UINT nFlags, CPoint point)
224 m_bPressed = FALSE;
225 InvalidateRect(NULL);
226 if (m_rcButtonArea.PtInRect(point))
228 SetWindowText(_T(""));
229 CWnd *pOwner = GetOwner();
230 if (pOwner)
232 pOwner->SendMessage(m_iCancelClickedMessageId, 0, 0);
234 Validate();
236 if (m_rcInfoArea.PtInRect(point))
238 CWnd *pOwner = GetOwner();
239 if (pOwner)
241 RECT rc = m_rcInfoArea;
242 ClientToScreen(&rc);
243 pOwner->SendMessage(m_iButtonClickedMessageId, 0, (LPARAM)(LPRECT)&rc);
247 CEdit::OnLButtonUp(nFlags, point);
250 void CFilterEdit::OnLButtonDown(UINT nFlags, CPoint point)
252 m_bPressed = m_rcButtonArea.PtInRect(point);
253 //InvalidateRect(NULL);
254 CEdit::OnLButtonDown(nFlags, point);
257 int CFilterEdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
259 if (CEdit::OnCreate(lpCreateStruct) == -1)
260 return -1;
262 ResizeWindow();
264 return 0;
267 LRESULT CFilterEdit::OnSetFont( WPARAM wParam, LPARAM lParam )
269 DefWindowProc( WM_SETFONT, wParam, lParam );
271 ResizeWindow();
273 return 0;
276 void CFilterEdit::OnSize( UINT nType, int cx, int cy )
278 CEdit::OnSize( nType, cx, cy );
279 ResizeWindow();
282 BOOL CFilterEdit::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
284 CPoint pntCursor;
285 GetCursorPos(&pntCursor);
286 ScreenToClient(&pntCursor);
287 // if the cursor is not in the edit area, show the normal arrow cursor
288 if (!m_rcEditArea.PtInRect(pntCursor))
290 SetCursor(AfxGetApp()->LoadStandardCursor(MAKEINTRESOURCE(IDC_ARROW)));
291 return TRUE;
294 return CEdit::OnSetCursor(pWnd, nHitTest, message);
297 BOOL CFilterEdit::OnEnChange()
299 // check whether the entered text is valid
300 Validate();
301 InvalidateRect(NULL);
302 return FALSE;
305 HBRUSH CFilterEdit::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
307 if (m_backColor != GetSysColor(COLOR_WINDOW))
309 pDC->SetBkColor(m_backColor);
310 return m_brBack;
312 return NULL;
315 void CFilterEdit::Validate()
317 if (m_pValidator)
319 int len = GetWindowTextLength();
320 TCHAR * pBuf = new TCHAR[len+1];
321 GetWindowText(pBuf, len+1);
322 m_backColor = GetSysColor(COLOR_WINDOW);
323 if (!m_pValidator->Validate(pBuf))
325 // Use a background color slightly shifted to red.
326 // We do this by increasing red component and decreasing green and blue.
327 const int SHIFT_PRECENTAGE = 10;
328 int r = GetRValue(m_backColor);
329 int g = GetGValue(m_backColor);
330 int b = GetBValue(m_backColor);
332 r = min(r * (100 + SHIFT_PRECENTAGE) / 100, 255);
333 // Ensure that there is at least some redness.
334 r = max(r, 255 * SHIFT_PRECENTAGE / 100);
335 g = g * (100 - SHIFT_PRECENTAGE) / 100;
336 b = b * (100 - SHIFT_PRECENTAGE) / 100;
337 m_backColor = RGB(r, g, b);
338 if (m_brBack)
339 DeleteObject(m_brBack);
340 m_brBack = CreateSolidBrush(m_backColor);
342 delete [] pBuf;
346 void CFilterEdit::OnPaint()
348 Default();
350 DrawDimText();
351 ValidateRect(NULL);
353 return;
356 void CFilterEdit::DrawDimText()
358 if (m_pCueBanner.get() == NULL)
359 return;
360 if (GetWindowTextLength())
361 return;
362 if (m_pCueBanner.get()[0] == 0)
363 return;
364 if (GetFocus() == this)
365 return;
367 CClientDC dcDraw(this);
368 CRect rRect;
369 int iState = dcDraw.SaveDC();
371 GetClientRect(&rRect);
372 rRect.OffsetRect(1, 1);
374 dcDraw.SelectObject((*GetFont()));
375 dcDraw.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
376 dcDraw.SetBkColor(GetSysColor(COLOR_WINDOW));
377 dcDraw.DrawText(m_pCueBanner.get(), (int)_tcslen(m_pCueBanner.get()), &rRect, DT_CENTER | DT_VCENTER);
378 dcDraw.RestoreDC(iState);
379 return;
382 void CFilterEdit::OnEnKillfocus()
384 InvalidateRect(NULL);
387 void CFilterEdit::OnEnSetfocus()
389 InvalidateRect(NULL);
392 LRESULT CFilterEdit::OnPaste(WPARAM, LPARAM)
394 if (OpenClipboard())
396 HANDLE hData = GetClipboardData (CF_TEXT);
397 CString toInsert((const char*)GlobalLock(hData));
398 GlobalUnlock(hData);
399 CloseClipboard();
401 // elimate control chars, especially newlines
402 toInsert.Replace(_T('\t'), _T(' '));
404 // only insert first line
405 toInsert.Replace(_T('\r'), _T('\n'));
406 int pos = 0;
407 toInsert = toInsert.Tokenize(_T("\n"), pos);
408 toInsert.Trim();
410 // get the current text
411 CString text;
412 GetWindowText(text);
414 // construct the new text
415 int from, to;
416 GetSel(from, to);
417 text.Delete(from, to - from);
418 text.Insert(from, toInsert);
419 from += toInsert.GetLength();
421 // update & notify controls
422 SetWindowText(text);
423 SetSel(from, from, FALSE);
424 SetModify(TRUE);
426 GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), EN_CHANGE), (LPARAM)GetSafeHwnd());
428 return 0;