Click here to Skip to main content
15,892,746 members
Articles / Desktop Programming / MFC

Enhanced Focus Edit Control with Input Filtering

Rate me:
Please Sign up or sign in to vote.
4.17/5 (6 votes)
3 Oct 2000CPOL 138.4K   2.7K   34  
An edit control that provides strong visual feedback when it has the input focus, and allows filtering of input and/or displayed characters.
/*|*\
|*|  File:      FocusEditCtrl.h
|*|  
|*|  By:        James R. Twine, Rubin And Associates, Inc.
|*|             Copyright 1988, Rubin And Associates, Inc.
|*|  Date:      Friday, October 09, 1998
|*|             
|*|             This implementes an Edit Control that provides
|*|             a custom background color to indicate focus.
|*|             (This class uses static variables for the colors,
|*|             so all controls that uses this class will use the
|*|             same colors).
|*|				Portions of this code are derived from CDropEdit
|*|				by Chris Losinger.  (See comments below)
|*|             
|*|             These Changes May Be Freely Incorporated Into 
|*|             Projects Of Any Type Subject To The Following 
|*|             Conditions:
|*|             
|*|             o This Header Must Remain In This File, And Any
|*|               Files Derived From It
|*|             o Do Not Misrepresent The Origin Of Any Parts Of 
|*|               This Code (IOW, Do Not Claim You Wrote It)
|*|             
|*|             A "Mention In The Credits", Or Similar Acknowledgement,
|*|             Is *NOT* Required.  It Would Be Nice, Though! :)
|*|             
|*|  Revisions: 
|*|             xx/xx/99 Incorporated The Ability To "Filter" The Input.
|*|	
\*|*/
//	CDropEdit
//	Copyright 1997 Chris Losinger
//
//	This code is freely distributable and modifiable, as long as credit
//	is given to where it's due. Watch, I'll demonstrate :
//
//	shortcut expansion code modified from :
//	CShortcut, 1996 Rob Warner

#include	"StdAfx.h"
#include	"FocusEditCtrl.h"

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

static	COLORREF	g_crATColor = RGB( 0x00, 0x00, 0x00 );	// Active (Focused) Text Color
static	COLORREF	g_crABColor = RGB( 0xFF, 0xFF, 0x00 );	// Active (Focused) Background Color

/////////////////////////////////////////////////////////////////////////////
// CFocusEditCtrl

CFocusEditCtrl::CFocusEditCtrl()	:
	m_bExcludeMask( false ),
	m_bIncludeMask( false )
{
	m_brBGBrush.CreateSolidBrush( g_crABColor );			// Create Active BG Brush

	return;													// Done!
}


CFocusEditCtrl::~CFocusEditCtrl()
{
}


void	CFocusEditCtrl::EnableFileDropping( bool bEnable )
{
	DragAcceptFiles( bEnable ? TRUE : FALSE );				// Set DragAcceptFiles Flag

	return;													// Done!
}


BEGIN_MESSAGE_MAP(CFocusEditCtrl, CEdit)
	//{{AFX_MSG_MAP(CFocusEditCtrl)
	ON_WM_CREATE()
	ON_WM_DROPFILES()
	ON_WM_CTLCOLOR_REFLECT()
	ON_WM_ERASEBKGND()
	ON_CONTROL_REFLECT_EX(EN_KILLFOCUS, OnKillFocusReflect)
	ON_CONTROL_REFLECT_EX(EN_SETFOCUS, OnSetFocusReflect)
	ON_MESSAGE( WM_CUT, OnCut )
	ON_MESSAGE( WM_COPY, OnCopy )
	ON_MESSAGE( WM_PASTE, OnPaste )
	ON_MESSAGE( WM_CLEAR, OnClear )
	ON_MESSAGE( WM_UNDO, OnUndo )
//	ON_MESSAGE( WM_SELECTALL, OnSelectAll )
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFocusEditCtrl message handlers

HBRUSH CFocusEditCtrl::CtlColor(CDC* pDC, UINT /*nCtlColor*/ ) 
{
	DWORD	dwStyle = GetStyle();

	if( ( GetFocus() == this ) && ( !( dwStyle &			// If We Have The Focus
			WS_DISABLED ) ) && ( !( dwStyle & 				// And Control Is Not Disabled
			ES_READONLY ) ) )								// And Control Is Not ReadOnly
	{
		pDC -> SetTextColor( g_crATColor );					// Set Text Color
		pDC -> SetBkColor( g_crABColor );					// Set Background Color

		return( (HBRUSH)m_brBGBrush );						// Return Custom BG Brush
	}
	return( NULL );											// Do Default
}


BOOL CFocusEditCtrl::OnEraseBkgnd(CDC* pDC) 
{
	DWORD	dwStyle = GetStyle();
	CRect	rClient;
	BOOL	bStatus = TRUE;

	if( ( GetFocus() == this ) && ( !( dwStyle &			// If We Have The Focus
			WS_DISABLED ) ) && ( !( dwStyle & 				// And Control Is Not Disabled
			ES_READONLY ) ) )								// And Control Is Not ReadOnly
	{
		GetClientRect( &rClient );							// Get Our Area
		pDC -> FillSolidRect( rClient, g_crABColor );		// Repaint Background
	}
	else
	{
		bStatus = CEdit::OnEraseBkgnd( pDC );				// Do Default
	}
	return( bStatus );										// Return Status
}


BOOL	CFocusEditCtrl::OnKillFocusReflect( void ) 
{
	Invalidate();											// Cause Background To Repaint

	return( FALSE );											// Pass On To Parent
}


BOOL	CFocusEditCtrl::OnSetFocusReflect() 
{
	Invalidate();											// Cause Background To Repaint

	return( FALSE );											// Pass On To Parent
}


int		CFocusEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CEdit::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	DragAcceptFiles(TRUE);
	
	return 0;
}		   


void	CFocusEditCtrl::SetExcludeMask( LPCTSTR cpMask )
{
	if( cpMask )											// If Mask Specified
	{
		//
		//	If This ASSERT Fires, You Need To Unset The Include
		//	Mask Before Setting The Exclude Mask.  Use 
		//	SetIncludeMask( NULL ) To Unset The Include Mask.
		//
		_ASSERTE( !m_bIncludeMask );						// (In)Sanity Check
		m_sExcludeCharMask = cpMask;						// Set It
		m_bExcludeMask = true;								// Set Exclude Mask
	}
	else													// If Mask Not Specified
	{
		m_sExcludeCharMask = afxEmptyString;				// Clear It
		m_bExcludeMask = false;								// Unset Exclude Mask
	}
	return;													// Done!
}


void	CFocusEditCtrl::SetIncludeMask( LPCTSTR cpMask )
{
	if( cpMask )											// If Mask Specified
	{
		//
		//	If This ASSERT Fires, You Need To Unset The Exclude
		//	Mask Before Setting The Include Mask.  Use 
		//	SetExcludeMask( NULL ) To Unset The Exclude Mask.
		//
		_ASSERTE( !m_bExcludeMask );						// (In)Sanity Check
		m_sIncludeCharMask = cpMask;						// Set It
		m_bIncludeMask = true;								// Set Include Mask
	}
	else													// If Mask Not Specified
	{
		m_sIncludeCharMask = afxEmptyString;				// Clear It
		m_bIncludeMask = false;								// Unset Include Mask
	}
	return;													// Done!
}


BOOL	CFocusEditCtrl::PreTranslateMessage(MSG* pMsg) 
{
	if( ( pMsg -> message ) == WM_CHAR )					// If A Char Message
	{
		TCHAR	cKey = (TCHAR)( pMsg -> wParam );			// Get Key (Character In Question)
		
		//
		//	If A Paste Operation...
		//
//		if( ( ( cKey == VK_INSERT ) && ( ::GetAsyncKeyState(
		if( ( ( cKey == VK_INSERT ) && ( ::GetKeyState(
				VK_SHIFT ) & 0x8000 ) ) || ( ( cKey == 
//				'v' ) && ( ::GetAsyncKeyState( 
				'v' ) && ( ::GetKeyState( 
				VK_CONTROL ) & 0x8000 ) ) )					// If A Paste Operation
		{
			Paste();										// Call Paste Handler
			return( TRUE );									// Handled This Message (Eat Keystroke)
		}
		if( ( m_bIncludeMask ) &&							// If Using An Include Mask
				( !m_sIncludeCharMask.IsEmpty() ) )			// And Including Certain Characters
		{
			if( ( cKey != VK_BACK ) && ( !::_tcschr(		// If Not A Backspace And
					m_sIncludeCharMask, cKey ) ) )			// If Key Is Not In The Include Mask
			{										 
				return( TRUE );								// Handled This Message (Eat Keystroke)
			}
		}
		else if( ( m_bExcludeMask ) &&						// If Using An Exclude Mask
				( !m_sExcludeCharMask.IsEmpty() ) )			// And Excluding Certain Characters
		{
			if( ::_tcschr( m_sExcludeCharMask, cKey ) )		// If Key Is In The Exclude Mask
			{										 
				return( TRUE );								// Handled This Message (Eat Keystroke)
			}
		}
	}
	return( CEdit::PreTranslateMessage( pMsg ) );			// Do Default If Not Handled
}


void	CFocusEditCtrl::StripFilteredChars( LPTSTR cpBuffer )
{
	int		iChars = ::_tcslen( cpBuffer );
	int		iSizeofChar = sizeof( TCHAR );

	if( ( m_bIncludeMask ) &&								// If Using An Include Mask
			( !m_sIncludeCharMask.IsEmpty() ) )				// And Including Certain Characters
	{
		TCHAR	*pWhere = NULL;

		pWhere = ::_tcsspnp( cpBuffer, m_sIncludeCharMask );// Look For Characters Not In Include Mask
		while( pWhere )										// While "Bad" Characters Found
		{
			int		iLeft = ( ( ( iChars - 1 ) * 
							iSizeofChar ) - ( pWhere - 
							cpBuffer ) );					// Calculate The Amount Of Memory To Move

			::memmove( pWhere, ( pWhere + 1 ), iLeft );		// Shift Memory Over
			pWhere = ::_tcsspnp( cpBuffer, 
					m_sIncludeCharMask );					// Look For Characters Not In Include Mask
		}
	}
	else if( ( m_bExcludeMask ) &&							// If Using An Exclude Mask
			( !m_sExcludeCharMask.IsEmpty() ) )				// And Excluding Certain Characters
	{
		TCHAR	*pWhere = NULL;

		pWhere = ::_tcspbrk( cpBuffer, m_sExcludeCharMask );// Look For A "Bad" Character
		while( pWhere )										// While "Bad" Characters Found
		{
			int		iLeft = ( ( ( iChars - 1 ) * 
							iSizeofChar ) - ( pWhere - 
							cpBuffer ) );					// Calculate The Amount Of Memory To Move

			::memmove( pWhere, ( pWhere + 1 ), iLeft );		// Shift Memory Over
			pWhere = ::_tcspbrk( cpBuffer, 
					m_sExcludeCharMask );					// Find Next "Bad" Character
		}
	}
	return;													// Done!
}


LRESULT CFocusEditCtrl::OnPaste( WPARAM, LPARAM )
{
	if( OpenClipboard() )									// Open The Clipboard
	{
#if defined( _UNICODE )
		HANDLE	hClipData = ::GetClipboardData( 
						CF_UNICODETEXT );					// Get Unicode Text Data From Clipboard
#else
		HANDLE	hClipData = ::GetClipboardData( CF_TEXT );	// Get ANSI Text Data From Clipboard
#endif
		if( hClipData )										// If Obtained
		{
			LPCTSTR cpText = (LPCTSTR)::GlobalLock( 
							hClipData );					// Lock Memory Into String Pointer
			if( cpText )									// If Pointer To Text Obtained
			{
				TCHAR	*cpBuffer = new TCHAR[ ::GlobalSize( 
								hClipData ) ];				// Allocate Buffer

				if( cpBuffer )								// If Buffer Allocated
				{
					::_tcscpy( cpBuffer, cpText );			// Copy Over The Text
					StripFilteredChars( cpBuffer );			// Remove Any Bad Characters
					ReplaceSel( cpBuffer, TRUE );			// Replace Selection
					
					delete [] cpBuffer;						// Free Buffer
				}
				::GlobalUnlock( (LPVOID)cpText );			// Release Memory Pointer
 			}
		}
		::CloseClipboard();									// Close The Clipboard
	}
	else
	{
		::MessageBeep( MB_ICONEXCLAMATION );				// Beep At User
		TRACE( _T( "FocusEditCtrl: Warning!  Failed To Open The Clipboard For A Paste Operation!\n" ) );
	}
	return( 0 );											// Done!
}


LRESULT CFocusEditCtrl::OnCut( WPARAM, LPARAM )
{
	return( Default() );									// Do Default Behaviour
}


LRESULT CFocusEditCtrl::OnCopy( WPARAM, LPARAM )
{
	return( Default() );									// Do Default Behaviour
}


LRESULT CFocusEditCtrl::OnClear( WPARAM, LPARAM )
{
	return( Default() );									// Do Default Behaviour
}


LRESULT CFocusEditCtrl::OnUndo( WPARAM, LPARAM )
{
	return( Default() );									// Do Default Behaviour
}



/*|*\
|*|  JRT:
|*|  Original code was part of CDropEdit 
|*|  Copyright 1997 Chris Losinger
\*|*/
void	CFocusEditCtrl::OnDropFiles( HDROP hdDropInfo )
{
	UINT		uiFiles = 0;
	TCHAR		*pBuffer = new TCHAR[ _MAX_PATH ];			// Allocate Buffer

	if( ( !pBuffer ) || ( !hdDropInfo ) )					// If No Drop Info Or Buffer
	{
		if( pBuffer )										// If Buffer Was Allocated
		{
			delete [] pBuffer;								// Free Buffer
		}
		return;												// Stop Here
	}				 
	uiFiles = ::DragQueryFile( hdDropInfo, (UINT)-1, NULL,
			0 );											// Get Files Dropped
	//
	//	Note!  Only Interested In The First File!
	//
	if( uiFiles )											// If One Or More Files Dropped
	{													
		::memset( pBuffer, 0, ( sizeof( TCHAR ) * 
				_MAX_PATH ) );								// Init Buffer
		::DragQueryFile( hdDropInfo, 0, pBuffer, 
				_MAX_PATH );								// Get File Path
	}
	::DragFinish( hdDropInfo );								// Done With Drop Info

/*
	CString expandedFile = ExpandShortcut(firstFile);

	// if that worked, we should have a real file name
	if (expandedFile!="") 
		firstFile=expandedFile;
	struct _stat buf;
	// get some info about that file
	int result = _stat( firstFile, &buf );
	if( result == 0 ) {

		// verify that we have a dir (if we want dirs)
		if ((buf.st_mode & _S_IFDIR) == _S_IFDIR) {
			if (m_bUseDir)
				SetWindowText(firstFile);

		// verify that we have a file (if we want files)
		} else if ((buf.st_mode & _S_IFREG) == _S_IFREG) {
			if (!m_bUseDir)
				SetWindowText(firstFile);
		}
	}
*/
	StripFilteredChars( pBuffer );							// Strip The Filtered Characters
	ReplaceSel( pBuffer, TRUE );							// Replace Selection With FilePath Text
	
	delete [] pBuffer;										// Free Buffer

	return;													// Done!
}


/*|*\
|*|  JRT:
|*|  original code was part of CShortcut 
|*|  1996 by Rob Warner
|*|  rhwarner@southeast.net
|*|  http://users.southeast.net/~rhwarner
\*|*/
/*
CString CFocusEditCtrl::ExpandShortcut(CString &inFile)
{
	CString outFile = "";

    // Make sure we have a path
    ASSERT(inFile != _T(""));

    IShellLink* psl;
    HRESULT hres;
    LPTSTR lpsz = inFile.GetBuffer(MAX_PATH);

    // Create instance for shell link
    hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
        IID_IShellLink, (LPVOID*) &psl);
    if (SUCCEEDED(hres))
    {
        // Get a pointer to the persist file interface
        IPersistFile* ppf;
        hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*) &ppf);
        if (SUCCEEDED(hres))
        {
            // Make sure it's ANSI
            WORD wsz[MAX_PATH];
            ::MultiByteToWideChar(CP_ACP, 0, lpsz, -1, wsz, MAX_PATH);

            // Load shortcut
            hres = ppf->Load(wsz, STGM_READ);
            if (SUCCEEDED(hres)) {
				WIN32_FIND_DATA wfd;
				// find the path from that
				HRESULT hres = psl->GetPath(outFile.GetBuffer(MAX_PATH), 
								MAX_PATH,
								&wfd, 
								SLGP_UNCPRIORITY);

				outFile.ReleaseBuffer();
            }
            ppf->Release();
        }
        psl->Release();
    }

	inFile.ReleaseBuffer();

	// if this fails, outFile == ""
    return outFile;
}
*/

#pragma warning (disable : 4018)	// '<':  signed/unsigned mismatch
#pragma warning (disable : 4100)	// unreferenced formal parameter
#pragma warning (disable : 4127)	// conditional expression is constant
#pragma warning (disable : 4244)	// conv from X to Y, possible loss of data
#pragma warning (disable : 4310)	// cast truncates constant value
#pragma warning (disable : 4505)	// X: unreference local function removed
#pragma warning (disable : 4510)	// X: default ctor could not be generated
#pragma warning (disable : 4511)	// X: copy constructor could not be generated
#pragma warning (disable : 4512)	// assignment operator could not be generated
#pragma warning (disable : 4514)	// debug symbol exceeds 255 chars
#pragma warning (disable : 4610)	// union X can never be instantiated
#pragma warning (disable : 4663)	// to explicitly spec class template X use ...
#pragma warning (disable : 4710)	// function 'XXX' not expanded
#pragma	warning	(disable : 4786)	// X: identifier truncated to '255' chars

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
President JRTwine Software, LLC
United States United States
Programming since the age of 10, started professionally at the age of 17. Currently involved in both Client and Server side development on Win32 platforms for MC/HA/FT financial applications. Development experience with Win32, Win16, Linux and other flavors of Unix.

Extensive multithreaded development experience on Windows platforms using the Win32 SDK, and MFC.

Experience with HA/FT n-tiered Client/Server systems as well as GUI apps of varying complexity. Some experience with Game developement.

Having learned that the stuff you can barely get away with doing Client-side apps just does not cut it in the real "Server World", I am amazed how many 'professionals' cannot tell the difference between "works" and "correct" or try to (mis)use VB and/or MFC on server-side development projects, never considering that just because it RUNS, does not mean it runs WELL.

Lastly, I am also a collector of arcade games, and can perform repairs, conversions, etc. Search for my name, you will find me on lots of arcade-related documents and sites.

Sites of interest(?):
http://www.jrtwine.com
http://www.jrtwine.com/jtwine
http://www.signingtime.com
http://www.deletefxpfiles.com
http://www.checkfavorites.com
http://www.coinop.org

Comments and Discussions