Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.5K   23.6K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
/*************************************************************************
				Class Implementation : CUGEditBase
**************************************************************************
	Source file : UGEditBase.cpp
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
*************************************************************************/

#include "stdafx.h"
#include <ATLConv.h>
#include "ugctrl.h"
//#include "UGEditBase.h"

#include "UGXPThemes.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


/***************************************************
CUGEditBase - Constructor
****************************************************/
CUGEditBase::CUGEditBase() :
	m_continueCol(0),
	m_continueRow(0),
	m_continueFlag(0),
	m_cancel(0),
	m_lastKey(0),
	m_ctrl(NULL)
{
	m_Brush.CreateStockObject(HOLLOW_BRUSH);
}

/***************************************************
~CUGEditBase - Destructor
****************************************************/
CUGEditBase::~CUGEditBase()
{
	m_Brush.DeleteObject();
}

/***************************************************
MessageMap
****************************************************/
BEGIN_MESSAGE_MAP(CUGEditBase, CEdit)
	//{{AFX_MSG_MAP(CUGEditBase)
	ON_WM_CTLCOLOR_REFLECT()
    ON_WM_LBUTTONDOWN()
    ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillfocus)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/***************************************************
WindowProc
	Provides all of the default functionality that
	any CEdit based Ultimate Grid edit control must
	have. 
	
	Most CEdit derived edit controls can be easily
	used in Ultimate Grid by just changing the base 
	class. There is generally no need to change any
	functions calls from CEdit::xxx to CUGEditBase::xxx
	
	All of the messages are put here instead of 
	in a Message Map, so that the so that they are 
	properly called, without having to switch any 
	CEdit::xxx calls over to CUGEditBase::xxx calls

	The only stipulation is that the CEdit derived
	class cannot override the WindowsProc. If it
	does, then the base funtionality will be rendered
	useless. In this case, the code below may be copied
	into the controls WindowsProc (also the member 
	variables will need to be copied over and initialized
	as well).
Params
	See CWnd::WindowsProc
Return
	See CWnd::WindowsProc
****************************************************/
LRESULT CUGEditBase::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	switch(message)
	{
		//***** WMCREATE *****
		case WM_CREATE:{
			
			if(m_ctrl == NULL)
				m_ctrl = (CUGCtrl*)GetParent();
			
			break;
		}
		//***** WM_SETFOCUS *****
		case WM_SETFOCUS:{
			
			if(m_ctrl == NULL)
				m_ctrl = (CUGCtrl*)GetParent();

			CEdit::WindowProc(message, wParam, lParam);

			m_lastKey = 0;
			m_cancel = FALSE;
			m_continueFlag = FALSE;

			SetSel(GetWindowTextLength(),0);

			if(m_ctrl->m_editCell.GetReadOnly())
				SetReadOnly(TRUE);

			return 0;
		}
		//***** WM_KILLFOCUS *****
		case WM_KILLFOCUS:{
			
			SetReadOnly(FALSE);
			
			CEdit::WindowProc(message, wParam, lParam);
			
			CWnd* pNewWnd = CWnd::FromHandle((HWND)wParam);

			CEdit::OnKillFocus(pNewWnd);

			if( m_ctrl->m_GI->m_bCancelMode != FALSE )
			{
				if(pNewWnd->GetSafeHwnd() != NULL)
				{
					if(pNewWnd != m_ctrl && pNewWnd->GetParent() != m_ctrl)
						m_cancel = TRUE;
				}
				else
					m_cancel = TRUE;
			}

			if( pNewWnd != m_ctrl )
				m_ctrl->OnKillFocus( UG_GRID, pNewWnd );

			if(pNewWnd->GetSafeHwnd() != NULL)
			{
				if( pNewWnd == m_ctrl )
					::SetFocus( m_ctrl->m_CUGGrid->GetSafeHwnd());
			}

			CString string;
			GetWindowText(string);

			if(m_ctrl->EditCtrlFinished(string,m_cancel,m_continueFlag,m_continueCol,m_continueRow) == FALSE)
			{
				m_ctrl->GotoCell(m_ctrl->m_editCol,m_ctrl->m_editRow);
				SetCapture();
				ReleaseCapture();
				SetFocus();
			}
			return 0;
		}
			// We need this to stop the control from drawing itself outside WM_PAINT when selecting text.
		case WM_MOUSEMOVE:
			{
				UpdateCtrl();
			}
			break;
		//***** WM_KEYDOWN *****
		case WM_KEYDOWN:{

			m_lastKey = (UINT)wParam;

			switch(wParam){
				case VK_TAB:{

					if(GetKeyState(VK_SHIFT)<0){
						m_continueCol = m_ctrl->m_editCol -1;
						m_continueRow = m_ctrl->m_editRow;
					}
					else{
						m_continueCol = m_ctrl->m_editCol +1;
						m_continueRow = m_ctrl->m_editRow;
					}

					m_continueFlag = TRUE;
					m_ctrl->SetFocus();
					return 0;
				}
				case VK_RETURN:{

					if( GetKeyState(VK_CONTROL) < 0 &&
						m_ctrl->m_editCell.GetCellTypeEx() & UGCT_NORMALMULTILINE )
					{	// in mulitiline cells allow CTRL-RETURN to advance to a new line
						return 0;
					}

					m_continueCol = m_ctrl->m_editCol;
					m_continueRow = m_ctrl->m_editRow +1;
					m_continueFlag = TRUE;
					m_ctrl->SetFocus();

					if (UGXPThemes::IsThemed())
						UpdateCtrl();

					return 0;
				}

				case VK_ESCAPE:{
					m_cancel = TRUE;
					m_continueFlag = FALSE;
					m_ctrl->SetFocus();
					return 0;
				}

				case VK_UP:{
					m_continueCol = m_ctrl->m_editCol;
					m_continueRow = m_ctrl->m_editRow-1;
					m_continueFlag = TRUE;
					m_ctrl->SetFocus();

					if (UGXPThemes::IsThemed())
						UpdateCtrl();

					return 0;
				}

				case VK_DOWN:{
					m_continueCol = m_ctrl->m_editCol;
					m_continueRow = m_ctrl->m_editRow+1;
					m_continueFlag = TRUE;
					m_ctrl->SetFocus();

					if (UGXPThemes::IsThemed())
						UpdateCtrl();

					return 0;
				}
				
				case VK_RIGHT:{
					CString cellText;
					int cellTextLength;
					int nStartChar;
					int nStopChar;

					// Get edited text and current selection information
					CEdit::GetWindowText(cellText);
					cellTextLength = cellText.GetLength();
					CEdit::GetSel(nStartChar, nStopChar);

					// Allow edit to jump to the next cell if the user hits the
					// right key and:
					// - the cursor reached the end of the cell
					if( nStartChar == nStopChar && nStopChar == cellTextLength )
					{
						m_continueCol = m_ctrl->m_editCol + 1;
						m_continueRow = m_ctrl->m_editRow;
						m_continueFlag = TRUE;
						m_ctrl->SetFocus();

						if (UGXPThemes::IsThemed())
							UpdateCtrl();

						return 0;
					}
					// - or the entire cell's text content is selected
					else if (nStartChar == 0 && nStopChar == cellTextLength)
					{
						m_continueCol = m_ctrl->m_editCol + 1;
						m_continueRow = m_ctrl->m_editRow;
						m_continueFlag = TRUE;
						m_ctrl->SetFocus();

						if (UGXPThemes::IsThemed())
							UpdateCtrl();

						return 0;
					}

					if (UGXPThemes::IsThemed())
						Invalidate();

					break;
				}

				case VK_LEFT:{
					CString cellText;
					int cellTextLength;
					int nStartChar;
					int nStopChar;

					// Get edited text and current selection information
					CEdit::GetWindowText(cellText);
					cellTextLength = cellText.GetLength();
					CEdit::GetSel(nStartChar, nStopChar);

					// If the user hit the Left key and the cursor position is at
					// the beginning of the edit strin without any selection
					// then allow the edit to jump to the previous cell.
					if( nStartChar == nStopChar && nStartChar == 0 )
					{
						m_continueCol = m_ctrl->m_editCol - 1;
						m_continueRow = m_ctrl->m_editRow;
						m_continueFlag = TRUE;
						m_ctrl->SetFocus();


						if (UGXPThemes::IsThemed())
							UpdateCtrl();

						return 0;
					}

					if (UGXPThemes::IsThemed())
						Invalidate();


					break;
				}

				case VK_DELETE:{
					// The OnEditVerify should also called when user enters
					// the DELETE key.  ASCII representation of the DEL key is decimal 127
					UINT nChar = 127;
					int col = m_ctrl->GetCurrentCol();
					long row = m_ctrl->GetCurrentRow();

					// First the datasource has a look ...
					if(m_ctrl->m_GI->m_defDataSource->OnEditVerify(col,row,this,&nChar) == FALSE)
						return 0;
					// then the ctrl class
					if(m_ctrl->OnEditVerify(col,row,this,&nChar) == FALSE)
						return 0;
				}
			}

			break;
		}
		//***** WM_CHAR *****
		case WM_CHAR:{

			UINT nChar = (UINT)wParam;
			m_lastKey = nChar;

			if(nChar == VK_TAB || nChar == VK_RETURN || nChar == VK_ESCAPE)
				return 0;

			// allow OnEditVerify a look at the char...
			int col = m_ctrl->GetCurrentCol();
			long row = m_ctrl->GetCurrentRow();
			
			// First the datasource has a look ...
			if(m_ctrl->m_GI->m_defDataSource->OnEditVerify(col,row,this,&nChar) == FALSE)
				return 0;

			// then the ctrl class
			if(m_ctrl->OnEditVerify(col,row,this,&nChar) == FALSE)
				return 0;

			// if the characted is allowed by both datasource and ctrl class
			// than set back any changes to the character that user could make
			wParam = (WPARAM)nChar;
			// and allow the edit control to process the new char.

			return CEdit::WindowProc(message, wParam, lParam);
		}
		//***** WM_GETDLGCODE *****
		case WM_GETDLGCODE:{

			return DLGC_WANTALLKEYS | DLGC_WANTARROWS;
		}
		//***** WM_MOUSEACTIVATE *****
		case WM_MOUSEACTIVATE:
			
			return MA_ACTIVATE;	

//		case WM_ERASEBKGND:
		case WM_PAINT:
		{
			if(!UGXPThemes::UseThemes())
				break;
			PAINTSTRUCT ps;
			CDC* cdc = BeginPaint(&ps);
			RECT rc;
			GetWindowRect(&rc);
			ScreenToClient(&rc);

			if (UGXPThemes::DrawBackground(m_hWnd, *cdc, XPCellTypeData, (UGXPThemeState)( ThemeStateSelected | ThemeStateCurrent), &rc, NULL))
			{
				CString text;

				GetWindowText(text);
				// Use the font in the edit box, not the themed font
				// this is because although we're doing our own drawing,
				// the edit box is still telling us how to lay out the text,
				// what is selected, etc.  Changing fonts will make a mess of all of this.
				CFont * pOld = cdc->SelectObject(this->GetFont());
				cdc->SetBkMode(TRANSPARENT);
				RECT rcText(rc);
				rcText.left += 2;
				rcText.top += 2;

				// Work out the part of the string that's selected.
				int nFirstChar = LOWORD(CharFromPos(CPoint(rcText.left, rcText.top)));
				int nBegin, nEnd;
				GetSel(nBegin, nEnd);
				bool bSelected = (nBegin != nEnd);

				nBegin -= nFirstChar;
				nEnd -= nFirstChar;

				RECT rcDraw;
				CopyRect(&rcDraw, &rcText);

				int nRowCount = GetLineCount();

				TCHAR nChar[1024];

				int nCharCount = nFirstChar;

				COLORREF backCol = cdc->GetBkColor();

				// Step through each line of the text.  By letting the
				// edit control tell us what's on each line, we make sure
				// we draw it as the underlying code expects it to be.
				for(int i = this->GetFirstVisibleLine(); i < nRowCount; ++i)
				{
					int nRowChars = GetLine(i, &nChar[0], 1024);

					if (GetStyle() & ES_PASSWORD)
					{
						::memset(&nChar[0], '*', nRowChars);
					}

					nChar[nRowChars] = 0;
					
					cdc->DrawText(&nChar[0], &rcText, DT_LEFT);
					CSize szText = cdc->GetTextExtent(&nChar[0]);
					CString row(&nChar[0]);
					::memset(&nChar[0], 0, 1024);

					rcText.top += szText.cy;

					if (nCharCount + nRowChars >= nBegin && bSelected)
					{
						cdc->SetBkMode(OPAQUE);
						cdc->SetBkColor(0xD00000);

						CString beforeSel = row.Left(nBegin - nCharCount);// text.Mid(nCharCount, nBegin-nCharCount);

						CSize size = cdc->GetTextExtent(beforeSel);

						CString inSel = row.Mid(nBegin - nCharCount, nEnd - nBegin);// text.Mid(nBegin, min(nEnd - nBegin, nRowChars + nCharCount - nBegin));
 
						rcDraw.left += size.cx;
						cdc->DrawText(inSel, &rcDraw, DT_LEFT);
						rcDraw.left = rcText.left;

						// If we selected the entire line, then we need the size of the drawn text for the height
						if (size.cy == 0)
							size = cdc->GetTextExtent(inSel);
	
						rcDraw.top += size.cy;

						cdc->SetBkMode(TRANSPARENT);
						cdc->SetBkColor(backCol);

						// This is to deal with /r/n appearing in the selection text.
						nEnd -= 2;
					}
					else
					{
						CString inSel = text.Mid(nCharCount, nRowChars);
						CSize size = cdc->GetTextExtent(inSel);
						rcDraw.top += size.cy;
					}

					nCharCount += nRowChars;

					if (nBegin < nCharCount) nBegin = nCharCount;

					// This is to deal with /r/n appearing in the selection text.
					nBegin -= 2;
					nEnd -= 2;
				}

				cdc->SelectObject(pOld);

				EndPaint(&ps);
				return true;
			}

			EndPaint(&ps);
		}
		break;
	}

	return CEdit::WindowProc(message, wParam, lParam);
}

/***************************************************
GetLastKey
	GetLastKey is handy if you need to know if
	ESC cancelled the edit.  Key state is not 
	saved (shift, ctrl etc)
Params:
	<none>
Returns:
	last key pressed
*****************************************************/
UINT CUGEditBase::GetLastKey()
{
	return m_lastKey;
}


void CUGEditBase::UpdateCtrl()
{
	if (UGXPThemes::IsThemed())
	{		
		CWnd* pParent = GetParent();
		CRect rect;
	    
		GetWindowRect(rect);
		pParent->ScreenToClient(rect);
		pParent->InvalidateRect(rect, TRUE);

		Invalidate(FALSE);
	}
}


void CUGEditBase::OnKillfocus() 
{
	if (UGXPThemes::IsThemed())
	    UpdateCtrl();

	CEdit::OnKillFocus(this);
}

void CUGEditBase::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if (UGXPThemes::IsThemed())
	    UpdateCtrl();
    
    CEdit::OnLButtonDown(nFlags, point);
}


HBRUSH CUGEditBase::CtlColor(CDC* pDC, UINT nCtlColor) 
{
	if (UGXPThemes::IsThemed())
	{
		pDC->SetBkMode(TRANSPARENT);	
		return (HBRUSH) m_Brush;
	}
	else
	{
		return CEdit::OnCtlColor(pDC, this, nCtlColor);
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions