// EditWithButton.cpp : implementation file // //DISCLAIMER: //The code in this project is Copyright (C) 2006 by Gautam Jain. You have the right to //use and distribute the code in any way you see fit as long as this paragraph is included //with the distribution. No warranties or claims are made as to the validity of the //information and code contained herein, so use it at your own risk. #include "stdafx.h" #include "EditWithButton.h" #include "cp_main.h" IMPLEMENT_DYNAMIC(CEditWithButton, CEdit) CEditWithButton::CEditWithButton() { m_iButtonClickedMessageId = NM_CANCEL_SEARCH; m_bButtonExistsAlways = FALSE; m_rcEditArea.SetRect(0, 0, 0, 0); } CEditWithButton::~CEditWithButton() { m_bmpEmptyEdit.DeleteObject(); m_bmpFilledEdit.DeleteObject(); } BEGIN_MESSAGE_MAP(CEditWithButton, CEdit) ON_MESSAGE(WM_SETFONT, OnSetFont) ON_WM_SIZE() ON_WM_ERASEBKGND() ON_WM_CHAR() ON_WM_KEYDOWN() ON_WM_LBUTTONUP() ON_WM_SETCURSOR() ON_WM_CREATE() END_MESSAGE_MAP() // CEditWithButton message handlers void CEditWithButton::PreSubclassWindow() { // We must have a multiline edit // to be able to set the edit rect ASSERT(GetStyle() & ES_MULTILINE); ResizeWindow(); } BOOL CEditWithButton::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class // Intercept Ctrl + Z (Undo), Ctrl + X (Cut), Ctrl + C (Copy), Ctrl + V (Paste) and Ctrl + A (Select All) // before CEdit base class gets a hold of them. if (pMsg->message == WM_KEYDOWN && CONTROL_PRESSED) { switch (pMsg->wParam) { case 'Z': Undo(); return TRUE; case 'X': Cut(); return TRUE; case 'C': Copy(); return TRUE; case 'V': Paste(); return TRUE; case 'A': SetSel(0, -1); return TRUE; } } switch(pMsg->message) { case WM_KEYDOWN: { if(pMsg->wParam == VK_RETURN) { CWnd *pWnd = GetParent(); if(pWnd) { if(g_Opt.m_bFindAsYouType) { pWnd->SendMessage(NM_SEARCH_ENTER_PRESSED, 0, 0); } else { //Send a message to the parent to refill the lb from the search pWnd->PostMessage(CB_SEARCH, 0, 0); } } return TRUE; } else if (pMsg->wParam == VK_DOWN || pMsg->wParam == VK_UP || pMsg->wParam == VK_F3 || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT) { CWnd *pWnd = GetParent(); if(pWnd) { pWnd->SendMessage(CB_UPDOWN, pMsg->wParam, pMsg->lParam); return TRUE; } } break; } } return CEdit::PreTranslateMessage(pMsg); } BOOL CEditWithButton::SetBitmaps(UINT iEmptyEdit, UINT iFilledEdit) { BITMAP bmpInfo; //delete if already loaded.. just in case m_bmpEmptyEdit.DeleteObject(); m_bmpFilledEdit.DeleteObject(); m_bmpEmptyEdit.LoadBitmap(iEmptyEdit); m_bmpFilledEdit.LoadBitmap(iFilledEdit); m_bmpEmptyEdit.GetBitmap(&bmpInfo); m_sizeEmptyBitmap.SetSize(bmpInfo.bmWidth,bmpInfo.bmHeight); m_bmpFilledEdit.GetBitmap(&bmpInfo); m_sizeFilledBitmap.SetSize(bmpInfo.bmWidth,bmpInfo.bmHeight); return TRUE; } //client area void CEditWithButton::SetButtonArea(CRect rcButtonArea) { m_rcButtonArea = rcButtonArea; } void CEditWithButton::ResizeWindow() { if (!::IsWindow(m_hWnd)) return; //proceed only if edit area is set if (m_rcBorder == CRect(0,0,0,0)) return; CRect r; GetWindowRect(r); ScreenToClient(r); SetWindowPos(&wndTop, 0, 0, r.Width(), r.Height(), SWP_NOMOVE|SWP_NOZORDER); m_rcEditArea.left = r.left + m_rcBorder.left; m_rcEditArea.top = r.top + m_rcBorder.top; m_rcEditArea.right = r.right -= m_rcBorder.right; m_rcEditArea.bottom = r.bottom -= m_rcBorder.bottom; SetRect(&m_rcEditArea); } //set edit area may be called before creating the edit control //especially when using the CEdit::Create method //or after creating the edit control in CEdit::DoDataExchange //we call ResizeWindow once in SetEditArea and once in PreSubclassWindow BOOL CEditWithButton::SetBorder(CRect rcEditArea) { m_rcBorder = rcEditArea; ResizeWindow(); return TRUE; } void CEditWithButton::SetButtonExistsAlways(BOOL bButtonExistsAlways) { m_bButtonExistsAlways = bButtonExistsAlways; } BOOL CEditWithButton::OnEraseBkgnd(CDC* pDC) { // Get the size of the bitmap CDC dcMemory; CSize sizeBitmap; CBitmap* pOldBitmap = NULL; int iTextLength = GetWindowTextLength(); CRect size; GetWindowRect(size); if (iTextLength == 0) { sizeBitmap = m_sizeEmptyBitmap; } else { sizeBitmap = m_sizeFilledBitmap; } // Create an in-memory DC compatible with the // display DC we're using to paint dcMemory.CreateCompatibleDC(pDC); if (iTextLength == 0) { // Select the bitmap into the in-memory DC pOldBitmap = dcMemory.SelectObject(&m_bmpEmptyEdit); } else { // Select the bitmap into the in-memory DC pOldBitmap = dcMemory.SelectObject(&m_bmpFilledEdit); } // Copy the bits from the in-memory DC into the on- // screen DC to actually do the painting. Use the centerpoint // we computed for the target offset. pDC->BitBlt(0,0, 50, sizeBitmap.cy, &dcMemory, 0, 0, SRCCOPY); for(int i = 50; i < size.Width()-50; i++) { pDC->BitBlt(i, 0, 1, sizeBitmap.cy, &dcMemory, 50, 0, SRCCOPY); } pDC->BitBlt(size.Width()-50, 0, 50, sizeBitmap.cy, &dcMemory, sizeBitmap.cx-50, 0, SRCCOPY); dcMemory.SelectObject(pOldBitmap); return TRUE; } void CEditWithButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { //this will draw the background again //so that the button will be drawn if the text exists InvalidateRect(NULL); CEdit::OnKeyDown(nChar, nRepCnt, nFlags); } void CEditWithButton::OnLButtonUp(UINT nFlags, CPoint point) { //if the button is clicked then send message to the //owner.. the owner need not be parent //you can set the owner using the CWnd::SetOwner method CRect crClose; GetWindowRect(crClose); ScreenToClient(crClose); int left = crClose.right - (m_sizeFilledBitmap.cx - m_rcButtonArea.left); int right = crClose.right - (m_sizeFilledBitmap.cx - m_rcButtonArea.right); crClose.top = m_rcButtonArea.top; crClose.bottom = m_rcButtonArea.bottom; crClose.left = left; crClose.right = right; if (crClose.PtInRect(point)) { //it is assumed that when the text is not typed in the //edit control, the button will not be visible //but you can override this by setting //the m_bButtonExistsAlways to TRUE if ( (GetWindowTextLength() > 0) || m_bButtonExistsAlways) { CWnd *pOwner = GetOwner(); if (pOwner) { pOwner->SendMessage(m_iButtonClickedMessageId, 0, 0); } } } CEdit::OnLButtonUp(nFlags, point); } //by default, when the mouse moves over the edit control //the system shows the I-beam cursor. However we want to //show the arrow cursor when it is over the Non-Edit area //where the button and icon is displayed //here is the code to do this BOOL CEditWithButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { CPoint pntCursor; GetCursorPos(&pntCursor); ScreenToClient(&pntCursor); CRect edit; GetWindowRect(edit); ScreenToClient(edit); edit.left += m_rcBorder.left; edit.top += m_rcBorder.top; edit.right -= m_rcBorder.right; edit.bottom -= m_rcBorder.bottom; //if mouse is not in the edit area then //show arrow cursor if (!edit.PtInRect(pntCursor)) { SetCursor(AfxGetApp()->LoadStandardCursor(MAKEINTRESOURCE(IDC_ARROW))); return TRUE; } return CEdit::OnSetCursor(pWnd, nHitTest, message); } int CEditWithButton::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CEdit::OnCreate(lpCreateStruct) == -1) return -1; ResizeWindow(); return 0; } LRESULT CEditWithButton::OnSetFont( WPARAM wParam, LPARAM lParam ) { DefWindowProc(WM_SETFONT, wParam, lParam); ResizeWindow(); return 0; } void CEditWithButton::OnSize(UINT nType, int cx, int cy) { CEdit::OnSize(nType, cx, cy); ResizeWindow(); }