/*
 *	@doc INTERNAL
 *
 *	@module	LBHOST.CPP -- Text Host for CreateWindow() Rich Edit 
 *		Combo Box Control | 
 *		Implements CCmbBxWinHost message
 *		
 *	Original Author: 
 *		Jerry Kim
 *
 *	History: <nl>
 *		01/30/97 - v-jerrki Created
 *
 *	Set tabs every four (4) columns
 *
 *	Copyright (c) 1997-1998 Microsoft Corporation. All rights reserved.
 */
#include "_common.h"
#include "_host.h"
#include "imm.h"
#include "_format.h"
#include "_edit.h"
#include "_cfpf.h"
#include "_cbhost.h"

ASSERTDATA

// Helper function in edit.cpp
LONG GetECDefaultHeightAndWidth(
	ITextServices *pts,
	HDC hdc,
	LONG lZoomNumerator,
	LONG lZoomDenominator,
	LONG yPixelsPerInch,
	LONG *pxAveWidth,
	LONG *pxOverhang,
	LONG *pxUnderhang);

// For effeciency and to avoid Winnt thunking layer we will call
// the listbox winproc directly
LRESULT CALLBACK RichListBoxWndProc(
	HWND hwnd,
	UINT msg,
	WPARAM wparam,
	LPARAM lparam);

//////////////////////////// System Window Procs ////////////////////////////
/*
 *	RichComboBoxWndProc (hwnd, msg, wparam, lparam)
 *
 *	@mfunc
 *		Handle window messages pertinent to the host and pass others on to
 *		text services. 
 *	#rdesc
 *		LRESULT = (code processed) ? 0 : 1
 */
LRESULT CALLBACK RichComboBoxWndProc(
	HWND hwnd,
	UINT msg,
	WPARAM wparam,
	LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "RichComboBoxWndProc");

	LRESULT	lres = 1;	//signify we didn't handle the message
	HRESULT hr = S_FALSE;
	CCmbBxWinHost *phost = (CCmbBxWinHost *) GetWindowLongPtr(hwnd, ibPed);

#ifdef DEBUG
	Tracef(TRCSEVINFO, "hwnd %lx, msg %lx, wparam %lx, lparam %lx", hwnd, msg, wparam, lparam);
#endif	// DEBUG

	switch(msg)
	{
	case WM_NCCREATE:
		return CCmbBxWinHost::OnNCCreate(hwnd, (CREATESTRUCT *)lparam);

	case WM_CREATE:
		// We may be on a system with no WM_NCCREATE (e.g. WINCE)
		if (!phost)
		{
			(void) CCmbBxWinHost::OnNCCreate(hwnd, (CREATESTRUCT *) lparam);
			phost = (CCmbBxWinHost *) GetWindowLongPtr(hwnd, ibPed);
		}
		break;
		
	case WM_DESTROY:
		if(phost)
			CCmbBxWinHost::OnNCDestroy(phost);
		return 0;
	}

	if (!phost)
		return ::DefWindowProc(hwnd, msg, wparam, lparam);

	// in certain out-of-memory situations, clients may try to re-enter us 
	// with calls.  Just bail on the call if we don't have a text services
	// pointer.
	if(!phost->_pserv)
		return 0;

	// stabilize ourselves
	phost->AddRef();

	switch(msg)
	{
	case WM_MOUSEMOVE:
		if (!phost->OnMouseMove(wparam, lparam))
			break;
		goto serv;

	case WM_LBUTTONUP:
		if (!phost->OnLButtonUp(wparam, lparam))
			break;
		goto serv;

	case WM_MOUSEWHEEL:
		if (!phost->OnMouseWheel(wparam, lparam))
			break;
		goto defproc;

	case WM_LBUTTONDBLCLK:
	case WM_LBUTTONDOWN:
		if (!phost->OnLButtonDown(wparam, lparam))
			goto Exit;
		goto serv;

	case WM_COMMAND:
		if (!phost->OnCommand(wparam, lparam))
			break;
		goto serv;		

	case WM_CREATE:
		lres = phost->OnCreate((CREATESTRUCT*)lparam);
		break;
	
	case WM_KEYDOWN:
		if (!phost->OnKeyDown((WORD) wparam, (DWORD) lparam))
			break;								
		goto serv;						//  give it to text services		   

	case WM_SETTEXT:
		if (phost->_cbType != CCmbBxWinHost::kDropDown)
		{
			lres = CB_ERR;
			break;
		}
		goto serv;
		
	case WM_GETTEXT:
		GETTEXTEX gt;
		if (W32->OnWin9x() || phost->_fANSIwindow)
			W32->AnsiFilter( msg, wparam, lparam, (void *) &gt );
		goto serv;

	case WM_GETTEXTLENGTH:
		GETTEXTLENGTHEX gtl;
		if (W32->OnWin9x() || phost->_fANSIwindow)
			W32->AnsiFilter( msg, wparam, lparam, (void *) &gtl );
		goto serv;
		
	case WM_CHAR:

		if (W32->OnWin9x() || phost->_fANSIwindow)
		{
			CW32System::WM_CHAR_INFO wmci;
			wmci._fAccumulate = phost->_fAccumulateDBC != 0;
			W32->AnsiFilter( msg, wparam, lparam, (void *) &wmci );
			if (wmci._fLeadByte)
			{
				phost->_fAccumulateDBC = TRUE;
				phost->_chLeadByte = wparam << 8;
				goto Exit;					// Wait for trail byte
			}
			else if (wmci._fTrailByte)
			{
				// UNDONE:
				// Need to see what we should do in WM_IME_CHAR
				wparam = phost->_chLeadByte | wparam;
				phost->_fAccumulateDBC = FALSE;
				phost->_chLeadByte = 0;
				msg = WM_IME_CHAR;
				goto serv;
			}
			else if (wmci._fIMEChar)
			{
				msg = WM_IME_CHAR;
				goto serv;
			}
			else if (wmci._fIMEChar)
			{
				msg = WM_IME_CHAR;
				goto serv;
			}
		}
	
		if(!phost->OnChar((WORD) wparam, (DWORD) lparam))
			// processed code: break out
			break;							
		goto serv;							//  else give it to text services

	case WM_DRAWITEM:
		lres = phost->CbMessageItemHandler(NULL, ITEM_MSG_DRAWLIST, wparam, lparam);
		if (lres)
			break;
		goto defproc;

	case WM_DELETEITEM:
		lres = phost->CbMessageItemHandler(NULL, ITEM_MSG_DELETE, wparam, lparam);
		if (lres)
			break;
		goto defproc;		

	case WM_ENABLE:
		if (phost->OnEnable(wparam, lparam))
		{
			if(!wparam ^ phost->_fDisabled)
			{
				// Stated of window changed so invalidate it so it will
				// get redrawn.
				InvalidateRect(phost->_hwnd, NULL, TRUE);
				phost->SetScrollBarsForWmEnable(wparam);

				// Need to enable the listbox window
				::EnableWindow(phost->_hwndList, wparam);
			}
			phost->_fDisabled = !wparam;				// Set disabled flag
			lres = 0;							// Return value for message
		}
											// Fall thru to WM_SYSCOLORCHANGE?
	case WM_SYSCOLORCHANGE:
		//forward message to listbox first then pass to textservice
		SendMessage(phost->_hwndList, msg, wparam, lparam);
		phost->OnSysColorChange();
		goto serv;							// Notify text services that
											//  system colors have changed
	case WM_GETDLGCODE:
		//forward message to listbox first then pass to textservice
		SendMessage(phost->_hwndList, msg, wparam, lparam);
		lres = phost->OnGetDlgCode(wparam, lparam);
		break;

    case WM_STYLECHANGING:
		// Just pass this one to the default window proc
		lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
		break;
		
	case WM_SIZE:
		lres = phost->OnSize(wparam, lparam);
		break;

	case WM_SETCURSOR:
		//	Only set cursor when over us rather than a child; this
		//	helps prevent us from fighting it out with an inplace child
		if((HWND)wparam == hwnd)
		{
			if(!(lres = ::DefWindowProc(hwnd, msg, wparam, lparam)))
				lres = phost->OnSetCursor(wparam, lparam);
		}
		break;

	case WM_SHOWWINDOW:
		hr = phost->OnTxVisibleChange((BOOL)wparam);
		break;

	case WM_NCPAINT:
		lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
		if(phost->TxGetEffects() == TXTEFFECT_SUNKEN && dwMajorVersion < VERS4)
		{
			HDC hdc = GetDC(hwnd);
			if(hdc)
			{
				phost->DrawSunkenBorder(hwnd, hdc);
				ReleaseDC(hwnd, hdc);
			}
		}		
		break;

	case WM_PAINT:
		lres = phost->OnPaint(wparam, lparam);
		break;

	case WM_KILLFOCUS:
		lres = phost->OnKillFocus(wparam, lparam);
		if (!lres)
			goto serv;
		goto defproc;

	case LBCB_TRACKING:
		// release any mousedown stuff
		phost->OnLButtonUp(0, 0);
		phost->_fFocus = 1;
		phost->_fLBCBMessage = 1;
		// Fall through case!!!
		
	case WM_SETFOCUS:
		lres = phost->OnSetFocus(wparam, lparam);		
		if (lres)
			goto defproc;
		goto serv;

	case WM_SYSKEYDOWN:
		if (phost->OnSyskeyDown((WORD)wparam, (DWORD)lparam))
			goto serv;
		break;

	case WM_CAPTURECHANGED:
		if (!phost->OnCaptureChanged(wparam, lparam))
			goto serv;
		break;
		
	//bug fix #4076
	case CB_GETDROPPEDSTATE:
		lres = phost->_fListVisible;
		goto Exit;

	// combo box messages
	case CB_GETEXTENDEDUI:
		lres = phost->CbGetExtendedUI();
		break;

	case CB_SETEXTENDEDUI:
		lres = phost->CbSetExtendedUI(wparam);
		break;
	
    case CB_SETITEMHEIGHT:
		lres = phost->CbSetItemHeight((wparam == (unsigned)-1) ? TRUE : FALSE, (int)lparam);
		break;

	case CB_GETITEMHEIGHT:
		lres = phost->CbGetItemHeight((wparam == (unsigned)-1) ? TRUE : FALSE);
		break;

// Listbox specific messages
    case CB_DELETESTRING:
    	msg = LB_DELETESTRING;
    	goto deflstproc;

    case CB_SETTOPINDEX:
    	msg = LB_SETTOPINDEX;
    	goto deflstproc;

    case CB_GETTOPINDEX:
    	msg = LB_GETTOPINDEX;
    	goto deflstproc;
 
    case CB_GETCOUNT:
    	msg = LB_GETCOUNT;
    	goto deflstproc;
    	
    case CB_GETCURSEL:
    	msg = LB_GETCURSEL;
    	goto deflstproc;
    	
    case CB_GETLBTEXT:
    	msg = LB_GETTEXT;
    	goto deflstproc;
    	
    case CB_GETLBTEXTLEN:
    	msg = LB_GETTEXTLEN;
    	goto deflstproc;
    	
    case CB_INSERTSTRING:
    	msg = LB_INSERTSTRING;
    	goto deflstproc;
    	
    case CB_RESETCONTENT:
    	msg = LB_RESETCONTENT;
    	goto deflstproc;

    case CB_FINDSTRING:
    	msg = LB_FINDSTRING;
    	goto deflstproc;

    case CB_FINDSTRINGEXACT:
    	msg = LB_FINDSTRINGEXACT;
    	goto deflstproc;

    case CB_SELECTSTRING:
    	//bug fix
    	// The system control does 2 things here.  1) selects the requested item
    	// 2) sets the newly selected item to the top of the list
    	lres = CB_ERR;
    	if (phost->_hwndList)
    	{
    		lres = RichListBoxWndProc(phost->_hwndList, LB_SELECTSTRING, wparam, lparam);
    		phost->UpdateEditBox();
    	}
    	break;    	

    case CB_GETITEMDATA:
    	msg = LB_GETITEMDATA;
    	goto deflstproc;

    case CB_SETITEMDATA:
    	msg = LB_SETITEMDATA;
    	goto deflstproc;

    case CB_SETCURSEL:
    	//bug fix
    	// The system control does 2 things here.  1) selects the requested item
    	// 2) sets the newly selected item to the top of the list
    	if (phost->_hwndList)
    	{
    		lres = RichListBoxWndProc(phost->_hwndList, LB_SETCURSEL, wparam, lparam);
    		if (lres != -1)
    			RichListBoxWndProc(phost->_hwndList, LB_SETTOPINDEX, wparam, 0);
    		phost->UpdateEditBox();
    	}
    	break;

	case CB_ADDSTRING:
		msg = LB_ADDSTRING;
		goto deflstproc;

// edit box specific messages
    case CB_GETEDITSEL:
		msg = EM_GETSEL;
		goto serv;

    case CB_LIMITTEXT:
		msg = EM_SETLIMITTEXT;
		goto serv;    	
    
    case CB_SETEDITSEL:
    	if (phost->_cbType == CCmbBxWinHost::kDropDownList)
    	{
    	    lres = CB_ERR;
    		break;
    	}
    	msg = EM_SETSEL;
		// When we are in a dialog box that is empty, EM_SETSEL will not select
		// the final always existing EOP if the control is rich.
		if (phost->_fUseSpecialSetSel &&
			((CTxtEdit *)phost->_pserv)->GetAdjustedTextLength() == 0 &&
			wparam != -1)
		{
			lparam = 0;
			wparam = 0;
		}
		else
		{			
			//parameters are different between CB and EM messages
			wparam = (WPARAM)(signed short)LOWORD(lparam);
			lparam = (LPARAM)(signed short)HIWORD(lparam);
		}
		goto serv;

	
	case EM_SETMARGINS:  //PPT uses this message for the combo box. bug fix #4072
		// We need to keep track of the margins size because we have a minimum inset
		// value bug fix #4659
		if (wparam & EC_LEFTMARGIN)
			phost->_dxLOffset = LOWORD(lparam);
		if (wparam & EC_RIGHTMARGIN)
			phost->_dxROffset = HIWORD(lparam);
		phost->OnSetMargins(wparam, LOWORD(lparam) + phost->_dxLInset, 
			HIWORD(lparam) + phost->_dxRInset);
		break;
		
	case EM_GETOPTIONS:
		lres = phost->OnGetOptions();
		break;
		
	case EM_SETOPTIONS:
		phost->OnSetOptions((WORD) wparam, (DWORD) lparam);
		lres = (phost->_dwStyle & ECO_STYLES);
		if(phost->_fEnableAutoWordSel)
			lres |= ECO_AUTOWORDSELECTION;
		break;

	case EM_HIDESELECTION:
		if(lparam)
		{
			DWORD dwPropertyBits = 0;

			phost->_dwStyle |= ES_NOHIDESEL;
			if(wparam)
			{
				phost->_dwStyle &= ~ES_NOHIDESEL;
				dwPropertyBits = TXTBIT_HIDESELECTION;
			}

			// Notify text services of change in status.
			phost->_pserv->OnTxPropertyBitsChange(TXTBIT_HIDESELECTION, 
				dwPropertyBits);
		}
		goto serv;

	case EM_GETPASSWORDCHAR:
#ifndef NOACCESSIBILITY    
		lres = 0;
		break;
#endif

	// We should ignore any EM_ messages which we don't handle ourselves
	case EM_SETPALETTE:
	case EM_GETRECT:
	case EM_SETBKGNDCOLOR:
	case EM_SETPASSWORDCHAR:
	case EM_SETREADONLY:
	case EM_SETRECTNP:							
	case EM_SETRECT:	
    case CB_GETDROPPEDCONTROLRECT:
    case CB_SETDROPPEDWIDTH:
    case CB_GETDROPPEDWIDTH:
    case CB_INITSTORAGE:
    case CB_GETHORIZONTALEXTENT:
    case CB_SETHORIZONTALEXTENT:        
    case CB_SETLOCALE:
    case CB_GETLOCALE:
		AssertSz(FALSE, "Message not supported");
		//FALL THROUGH!!!

	case WM_STYLECHANGED:
		break;

	case EM_SETTEXTEX:
		phost->OnSetTextEx(wparam, lparam);
		break;

    case CB_SHOWDROPDOWN:
        if (wparam && !phost->_fListVisible)
        {
            phost->ShowListBox(TRUE);
        }
        else if (!wparam && phost->_fListVisible)
        {
            phost->HideListBox(TRUE, FALSE);
        }
        break;

#ifndef NOACCESSIBILITY        
	case WM_GETOBJECT:	
		IUnknown* punk;
		phost->QueryInterface(IID_IUnknown, (void**)&punk);
		Assert(punk);
		lres = W32->LResultFromObject(IID_IUnknown, wparam, (LPUNKNOWN)punk);
		AssertSz(!FAILED((HRESULT)lres), "WM_GETOBJECT message FAILED\n");
		punk->Release();
		break;
#endif		
		
	default:
		//CTxtWinHost message handler
serv:
		hr = phost->_pserv->TxSendMessage(msg, wparam, lparam, &lres);

defproc:
		if(hr == S_FALSE)
		{			
			// Message was not processed by text services so send it
			// to the default window proc.
			lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
		}

		// Need to do some things after we send the message to ITextService
		switch (msg)
		{
		case EM_SETSEL:
			phost->_pserv->TxSendMessage(EM_HIDESELECTION, 0, 0, NULL);
			lres = 1;
			break;

		// Need to return 1 per SDK documentation
		case EM_SETLIMITTEXT:
			lres = 1;
			break;

		case WM_SETFONT:
		{
			// Special border processing. The inset changes based on the size of the
			// defautl character set. So if we got a message that changes the default
			// character set, we need to update the inset.
			// Update our font height member variable with the new fonts height
			// Get the inset information
			HDC hdc = GetDC(hwnd);
			LONG xAveCharWidth = 0;
			LONG yCharHeight = GetECDefaultHeightAndWidth(phost->_pserv, hdc, 1, 1,
				W32->GetYPerInchScreenDC(), &xAveCharWidth, NULL, NULL);
			ReleaseDC(hwnd, hdc);

			if (yCharHeight)
				phost->_dyFont = yCharHeight;

			// force a recalculation of the edit control
			phost->_dyEdit = 0;
			phost->CbCalcControlRects(&phost->_rcWindow, TRUE);

			// force a resize of the control
			phost->_fListVisible = 1;
			phost->HideListBox(FALSE, FALSE);
		}
			goto deflstproc;
			
		case EM_FORMATRANGE:
		case EM_SETPARAFORMAT:
		case EM_SETCHARFORMAT:
		case EM_SETLANGOPTIONS:
		case EM_SETBIDIOPTIONS:
		case EM_SETTYPOGRAPHYOPTIONS:
			goto deflstproc;			
		}
		break;

deflstproc:
		//CLstBxWinHost message handler
		Assert(phost->_hwndList);
		if (phost->_hwndList)
		{
			lres = SendMessage(phost->_hwndList, msg, wparam, lparam);
			
			switch (msg)
			{
			case LB_RESETCONTENT:
				//need to remove the content from the edit box
				phost->_pserv->TxSendMessage(WM_SETTEXT, wparam, NULL, &lres);
				break;

			case LB_SETCURSEL:
				// need to update the edit control
				phost->UpdateEditBox();
				break;	
			}
		}
		break;				
	}	

Exit:
	phost->Release();
	return lres;
}


//////////////// CCmbBxWinHost Creation/Initialization/Destruction ///////////////////////
#ifndef NOACCESSIBILITY
/*
 *	CCmbBxWinHost::QueryInterface(REFIID riid, void **ppv)
 *
 *	@mfunc
 *		
 */
HRESULT CCmbBxWinHost::QueryInterface(REFIID riid, void **ppv)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::QueryInterface");
  
  	if(riid == IID_IAccessible)
		*ppv = (IAccessible*)this;
    else if (riid == IID_IDispatch)
		*ppv = (IDispatch*)(IAccessible*)this;
    else if (IsEqualIID(riid, IID_IUnknown))
		*ppv = (IUnknown*)(IAccessible*)this;
    else
        return CTxtWinHost::QueryInterface(riid, ppv);

	AddRef();		
	return NOERROR;
}
#endif

/*
 *	CCmbBxWinHost::OnNCCreate (hwnd, pcs)
 *
 *	@mfunc
 *		Static global method to handle WM_NCCREATE message (see remain.c)
 */
LRESULT CCmbBxWinHost::OnNCCreate(
	HWND hwnd,
	const CREATESTRUCT *pcs)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnNCCreate");


	CCmbBxWinHost *phost = new CCmbBxWinHost();

	if (!phost)
	{
		// Allocation failure.
		return 0;
	}

	if(!phost->Init(hwnd, pcs))					// Stores phost in associated
	{											//  window data
		phost->Shutdown();
		delete phost;
		return 0;
	}
	return TRUE;
}

/*
 *	CCmbBxWinHost::OnNCDestroy (phost)
 *
 *	@mfunc
 *		Static global method to handle WM_NCCREATE message
 *
 *	@devnote
 *		phost ptr is stored in window data (GetWindowLongPtr())
 */
void CCmbBxWinHost::OnNCDestroy(
	CCmbBxWinHost *phost)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnNCDestroy");

	// NOTE:
	//	We have to be careful when we destroy the window because there can be cases
	// when we have a valid hwnd but no host for the hwnd so we have to check for
	// both cases

	if (phost->_plbHost)
	{
		// ALERT!! :The DestroyWindow function does not send the WM_NCDESTROY message for Windows CE		
		phost->_plbHost->Release();
	}
		
	// Destroy list box here so we will get the WM_DELETEITEM before the
	// combo box gets destroyed
	if (phost->_hwndList)
		DestroyWindow(phost->_hwndList);

	phost->Shutdown();
	phost->Release();
	
}

/*
 *	CCmbBxWinHost::CCmbBxWinHost()
 *
 *	@mfunc
 *		constructor
 */
CCmbBxWinHost::CCmbBxWinHost(): CTxtWinHost(), _plbHost(NULL), _hwndList(NULL), _hcurOld(NULL)
{
	_dxLInset = _dxRInset = 0;
	_fIgnoreUpdate = 0;
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CTxtWinHost");
}

/*
 *	CCmbBxWinHost::~CCmbBxWinHost()
 *
 *	@mfunc
 *		destructor
 */
CCmbBxWinHost::~CCmbBxWinHost()
{
}

/*
 *	CCmbBxWinHost::Init (hwnd, pcs)
 *
 *	@mfunc
 *		Initialize this CCmbBxWinHost
 */
BOOL CCmbBxWinHost::Init(
	HWND hwnd,					//@parm Window handle for this control
	const CREATESTRUCT *pcs)	//@parm Corresponding CREATESTRUCT
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::Init");

	if(!pcs->lpszClass)
		return -1;
		
	_fRightAlign = 0;
	_fListVisible = 0;
	_fOwnerDraw = 0;
	_fFocus = 0;
	_fMousedown = 0;
	_cyList = 0;
	_fDisabled = 0;
	_fNoIntegralHeight = 0;
	_idCtrl = (UINT)(DWORD_PTR) pcs->hMenu;
	_fKeyMaskSet = 0;
	_fMouseMaskSet = 0;
	_fScrollMaskSet = 0;
	_fMousedown = 0;
	_nCursor = -2;
	_fExtendedUI = 0;
	_fLBCBMessage = 0;
	_dxROffset = _dxLOffset = 0;

	// Set pointer back to CCmbBxWinHost from the window
	if(hwnd)
		SetWindowLongPtr(hwnd, ibPed, (INT_PTR)this);
		
	_hwnd = hwnd;

	if(pcs)
	{
		_hwndParent = pcs->hwndParent;
		_dwExStyle	= pcs->dwExStyle;
		_dwStyle	= pcs->style;

		// We need to change our Extended because we don't support most of them
		DWORD dwExStyle = _dwExStyle & (WS_EX_LEFTSCROLLBAR | WS_EX_TOPMOST | WS_EX_RIGHT |
							WS_EX_RTLREADING | WS_EX_CLIENTEDGE); 
		//	NOTE:
		//	  The order in which we check the style flags immulate
		//	WinNT's order.  So please verify with NT order before
		//	reaaranging order.
		if (_dwStyle & CBS_DROPDOWN)
		{
			_cbType = kDropDown;
			if (_dwStyle & CBS_SIMPLE)
				_cbType = kDropDownList;
		}
		else
		{
			AssertSz(FALSE, "CBS_SIMPLE not supported");
		}

		if (_dwStyle & CBS_OWNERDRAWFIXED)
			_fOwnerDraw = 1;
			
		if (_dwStyle & WS_DISABLED)
			_fDisabled = 1;

		if (_dwStyle & CBS_NOINTEGRALHEIGHT)
			_fNoIntegralHeight = 1;

		// the combobox doesn't support ES_RIGHT because its value is the 
		// same as CBS_DROPDOWN!!
		if (_dwExStyle & WS_EX_RIGHT)
		{
			_fRightAlign = 1;
			_dwStyle |= ES_RIGHT;
		}

		// implicitly set the ES_AUTOHSCROLL style bit
		_dwStyle |= ES_AUTOHSCROLL;				
		_dwStyle &= ~ES_AUTOVSCROLL;

		// If we have any kind of border it will always be a 3d border
		if (_dwStyle & WS_BORDER || _dwExStyle & WS_EX_CLIENTEDGE)
		{
			_fBorder = 1;
			_dwStyle &= ~WS_BORDER;
			_dwExStyle |= WS_EX_CLIENTEDGE;
			dwExStyle |= WS_EX_CLIENTEDGE;
		}

		// handle default disabled
		if(_dwStyle & WS_DISABLED)
			_fDisabled = TRUE;

		DWORD dwStyle = _dwStyle;
		// Remove the verticle scroll style for the window
		if (_dwStyle & WS_VSCROLL)
			dwStyle &= ~WS_VSCROLL;

        // Set the window styles
        SetWindowLong(_hwnd, GWL_STYLE, dwStyle);
        SetWindowLong(_hwnd, GWL_EXSTYLE, dwExStyle);
	}

	// Create Text Services component
	if(FAILED(CreateTextServices()))
		return FALSE;

	_xInset = 1;
	_yInset = 1;

	PARAFORMAT PF2;	
	PF2.dwMask = 0;
	if(_dwExStyle & WS_EX_RIGHT)
	{
		PF2.dwMask |= PFM_ALIGNMENT;
		PF2.wAlignment = (WORD)(PFA_RIGHT);	// right or center-aligned
	}

	if(_dwExStyle & WS_EX_RTLREADING)
	{
		PF2.dwMask |= PFM_RTLPARA;
		PF2.wEffects = PFE_RTLPARA;		// RTL reading order
	}

	if (PF2.dwMask)
	{
		PF2.cbSize = sizeof(PARAFORMAT2);
		//  tell text services
		_pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&PF2, NULL);
	}
	
	PARAFORMAT PF;							// If left or right alignment,
	if(_fRightAlign)				//  tell text services
	{
		PF.cbSize = sizeof(PARAFORMAT);
		PF.dwMask = PFM_ALIGNMENT;
		PF.wAlignment = (WORD)PFA_RIGHT;
		_pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&PF, NULL);
	}

	//bug fix #4644 we want the EN_CHANGE and EN_UPDATE notifications
	_pserv->TxSendMessage(EM_SETEVENTMASK, 0, ENM_UPDATE | ENM_CHANGE, NULL);

	// Tell textservices to turn-on auto font sizing
	_pserv->TxSendMessage(EM_SETLANGOPTIONS, 0, 
			IMF_AUTOKEYBOARD | IMF_AUTOFONT | IMF_AUTOFONTSIZEADJUST | IMF_UIFONTS |
			IMF_IMEALWAYSSENDNOTIFY, NULL);
			
	
	return TRUE;
}


/*
 *	CCmbBxWinHost::OnCreate (pcs)
 *
 *	@mfunc
 *		Handle WM_CREATE message
 *
 *	@rdesc
 *		LRESULT = -1 if failed to in-place activate; else 0
 */
LRESULT CCmbBxWinHost::OnCreate(const CREATESTRUCT *pcs)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnCreate");

	RECT rcClient;

	// sometimes, these values are -1 (from windows itself); just treat them
	// as zero in that case
	LONG cy = (pcs->cy < 0) ? 0 : pcs->cy;
	LONG cx = (pcs->cx < 0) ? 0 : pcs->cx;

	rcClient.top = pcs->y;
	rcClient.bottom = rcClient.top + cy;
	rcClient.left = pcs->x;
	rcClient.right = rcClient.left + cx;

	// Notify Text Services that we are in place active
	if(FAILED(_pserv->OnTxInPlaceActivate(&rcClient)))
		return -1;	

	// Get the font height to base the control heights from
	// Initially the font height is the item height	
	HDC hdc = GetDC(_hwnd);	
	LONG xAveCharWidth = 0;
	_dyFont = GetECDefaultHeightAndWidth(_pserv, hdc, 1, 1,
		W32->GetYPerInchScreenDC(), &xAveCharWidth, NULL, NULL);
	Assert(_dyFont != 0); // _yInset should be zero since listbox's doesn't have yinsets

	ReleaseDC(_hwnd, hdc);
	
	
	// init variables
	_idCtrl = (UINT)(DWORD_PTR)pcs->hMenu;

	// Need to calculate the rects of EVERYTHING!!
	// Force a request of itemHeight
	_rcButton.left = 0;
	_dyEdit = 0;
	_cyList = -1;
	CbCalcControlRects(&rcClient, TRUE);

	// Now lets handle the listbox stuff!
	// create and tranlate styles for combo box to listbox
	DWORD lStyle = WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_NOTIFY | LBS_COMBOBOX | WS_CLIPSIBLINGS;
	if (_dwStyle & CBS_HASSTRINGS)
		lStyle |= LBS_HASSTRINGS;

	if (_dwStyle & CBS_SORT)
		lStyle |= LBS_SORT;

	if (_dwStyle & CBS_DISABLENOSCROLL)
		lStyle |= LBS_DISABLENOSCROLL;

	if (_dwStyle & CBS_NOINTEGRALHEIGHT)
		lStyle |= LBS_NOINTEGRALHEIGHT;

	if (_dwStyle & CBS_OWNERDRAWFIXED)
	{
		_fOwnerDraw;
		lStyle |= LBS_OWNERDRAWFIXED;
	}

	// copy over some window styles
	lStyle |= (_dwStyle & WS_DISABLED);
	lStyle |= (_dwStyle & WS_VSCROLL);
	
	DWORD lExStyle = _dwExStyle & (WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);

	//NOTE. It doesn't matter if the listbox is made with the correct size since
	// it's going to get resized anyways
	if (!W32->OnWin9x())
	{
		//WinNT
		_hwndList = ::CreateWindowExW(lExStyle | WS_EX_TOOLWINDOW, L"REListBox20W", 
					NULL, lStyle, _rcList.left, _rcList.top, _rcList.right - _rcList.left,
					_rcList.bottom - _rcList.top, _hwnd, (HMENU)CB_LISTBOXID, NULL, this);
	}
	else
	{
		// Win '95, '98 system
		_hwndList = ::CreateWindowExA(lExStyle | WS_EX_TOOLWINDOW, "REListBox20W", 
					NULL, lStyle, _rcList.left, _rcList.top, _rcList.right - _rcList.left,
					_rcList.bottom - _rcList.top, _hwnd, (HMENU)CB_LISTBOXID, NULL, this);
	}
	
	Assert(_hwndList);
	_plbHost = (CLstBxWinHost *) GetWindowLongPtr(_hwndList, ibPed);
	Assert(_plbHost);
	if (!_plbHost)
		return -1;
		
	// increment reference counter!
	_plbHost->AddRef();

	if (_cbType != kSimple)
		ShowWindow(_hwndList, SW_HIDE);
	SetParent(_hwndList, NULL);
				
	if (_cbType == kDropDownList)
	{			
		AssertSz(!((CTxtEdit*)_pserv)->_fReadOnly, "edit is readonly");
		
		// Tell textservices to select the entire background
		_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR, NULL);	

		// format the paragraph to immulate the system control
		PARAFORMAT2 pf;
		pf.cbSize = sizeof(PARAFORMAT2);
		pf.dwMask = PFM_STARTINDENT;
		pf.dxStartIndent = (1440.0 / W32->GetXPerInchScreenDC());
		_pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&pf, NULL);
		_usIMEMode = ES_NOIME;
		// Tell textservices to turnoff ime
		_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_NOIME, SES_NOIME, NULL);	

	}
	else
	{
		// make the richedit control behave like the edit control		
		_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_EMULATESYSEDIT, SES_EMULATESYSEDIT, NULL);
	}

	// Need to resize the list box
	if (_cbType != kSimple)
		SetDropSize(&_rcList);

	return 0;
}


/////////////////////////// CCmbBxWinHost Helper functions /////////////////////////////////
/*
 *	CCmbBxWinHost::GetTextLength ()
 *
 *	@mfunc
 *		returns the text length of the edit control using CR and NOT CRLF
 *
 *	@rdesc
 *		LRESULT = text length
 */
 LRESULT CCmbBxWinHost::GetTextLength()
 {
 	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::GetTextLength");
 	
 	LRESULT lr = 0;
	GETTEXTLENGTHEX gtl;
	gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
	gtl.codepage = 1200;

#ifdef DEBUG
	HRESULT hr = _pserv->TxSendMessage(EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0, &lr);
	Assert(hr == NOERROR);
#else
	_pserv->TxSendMessage(EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0, &lr);
#endif
	return lr;
 }

/*
 *	CCmbBxWinHost::GetEditText (LPTSTR, int)
 *
 *	@mfunc
 *		returns the text length in the edit control in UNICODE
 *
 *	@rdesc
 *		LRESULT = text length copied to passed in buffer
 */
 LRESULT CCmbBxWinHost::GetEditText (LPTSTR szStr, int nSize)
 {
 	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::GetEditText");

	LRESULT lr = 0;
 	GETTEXTEX gt;
	gt.cb = nSize * sizeof(TCHAR);
	gt.flags = 0;
	gt.codepage = 1200;
	gt.lpDefaultChar = NULL;
	gt.lpUsedDefChar = NULL;

#ifdef DEBUG
	HRESULT hr = _pserv->TxSendMessage(EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)szStr, &lr);
	Assert(hr == NOERROR);
#else
	_pserv->TxSendMessage(EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)szStr, &lr);
#endif
	return lr;
 }
 
 
/*
 *	CCmbBxWinHost::SetDropSize(RECT* prc)
 *
 *	@mfunc
 *		Compute the drop down window's width and max height
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
void CCmbBxWinHost::SetDropSize(RECT* prc)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::SetDropSize");

	_fListVisible = TRUE;
	HideListBox(FALSE, FALSE);
	POINT pt1 = {prc->left, prc->top};
	POINT pt2 = {prc->right, prc->bottom};
	::ClientToScreen(_hwnd, &pt1);
	::ClientToScreen(_hwnd, &pt2);
	MoveWindow(_hwndList, pt1.x, pt1.y, pt2.x - pt1.x,
			pt2.y - pt1.y, FALSE);

}

/*
 *	CCmbBxWinHost::SetSizeEdit(int nLeft, int nTop, int nRight, int nBottom)
 *
 *	@mfunc
 *		sets the edit controls size
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
void CCmbBxWinHost::SetSizeEdit(int nLeft, int nTop, int nRight, int nBottom)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::SizeEdit");

	// Generate default view rect from client rect
	if(_fBorder)
	{
		// Factors in space for borders
  		_rcViewInset.top	= W32->DYtoHimetricY(nTop, W32->GetYPerInchScreenDC());
   		_rcViewInset.bottom	= W32->DYtoHimetricY(nBottom, W32->GetYPerInchScreenDC());
   		_rcViewInset.left	= W32->DXtoHimetricX(nLeft, W32->GetXPerInchScreenDC());
   		_rcViewInset.right	= W32->DXtoHimetricX(nRight, W32->GetXPerInchScreenDC());
	}
	else
	{
		// Default the top and bottom inset to 0 and the left and right
		// to the size of the border.
		_rcViewInset.top = 0;
		_rcViewInset.bottom = 0;
		_rcViewInset.left = W32->DXtoHimetricX(nLeft, W32->GetXPerInchScreenDC());
		_rcViewInset.right = W32->DXtoHimetricX(nRight, W32->GetXPerInchScreenDC());
	}
}

/*
 *	CCmbBxWinHost::CbCalcControlRects(RECT* prc, BOOL bCalcChange)
 *
 *	@mfunc
 *		Calculates the RECT for all the controls.  The rect should
 *	include the non-client area's also
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
BOOL CCmbBxWinHost::CbCalcControlRects(RECT* prc, BOOL bCalcChange)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbCalcControlRects");

	// copy over the window rect
	_rcWindow = *prc;

	// Item specific things
	const int smY = GetSystemMetrics(SM_CYEDGE);
	const int smX = GetSystemMetrics(SM_CXEDGE);
	
	_cxCombo = _rcWindow.right - _rcWindow.left;

	if (!_dyEdit)
		_dyEdit = _dyFont + 2 + ((_fBorder) ? (2 * _yInset) : 0);
	
	if (_fOwnerDraw)
	{		
		if (bCalcChange)
		{
            // No height has been defined yet for the static text window.  Send
            // a measure item message to the parent
			MEASUREITEMSTRUCT mis;
            mis.CtlType = ODT_COMBOBOX;
            mis.CtlID = _idCtrl;
            mis.itemID = (UINT)-1;
            mis.itemHeight = _dyEdit;
            mis.itemData = 0;

            SendMessage(_hwndParent, WM_MEASUREITEM, _idCtrl, (LPARAM)&mis);
			_dyEdit = mis.itemHeight;
        }
	}
	else
	{
		// NOTE:
		//	Richedit prevents us from trying to set the itemHeight less than the 
		// font height so we need to take account of this by preventing user from
		// setting height less than font height
		int nyEdit = _dyFont + ((_fBorder) ? 2 * _yInset : 0);
		if (_dyEdit > nyEdit)
		{
			//In order for the highlighting to work properly we need to empty
			//the richedit control
			LRESULT nLen;
			_pserv->TxSendMessage(WM_GETTEXTLENGTH, 0, 0, &nLen);

			TCHAR* pwch = NULL;
			if (nLen && _cbType == kDropDownList)
			{
				pwch = new TCHAR[nLen + 1 /*NULL*/];				
				AssertSz(pwch, "Unable to allocate memory for string");

				if (pwch)
				{				
					// Get the text from richedit and emtpy it
					_pserv->TxSendMessage(WM_GETTEXT, nLen + 1, (LPARAM)pwch, NULL);
					_pserv->TxSendMessage(WM_SETTEXT, 0, NULL, NULL);
				}
				else
				{
					// something bad happened so send a message
					// to client
					TxNotify(EN_ERRSPACE, NULL);	
				}
			}
			else if (_cbType == kDropDown && nLen == 0)
			{
				// we need to insert a dummy character into the richedit
				// control so it won't try to highlight space after
				// the paragraph
				_pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)L" ", NULL);
			}
					
		 	// Calculate the difference in size
		 	nyEdit = _dyEdit - nyEdit;				 	

			PARAFORMAT2 pf;			
			pf.cbSize = sizeof(PARAFORMAT2);
			pf.dwMask = PFM_SPACEAFTER;
			pf.dySpaceAfter = (int)(((double)nyEdit * 1440.0) / (double)W32->GetYPerInchScreenDC());
			_pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&pf, NULL);

			//Reset the text which was there before in the richedit control
			if (pwch || (_cbType == kDropDown && nLen == 0))
			{
				_pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)(pwch ? pwch : NULL), NULL);
				if (pwch)
					delete pwch;
			}
		}
		else
			_dyEdit = nyEdit;	// stabalize ourselves
	}

	// For Bordered Combobox we take account of the clientedge for the top
	// and bottom. And since we want to draw the focus rect within the yellow
	// area we need to subtract 1.
	_cyCombo = min(_dyEdit + ((_fBorder) ? 2 * smY : 0), 
				_rcWindow.bottom - _rcWindow.top); 
	
	// recompute the max height of the dropdown listbox -- full window
    // size MINUS edit/static height
    if (_cyList == -1)        
        _cyList = max((_rcWindow.bottom - _rcWindow.top) - _cyCombo, 0);

	// calculate the rect for the buttons
	if (_cbType != kSimple)
	{
		_rcButton.top = 0;
		_rcButton.bottom = min(_dyEdit, _rcWindow.bottom - _rcWindow.top);
		if (_fRightAlign)
		{
			_rcButton.left = 0;
			_rcButton.right = _rcButton.left + GetSystemMetrics(SM_CXVSCROLL);
		}
		else
		{
			_rcButton.right = _cxCombo - ((_fBorder) ? (2 * smX): 0);
			_rcButton.left = _rcButton.right - GetSystemMetrics(SM_CXVSCROLL);
		}
	}


	// calculate the edit control rect	
	int nTop = _yInset;
	int nBottom = 0;
	_dxLInset = _xInset;
	_dxRInset = _xInset;	
	if (_cbType != kSimple)
	{
		if (_fRightAlign)
			_dxLInset = (_rcButton.right - _rcButton.left) + smX;
		else
			_dxRInset = (_rcButton.right - _rcButton.left) + smX;
	}
	SetSizeEdit(_dxLInset + _dxLOffset, nTop, _dxRInset + _dxROffset, nBottom);

	// calculate the rect for the list box window
	_rcList.left = (_fBorder) ? - smX : 0;
	_rcList.top = _cyCombo - ((_fBorder) ? smY : 0);
	_rcList.right = (_fBorder) ? max(_cxCombo - smX, 0) : _rcWindow.right;
	_rcList.bottom = _cyCombo + _cyList;	

	return TRUE;
}


/*
 *	CCmbBxWinHost::DrawButton(HDC, BOOL)
 *
 *	@mfunc
 *		Draws the combo box button given an hdc
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
 void CCmbBxWinHost::DrawButton(HDC hdc, BOOL bDown)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::DrawButton");

	// Check if we have to draw the drop down button
    if (_cbType != kSimple) 
	{
		BOOL bRelease = !hdc;
		if (!hdc)
			hdc = TxGetDC();

        DrawFrameControl(hdc, &_rcButton, DFC_SCROLL, DFCS_SCROLLCOMBOBOX |
            (bDown ? DFCS_PUSHED | DFCS_FLAT: 0) | (!_fBorder ? DFCS_FLAT : 0) |
            (!_fDisabled ? 0 : DFCS_INACTIVE));

		if (bRelease)
			TxReleaseDC(hdc);
    }
}


/* 
 *	CCmbBxWinHost::TxNotify (iNotify,	pv)
 *
 *	@mfunc
 *		Notify Text Host of various events.  Note that there are
 *		two basic categories of events, "direct" events and 
 *		"delayed" events.  In the case of the combobox we will
 *		notify parent of only two edit notifications; EN_CHANGE 
 *		and EN_UPDATE.  The others will be from the listbox
 *		or be generated because of focus changing
 *
 *
 *	@rdesc	
 *		S_OK - call succeeded <nl>
 *		S_FALSE	-- success, but do some different action
 *		depending on the event type (see below).
 *
 *	@comm
 *		<CBN_DBLCLK> user double-clicks an item in the list box
 *
 *		<CBN_ERRSPACE> The list box cannot allocate enough memory to 
 *		fulfill a request
 *
 *		<CBN_KILLFOCUS> The list box loses the keyboard focus
 *
 *		<CBN_SELENDCANCEL> notification message is sent when the user 
 *		selects an item, but then selects another control or closes the 
 *		dialog box
 *
 *		<CBN_SELCHANGE> notification message is sent when the user changes 
 *		the current selection in the list box of a combo box
 *
 *		<CBN_SETFOCUS> The list box receives the keyboard focus
 *
 *		<CBN_CLOSEUP> This message is sent when the listbox has been closed
 *
 *		<CBN_SELENDOK> notification message is sent when the user selects a 
 *		list item, or selects an item and then closes the list
 *
 *		<CBN_EDITCHANGE> notification message is sent after the user 
 *		has taken an action that may have altered the text in the edit 
 *		control portion of a combo box
 *
 *		<CBN_EDITUPDATE> notification message is sent when the edit control 
 *		portion of a combo box is about to display altered text
 *
 *		<CBN_DROPDOWN> This message is sent when the listbox has been made visible
 */
HRESULT CCmbBxWinHost::TxNotify(
	DWORD iNotify,		//@parm	Event to notify host of.  One of the
						//		EN_XXX values from Win32, e.g., EN_CHANGE
	void *pv)			//@parm In-only parameter with extra data.  Type
						//		dependent on <p iNotify>
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CCmbBxWinHost::TxNotify");
	HRESULT hr = S_FALSE;

	if(_hwndParent)
	{
		// First, handle WM_NOTIFY style notifications
		//WPARAM LOWORD(_idCtrl) ; LPARAM - HWND(COMBO)
		switch(iNotify)
		{
		case EN_CHANGE:
			//update the listbox bug fix #5206
			if (_fIgnoreChange)
			{
				_fIgnoreChange = 0;
				return hr;
			}

			if (_fListVisible && _cbType == kDropDown)
				UpdateListBox(FALSE);
			else if (_cbType == kDropDownList)
			    // don't send notification if dropdownlist
			    return S_FALSE;
			    
			iNotify = CBN_EDITCHANGE;
			goto sndmsg;
			
		case EN_UPDATE:
			//bug fix - we're sending too much CBN_UPDATE notifications
			if (_fIgnoreUpdate)
				return hr;
			if (_cbType == kDropDownList)
			    return S_FALSE;
			    
			iNotify = CBN_EDITUPDATE;
			goto sndmsg;
			
		case EN_ERRSPACE:
			iNotify = (unsigned)CBN_ERRSPACE;
			goto sndmsg;

		case CBN_SELCHANGE: 
		case CBN_SELENDCANCEL:		
		case CBN_CLOSEUP:
		case CBN_DBLCLK:	
		case CBN_DROPDOWN:  
		case CBN_KILLFOCUS:  
		case CBN_SELENDOK:
		case CBN_SETFOCUS:
	
sndmsg:
		hr = SendMessage(_hwndParent, WM_COMMAND, 
						GET_WM_COMMAND_MPS(_idCtrl, _hwnd, iNotify));
		}		
	}
	return hr;
}

/*
 *	CCmbBxWinHost::DrawEditFocus(HDC)
 *
 *	@mfunc
 *		Either draws or notifies owner to draw the focus rect
 *
 *	@rdesc
 *		void
 */
void CCmbBxWinHost::DrawEditFocus(HDC hdc)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::DrawEditFocus");

	BOOL bRelease = FALSE;
	if (!hdc)
	{
		hdc = TxGetDC();
		bRelease = TRUE;
	}

	RECT rc;
	GetClientRect(_hwnd, &rc);

	if (!_fOwnerDraw)
	{
		HiliteEdit(_fFocus);

		if (_cbType == kDropDownList)
		{
			// shrink the focus rect by the inset
			rc.top += _yInset;
			rc.bottom -= _yInset;			
			
			if (_fRightAlign)
				rc.left = _rcButton.right;
			else
				rc.right = _rcButton.left;

			rc.left += _xInset;
			rc.right -= _xInset;

			DrawFocusRect(hdc, &rc);
		}
	}

	if (bRelease)
		TxReleaseDC(hdc);
	
}

/*
 *	CCmbBxWinHost::SetSelectionInfo(BOOL bOk, int nIdx)
 *
 *	@mfunc
 *		Completes the text in the edit box with the closest match from the
 * listbox.  If a prefix match can't be found, the edit control text isn't
 * updated. Assume a DROPDOWN style combo box.
 *
 *	@rdesc
 *		void
 */
void CCmbBxWinHost::SetSelectionInfo(BOOL bOk, int nIdx)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::SetSelectionInfo");
	

	_nCursor = nIdx;
	_bSelOk = bOk;	
}

/*
 *	CCmbBxWinHost::AutoUpdateEdit(int i)
 *
 *	@mfunc
 *		Completes the text in the edit box with the closest match from the
 * listbox.  If a prefix match can't be found, the edit control text isn't
 * updated. Assume a DROPDOWN style combo box.
 *
 *	@rdesc
 *		void
 */
void CCmbBxWinHost::AutoUpdateEdit(int nItem)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::AutoUpdateEdit");

    // We update the edit part of the combo box with the current selection of
    // the list box
	int cch;
	TCHAR* pszText;
	LRESULT lr;

	// find the best matching string in the list box
	if (nItem == -1 || nItem == -2)
	{
		cch = GetTextLength();

		// no text to search so just get out
	    if (!cch)
	    	return;

	    cch++; // account for null character
	    pszText = new TCHAR[cch];
	    AssertSz(pszText, "string allocation failed");
	    if (!pszText) 
	    {
			TxNotify((unsigned)CBN_ERRSPACE, NULL);
			return;
		}

		// get string from edit control and try to find a exact match else a match
		// in the list box
		GetEditText(pszText, cch);
		
	    nItem = RichListBoxWndProc(_hwndList, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pszText);

	    if (nItem == -1)
	    	nItem = RichListBoxWndProc(_hwndList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)pszText);
		delete [] pszText;

		// no match found so just get out
	    if (nItem == -1)         	
	    	return;
    }

	cch = RichListBoxWndProc(_hwndList, LB_GETTEXTLEN, nItem, 0);

	if (cch <= 0)
		return;
		
    cch++; // account for null character
    pszText = new TCHAR[cch];
	AssertSz(pszText, "Unable to allocate string");
	if (!pszText)
	{
		TxNotify((unsigned)CBN_ERRSPACE, NULL);
		return;
	}

	RichListBoxWndProc(_hwndList, LB_GETTEXT, nItem, (LPARAM)pszText);
	_fIgnoreChange = 1;
	_pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)pszText, &lr);

   	HiliteEdit(TRUE);

    delete [] pszText;
}

/*
 *	CCmbBxWinHost::HiliteEdit(BOOL)
 *
 *	@mfunc
 *		Sets the hilite background or selects the entire text for the
 *	edit control
 *
 *	@rdesc
 *		void
 */
void CCmbBxWinHost::HiliteEdit(BOOL bSelect)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::HiliteEdit");

	//bug fix 4073
	Assert(!_fOwnerDraw || _cbType == kDropDown);

	if (_cbType != kDropDownList)
	{
		//if bSelect is true else put cursor at beginning of text
		_pserv->TxSendMessage(EM_SETSEL, 0, (LPARAM)((bSelect) ? -1 : 0), NULL);
	}
	else
	{
		//Get the range of the paragraph
		ITextRange* pRange;		
		if (NOERROR != ((CTxtEdit*)_pserv)->Range(0, 0, &pRange))
		{
			AssertSz(FALSE, "unable to get range");
			return;
		}
		Assert(pRange);

		DWORD crFore = (unsigned)tomAutoColor;
		DWORD crBack = (unsigned)tomAutoColor;
		if (bSelect)
		{
			crFore = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
			crBack = ::GetSysColor(COLOR_HIGHLIGHT);
		}

		// Get the entire paragraph
		ITextFont* pFont = NULL;	

		// Select entire text
		CHECKNOERROR(pRange->SetIndex(tomParagraph, 1, 1));
		
		// Set the background and forground color
		CHECKNOERROR(pRange->GetFont(&pFont));
		
		Assert(pFont);
		CHECKNOERROR(pFont->SetBackColor(crBack));
		CHECKNOERROR(pFont->SetForeColor(crFore));

CleanExit:
		// Release pointers
		if (pFont)
			pFont->Release();
		pRange->Release();
	}
}


/*
 *	CCmbBxWinHost::UpdateEditBox()
 *
 *	@mfunc
 *		Updates the editcontrol window so that it contains the text
 * given by the current selection in the listbox.  If the listbox has no
 * selection (ie. -1), then we erase all the text in the editcontrol.
 *
 * hdc is from WM_PAINT messages Begin/End Paint hdc. If null, we should
 * get our own dc.
 *
 *	@rdesc
 *		void
 */
void CCmbBxWinHost::UpdateEditBox()
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::UpdateEditBox");

    Assert(_hwndList);
    Assert(_plbHost);

    // Update the edit box
    if (_cbType == kDropDownList && _fOwnerDraw)
    {
	   	CbMessageItemHandler(NULL, ITEM_MSG_DRAWCOMBO, 0, 0);
	   	return;
	}
    else 
    {
		TCHAR* pszText = NULL;
	   	int nItem = (signed)_plbHost->GetCursor();   	
	    if (nItem != -1)
		{
			int cch = RichListBoxWndProc(_hwndList, LB_GETTEXTLEN, (LPARAM)nItem, 0);
		    pszText = new TCHAR[cch + 1];
			AssertSz(pszText, "allocation failed");

			// just get out if memory allocation failed
			if (!pszText)
			{
				TxNotify((unsigned)CBN_ERRSPACE, NULL);
				return;
			}
			RichListBoxWndProc(_hwndList, LB_GETTEXT, (WPARAM)nItem, (LPARAM)pszText);		
		}
	
    	// if the cursor is on a valid item then update edit with the item text
    	// else we just display a blank text
    	TCHAR szEmpty[] = L"";
    	_fIgnoreChange = 1;
    	_pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)((pszText) ? pszText : szEmpty), NULL);
   		DrawEditFocus(NULL);
    	if (pszText)
    		delete pszText;
    }
}

/*
 *	CCmbBxWinHost::UpdateListBox(BOOL)
 *
 *	@mfunc
 *		Updates the list box by searching and moving to the top the text in
 *	edit control.  And possibly pre-selecting the item if bSetSel is set
 *
 *	@rdesc
 *		int = found ? index of item : -1
 */
int CCmbBxWinHost::UpdateListBox(BOOL bSetSel)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::UpdateListBox");

    int nItem = -1;
    int nSel = -1;
	TCHAR* pszText;
	int cch;

	// Get text from edit box
    cch = GetTextLength();
    if (cch) 
    {
    	// add one for null string
        cch++;
        pszText = new TCHAR[cch];
        if (pszText != NULL) 
        {  
        	if (GetEditText(pszText, cch))
        	{
        		//Bypass Winnt thunking layer by calling the function directly
        		nItem = RichListBoxWndProc(_hwndList, LB_FINDSTRING, (WPARAM)-1L, (LPARAM)pszText);
        	}
        	delete [] pszText;        	
        }
        else
        {
			TxNotify((unsigned)CBN_ERRSPACE, NULL);
			return 0;
		}
    }

    if (bSetSel)
        nSel = nItem;

	// update the list box
    RichListBoxWndProc(_hwndList, LB_SETCURSEL, (LPARAM)nSel, 0);
	RichListBoxWndProc(_hwndList, LB_SETTOPINDEX, (LPARAM)max(nItem, 0), 0);	
    return nItem;
}


/*
 *	CCmbBxWinHost::HideListBox(BOOL, BOOL)
 *
 *	@mfunc
 *		Hides the list box
 *
 *	@rdesc
 *		void
 */
BOOL CCmbBxWinHost::HideListBox(BOOL bNotify, BOOL fSelOk)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::HideListBox");

	//send CBN_SELENDOK to all types of comboboxes but only
    // allow CBN_SELENDCANCEL to be sent for droppable comboboxes
	if (bNotify)
	{
		if (fSelOk)
		{
			TxNotify(CBN_SELENDOK, NULL);
		}
		else if (_cbType != kSimple)
		{
			TxNotify(CBN_SELENDCANCEL, NULL);
		}
	}
	
    // return, we don't hide simple combo boxes.
	if (!_fListVisible || _cbType == kSimple) 
    	return TRUE;

    // Tell the listbox to end tracking
    Assert(_plbHost);
	_plbHost->OnCBTracking(LBCBM_END, 0);     	
    
    // Hide the listbox window
    _fListVisible = 0;
    ShowWindow(_hwndList, SW_HIDE);
	if (_fCapture)
	{
		_fCapture = FALSE;
		TxSetCapture(FALSE);
	}

	_fResizing = 1;
    // Invalidate the item area now since SWP() might update stuff.
    // Since the combo is CS_VREDRAW/CS_HREDRAW, a size change will
    // redraw the whole thing, including the item rect.  But if it
    // isn't changing size, we still want to redraw the item anyway
    // to show focus/selection
    if (_cbType == kDropDownList)
	{
		if (!_fOwnerDraw)
			HiliteEdit(_fFocus);
        InvalidateRect(_hwnd, NULL, TRUE);
	}

	//bug fix
	// The button may look depressed so we must redraw the button
	if (_fMousedown)
	{
		_fMousedown = FALSE;
		InvalidateRect(_hwnd, &_rcButton, FALSE);
	}

    SetWindowPos(_hwnd, HWND_TOP, 0, 0, _cxCombo, _cyCombo, 
    	SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

	_fResizing = 0;

	if (_cbType == kDropDown)
		AutoUpdateEdit(_nCursor);
	_nCursor = -2;

    // In case size didn't change
    UpdateWindow(_hwnd);

    if (bNotify) 
    {
        //Notify parent we will be popping up the combo box.
        TxNotify(CBN_CLOSEUP, NULL);
    }

	// reset back to old cursor if mouse cursor was set
	if (_hcurOld)
	{
		TxSetCursor2(_hcurOld, NULL);
		_hcurOld = NULL;
	}
    return(TRUE);
}



/*
 *	CCmbBxWinHost::ShowListBox(BOOL)
 *
 *	@mfunc
 *		Displays the list box
 *
 *	@rdesc
 *		void
 */
void CCmbBxWinHost::ShowListBox(BOOL fTrack)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::ShowListBox");
	
	Assert(_cbType != kSimple);
	Assert(_hwndList);

	// Notify parent window we are about to drop down the list box
	TxNotify(CBN_DROPDOWN, NULL);

	// force a redraw of the button so it looks depressed
	InvalidateRect(_hwnd, &_rcButton, TRUE);

	_fListVisible = TRUE;
	_fIgnoreChange = 0;

	_bSelOk = 0;
	if (_cbType == kDropDown)
	{
		UpdateListBox(!_fMousedown);
		if (!_fMousedown)
			AutoUpdateEdit(-1);
		_nCursor = _plbHost->GetCursor();

	}
	else
	{
        // Scroll the currently selected item to the top of the listbox.        
		int idx = (signed)_plbHost->GetCursor();
		_nCursor = idx;
		if (idx == -1)
			idx = 0;

		// set the top index if there is something in the list box
		if (_plbHost->GetCount() > 0)
			RichListBoxWndProc(_hwndList, LB_SETTOPINDEX, idx, 0);	

		// We are to lose focus in this case
		_fFocus = 0;
		if (!_fOwnerDraw)
			HiliteEdit(FALSE);
		
	    // We need to invalidate the edit rect so that the focus frame/invert
        // will be turned off when the listbox is visible.  Tandy wants this for
        // his typical reasons...        
        InvalidateRect(_hwnd, NULL, TRUE);        
    }

    // Figure out where to position the dropdown listbox.
    // We want the dropdown to pop below or above the combo
    // Get screen coords
    RECT rcList;
	POINT pt1;
    pt1.x = _rcList.left;
    pt1.y = _rcList.top;

	TxClientToScreen(&pt1);
	rcList.left = pt1.x;
	rcList.top = pt1.y;
	rcList.right = rcList.left + (_rcList.right - _rcList.left);
	rcList.bottom = rcList.top + _cyList;


    // List area
    int cyItem = _plbHost->GetItemHeight();
    AssertSz(cyItem, "LB_GETITEMHEIGHT is returning 0");

    if (cyItem == 0)
    	cyItem = _plbHost->GetFontHeight();

    // Windows NT comment:
    //  we shoulda' just been able to use cyDrop here, but thanks to VB's need
    //  to do things their OWN SPECIAL WAY, we have to keep monitoring the size
    //  of the listbox 'cause VB changes it directly (jeffbog 03/21/94)
    int iHeight = max(_cyList, _rcWindow.bottom - _rcWindow.top);
	DWORD dwMult = (DWORD)RichListBoxWndProc(_hwndList, LB_GETCOUNT, 0, 0);
	
    if (dwMult) 
    {
        dwMult = (DWORD)(LOWORD(dwMult) * cyItem);
        dwMult += GetSystemMetrics(SM_CYEDGE);

        if (dwMult < 0x7FFF)
            iHeight = min(LOWORD(dwMult), iHeight);
    }

    if (!_fNoIntegralHeight) 
    {
        iHeight = ((iHeight - GetSystemMetrics(SM_CYEDGE)) / cyItem) * cyItem + 
        	GetSystemMetrics(SM_CYEDGE);
    }

    //UNDONE: Multi-monitor
    //	We need to change the following code if we are to support multi-monitor
    int yTop;
    int nScreenHeight = GetSystemMetrics(SM_CYFULLSCREEN);    
    if (rcList.top + iHeight <= nScreenHeight) 
    {
        yTop = rcList.top;
        if (!_fBorder)
            yTop -= GetSystemMetrics(SM_CYBORDER);
    } 
    else 
    {
        yTop = max(rcList.top - iHeight - _cyCombo + 
			((_fBorder) ? GetSystemMetrics(SM_CYBORDER) : 0), 0);
    }

    
    SetWindowPos(_hwndList, HWND_TOPMOST, rcList.left,
        yTop, rcList.right - rcList.left, iHeight, 0);

	Assert(_plbHost);
    _plbHost->SetScrollInfo(SB_VERT, FALSE);

	
	if (_cbType == kDropDownList)
		_fFocus = 0;

	// UNDONE:
	// Are we going to support window animation?	
    ShowWindow(_hwndList, SW_SHOW);
	
	// We send a message to the listbox to prepare for tracking
	if (fTrack)
	{		
		Assert(_plbHost);
		// initialize type searching
		_plbHost->InitSearch();
		_plbHost->OnCBTracking(LBCBM_PREPARE, LBCBM_PREPARE_SAVECURSOR | 
						((_cbType == kDropDownList) ? LBCBM_PREPARE_SETFOCUS : 0));
	}
	
	// Since we are about to display the list box change mouse cursor to arrow
	if (!_hcurOld)
		_hcurOld = TxSetCursor2(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)), NULL);
}

////////////////////////// Combo box Message Handlers ////////////////////////////////

/*
 *	CCmbBxWinHost::CbSetItemHeight(BOOL, int)
 *
 *	@mfunc
 *		Sets the size of the edit or list box.
 *
 *
 *	@rdesc
 *		LRESULT = successful ? 1 : CB_ERR
 */
LRESULT CCmbBxWinHost::CbSetItemHeight(BOOL bEdit, int nHeight)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbSetItemHeight");

	//bug fix #4556
	if (nHeight == 0 || nHeight > 255)
		return CB_ERR;

	// We need to update the height internally
	if (bEdit)
	{
		RECT rc;
		GetClientRect(_hwnd, &rc);
		_dyEdit = nHeight;
		OnSize(0, MAKELONG(rc.right - rc.left, rc.bottom - rc.top));
	}
	else
	{
		RichListBoxWndProc(_hwndList, LB_SETITEMHEIGHT, 0, MAKELPARAM(nHeight, 0));
	}
	return 1;
}

/*
 *	CCmbBxWinHost::CbGetItemHeight(BOOL)
 *
 *	@mfunc
 *		Retrieves the size of the edit or list box.
 *
 *
 *	@rdesc
 *		LRESULT = successful ? 1 : CB_ERR
 */
LRESULT CCmbBxWinHost::CbGetItemHeight(BOOL bEdit)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbGetItemHeight");

	// We need to update the height internally
	if (bEdit)
	{
		return _dyEdit;
	}
	else
	{
		return RichListBoxWndProc(_hwndList, LB_GETITEMHEIGHT, 0, 0);
	}
}


/*
 *	CCmbBxWinHost::CbSetExtendedUI(BOOL)
 *
 *	@mfunc
 *		Retrieves the size of the edit or list box.
 *
 *
 *	@rdesc
 *		LRESULT = successful ? CB_OKAY : CB_ERR
 */
LRESULT CCmbBxWinHost::CbSetExtendedUI(BOOL bExtendedUI)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbSetExtendedUI");

	// We need to update the height internally
	_fExtendedUI = bExtendedUI ? 1 : 0;
	return CB_OKAY;
}


/*
 *	CCmbBxWinHost::CbMessageItemHandler(int, WPARAM, LPARAM)
 *
 *	@mfunc
 *		Handles any and all WM_DRAWITEM and WM_DELETEITEM messages
 *
 *
 *	@rdesc
 *		LRESULT = whatever the parent window returns
 */
LRESULT CCmbBxWinHost::CbMessageItemHandler(HDC hdc, int ff, WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbMessageItemHandler");

	// modify the structure info a bit and pass it to the parent window
    DRAWITEMSTRUCT dis;   
    BOOL bRelease = FALSE;
    UINT msg = WM_DRAWITEM;
	switch (ff)
	{
	case ITEM_MSG_DRAWLIST:
		((LPDRAWITEMSTRUCT)lparam)->CtlType = ODT_COMBOBOX;
	    ((LPDRAWITEMSTRUCT)lparam)->CtlID = _idCtrl;
	    ((LPDRAWITEMSTRUCT)lparam)->hwndItem = _hwnd;	    
	    break;

	case ITEM_MSG_DELETE:
		((LPDELETEITEMSTRUCT)lparam)->CtlType = ODT_COMBOBOX;
	    ((LPDELETEITEMSTRUCT)lparam)->CtlID = _idCtrl;
	    ((LPDELETEITEMSTRUCT)lparam)->hwndItem = _hwnd;
	    msg = WM_DELETEITEM;
	    break;

	case ITEM_MSG_DRAWCOMBO:
		if (!hdc)
	    {
	    	bRelease = TRUE;
	    	hdc = TxGetDC();
	    }
	    //Fill the DRAWITEMSTRUCT with the unchanging constants
	    dis.CtlType = ODT_COMBOBOX;
	    dis.CtlID = _idCtrl;    

	    // Use -1 if an invalid item number is being used.  This is so that the app
	    // can detect if it should draw the caret (which indicates the lb has the
	    // focus) in an empty listbox
	    dis.itemID = _plbHost->GetCursor();
	    dis.itemAction = ODA_DRAWENTIRE;
	    dis.hwndItem = _hwnd;	    
	    dis.hDC = hdc;
		dis.itemData = (_plbHost->GetCount()) ? (((signed)dis.itemID >= 0) ? _plbHost->GetData(dis.itemID) : 0) : 0;
	    dis.itemState = (UINT)((_fFocus && !_fListVisible ? ODS_SELECTED | ODS_FOCUS : 0) |
                    ((_fDisabled) ? ODS_DISABLED : 0) | ODS_COMBOBOXEDIT);
		           
		// Calculate the drawing rect
        TxGetClientRect(&dis.rcItem);
        if (_cbType != kSimple)
        {
        	if (_fRightAlign)
        		dis.rcItem.left = _rcButton.right;
        	else
        		dis.rcItem.right = _rcButton.left;
        }

        // immulate the system by making the HDC invert text if we have focus
		SetBkMode(hdc, OPAQUE);
		PatBlt(hdc, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right - dis.rcItem.left,
                dis.rcItem.bottom - dis.rcItem.top, PATCOPY);

		if (_fFocus && !_fListVisible) 
		{
	        // only do the FillRect if we know its not
	        // ownerdraw item, otherwise we mess up people up
	        // BUT: for Compat's sake we still do this for Win 3.1 guys
            SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
            SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
        } 
        
        // Don't let ownerdraw dudes draw outside of the combo client
        // bounds.
		InflateRect(&dis.rcItem, -1, -1);
        IntersectClipRect(hdc, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, 
        				dis.rcItem.bottom);
	    lparam = (LPARAM)&dis;		
	}

	LRESULT lres = SendMessage(_hwndParent, msg, _idCtrl, lparam);
	if (bRelease)
		TxReleaseDC(hdc);

	return lres;
}

/////////////////////////// Windows Message Handlers /////////////////////////////////
/* 
 *	CCmbBxWinHost::OnCommand(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Handles notification from listbox and reflects it to the parent of
 *		the combo box
 *
 *	@comm
 *		LRESULT = Handled ? 0 : 1
 *
 *
 */
HRESULT CCmbBxWinHost::OnCommand(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CCmbBxWinHost::OnCommand");
	
	// Filter-out all the messages except Listbox notification messages
	Assert(_hwndParent);
	switch (HIWORD(wparam))
	{	
	case LBN_DBLCLK:
    	TxNotify(CBN_DBLCLK, NULL);
        break;

    case LBN_ERRSPACE:
        TxNotify((unsigned)CBN_ERRSPACE, NULL);
        break;

    case LBN_SELCHANGE:
    case LBN_SELCANCEL:
    	if (!_fListVisible)
			HideListBox(TRUE, TRUE);
    	TxNotify(CBN_SELCHANGE, NULL);
        UpdateEditBox();
        break;

    default:
    	// not handled so pass down the line
        return 1;
	}
	return 0;
}

/*
 *	CCmbBxWinHost::OnEnable(WPARAM, LPARAM)
 *
 *	@mfunc
 *		handles the WM_ENABLE message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnEnable(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnEnable");

	if (_fMousedown) 
	{
        _fMousedown = FALSE;
        DrawButton(NULL, FALSE);

        //
        // Pop combo listbox back up, canceling.
        //
        if (_fListVisible)
            HideListBox(TRUE, FALSE);
    }
    return 1;
}


/*
 *	CCmbBxWinHost::OnChar(WPARAM, LPARAM)
 *
 *	@mfunc
 *		handles the WM_CHAR message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnChar(WORD wparam, DWORD lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnChar");

	// Check if we should eat the message or not
	if (_cbType == kDropDownList)
	{
		//bug fix #5318 - ignore delete, insert and clear
		if (((WCHAR)wparam) == VK_DELETE || ((WCHAR)wparam) == VK_INSERT ||
			((WCHAR)wparam) == VK_CLEAR)
			return 0;
			
		// Sending WM_CHAR is BAD!!! call the message handler directly
		// send the character string message to the listbox if visible
		_plbHost->OnChar(LOWORD(wparam), lparam);

		//	If Hi-Ansi need to send a wm_syskeyup message to ITextServices to 
		// stabalize the state
		if (0x80 <= wparam && wparam <= 0xFF && !HIWORD(GetKeyState(VK_MENU)))
		{
			LRESULT lres;
			_pserv->TxSendMessage(WM_SYSKEYUP, VK_MENU, 0xC0000000, &lres);
		}		
		return 0;
	}

	
	if (_cbType == kDropDown)
	{
		if (_fListVisible)
		{
			if (!_fCapture)
			{
				// Tell listbox to reset capturing by ending then starting it up
				_plbHost->OnCBTracking(LBCBM_END, 0);
				_plbHost->OnCBTracking(LBCBM_PREPARE, 0);			
			}

			// Send the message to the edit control iff it's not a tab
			if (((WCHAR)wparam) != VK_TAB)
				_pserv->TxSendMessage(WM_CHAR, wparam, lparam, NULL);

			if (!_fCapture)
			{
				// capture the cursor
				TxSetCapture(TRUE);
				_fCapture = 1;				
			}
		}
		else
		{
			// set the cursel to -1 if it already isn't
			if ((wparam != VK_RETURN) && (_plbHost->GetCursor() != -1))
				RichListBoxWndProc(_hwndList, LB_SETCURSEL, (WPARAM)-1, 0);

			// Send the message to the edit control iff it's not CTRL+i or CTRL+h
			if (((WCHAR)wparam) != VK_TAB)
				_pserv->TxSendMessage(WM_CHAR, wparam, lparam, NULL);
		}		
		return 0;
	}
	return 1;
}

/*
 *	CCmbBxWinHost::OnKeyDown(WPARAM, LPARAM)
 *
 *	@mfunc
 *		handles the WM_KEYDOWN message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnKeyDown(WORD wparam, DWORD lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnKeyDown");

	if (_fListVisible && (wparam == VK_RETURN || wparam == VK_ESCAPE))
	{
		if (wparam == VK_RETURN)
			_nCursor = _plbHost->GetCursor();
		HideListBox(TRUE, wparam == VK_RETURN);
		return 0;
	}
	
	// if we are in extended mode and F4 is hit
	// we just ignore it
	if (_fExtendedUI && wparam == VK_F4)
		return 0;
	
	Assert(_plbHost);
	int fExtUI = _fExtendedUI;
	int nCurSel = _plbHost->GetCursor();
	Assert(nCurSel >= -1);
	
	// if we are a dropdownlist combo box then just forward the message on to the 
	// list box 
	if (_cbType == kDropDownList)
	{
		switch (wparam)
		{		
		case VK_F4:
			if (_fListVisible)
				break;;
			fExtUI = 1;
			Assert(fExtUI && !_fListVisible);
			// fall through case		
			
		case VK_DOWN:
			if (fExtUI && !_fListVisible)
			{
				ShowListBox(TRUE);
				TxSetCapture(TRUE);			
				_fCapture = TRUE;
				return 1;			
			}		
			// Fall through case
			
		case VK_UP:
		case VK_NEXT:
		case VK_PRIOR:		
		case VK_RETURN:
		case VK_ESCAPE:	
			break;	

		//bug fix #5318
		/*
		case VK_DELETE:
		case VK_CLEAR:
		case VK_INSERT:
		*/

		default:
			// There no reason for us to pass these keys to ITextServices since the control is suppose
			// to be read-only
			return 0;
		}
	}
	else 
	{
		switch (wparam)
		{		
		case VK_F4:
			if (_fListVisible)
				break;
			fExtUI = 1;
			Assert(fExtUI && !_fListVisible);
			// fall through case		
			
		case VK_DOWN:
			if (fExtUI && !_fListVisible)
			{
				ShowListBox(TRUE);
				TxSetCapture(TRUE);			
				_fCapture = TRUE;
				return 0;			
			}		
			// Fall through case
			
		case VK_UP:
		case VK_NEXT:
		case VK_PRIOR:
			if (_fListVisible)
			{				
				if (_fCapture)
				{
					// release our capture flag and tell lb to start tracking
					_fCapture = 0;
					_plbHost->OnCBTracking(LBCBM_START, _fMousedown);
				}

				// selecting the top index and then sending the keydown to the 
				// listbox causes 2 moves so handle this ourselves
				if (nCurSel == -1)
				{
					RichListBoxWndProc(_hwndList, LB_SETCURSEL, _plbHost->GetTopIndex(), 0);
					UpdateEditBox();
					UpdateCbWindow();
					return 0;
				}
			}
			else
			{
				// if Listbox isn't visible and the listbox cursor is -1
				// then we should try to select the correct item in the list
				// box
				if (nCurSel == -1)
				{
					UpdateListBox(TRUE);
					if (_plbHost->GetCursor() >= 0)
					{
						HiliteEdit(TRUE);
						return 0;
					} else if (!_plbHost->GetCount())
					{
						return 0;
					}
				}
			}
			break;
		
		case VK_RETURN:
		case VK_ESCAPE:	
			break;		

		default:
			// return zero to say we didn't handle this
			return 1;
		}
	}
	// pass message to list box
	_plbHost->OnKeyDown(wparam, lparam, 0);
	UpdateCbWindow();
			
	return 0; 
	
}

/*
 *	CCmbBxWinHost::OnSyskeyDown(WORD, DWORD)
 *
 *	@mfunc
 *		handles the WM_SYSKEYDOWN message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnSyskeyDown(WORD wparam, DWORD lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSyskeyDown");

	if (lparam & 0x20000000L)  /* Check if the alt key is down */ 
	{
	    // Handle Combobox support.  We want alt up or down arrow to behave
	    // like F4 key which completes the combo box selection
		if (lparam & 0x1000000)
		{
			// We just want to ignore keys on the number pad...
	        // This is an extended key such as the arrow keys not on the
	        // numeric keypad so just drop the combobox.
	        if (wparam != VK_DOWN && wparam != VK_UP)
	            return 1;
		}
		else if (GetKeyState(VK_NUMLOCK) & 0x1) 
	    {
	        //If numlock down, just send all system keys to dwp
	        return 1;
	    } 
	    else 
	    {
			if (wparam != VK_DOWN && wparam != VK_UP)
				return 1;	    	
	    }

	    // If the listbox isn't visible, just show it
	    if (!_fListVisible) 
		{
			ShowListBox(TRUE);
			TxSetCapture(TRUE);			
			_fCapture = TRUE;
		}
	    else  	//Ok, the listbox is visible.  So hide the listbox window.
	        HideListBox(TRUE, TRUE);
	    return 0;
	}
	return 1;
}

/*
 *	CCmbBxWinHost::OnCaptureChanged(WPARAM, LPARAM)
 *
 *	@mfunc
 *		handles the WM_CAPTURECHANGED message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnCaptureChanged(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnCaptureChanged");
    if (_fCapture) 
    {   
        // Pop combo listbox back up, canceling.
        if (_fListVisible)
            HideListBox(TRUE, FALSE);
        else
        {
        	_fCapture = FALSE;
   			_fMousedown = FALSE;
        	DrawButton(NULL, FALSE);
        }
		return 0;
    }
	return 1;
}


/*
 *	CCmbBxWinHost::OnMouseMove(WPARAM, LPARAM)
 *
 *	@mfunc
 *		handles the WM_MOUSEMOVE message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnMouseMove(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnMouseMove");

	// We do the following if we have mouse captured or if the listbox is visible
	if (_cbType != kSimple && _fCapture)
	{
		// get the point coordinates of mouse
		POINT pt;
		POINTSTOPOINT(pt, lparam);
		if (_fListVisible)
		{
			// if the listbox is visible visible check if the cursor went over 
			// list box
			RECT rc;
			POINT ptScreen = pt;
			GetWindowRect(_hwndList, &rc);
			TxClientToScreen(&ptScreen);			
			if (PtInRect(&rc, ptScreen))
			{
				// Release the capture state of the mouse
				if (_fCapture)
				{
					_fCapture = FALSE;
					TxSetCapture(FALSE);
				}

				// notify the listbox to start tracking
				Assert(_plbHost);					
				::PostMessage(_hwndList, LBCB_TRACKING, LBCBM_START, _fMousedown);
				_fMousedown = 0;
			}
		}
		DrawButton(NULL, _fMousedown ? PtInRect(&_rcButton, pt) : FALSE);
		return FALSE;
	}
#ifdef DEBUG
	if (_cbType != kSimple)
		Assert(!_fListVisible);
#endif
	return TRUE;
}
	
/*
 *	CCmbBxWinHost::OnLButtonUp(WPARAM, LPARAM)
 *
 *	@mfunc
 *		handles the WM_LBUTTONUP message
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnLButtonUp(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnLButtonUp");
 
    if (_fMousedown) 
	{
        _fMousedown = FALSE;
        if (_cbType != kSimple) 
		{
            // If an item in the listbox matches the text in the edit
            // control, scroll it to the top of the listbox. Select the
            // item only if the mouse button isn't down otherwise we
            // will select the item when the mouse button goes up.
			if (_cbType == kDropDown)
			{
				UpdateListBox(TRUE);
				AutoUpdateEdit(-1);		
			}
			
			// if we recieved a mouse up and the listbox is still visible then user 
			// hasn't selected any items from the listbox so don't release the capture yet
			if (_fCapture && !_fListVisible)
			{
				_fCapture = FALSE;
				TxSetCapture(FALSE);
			}

			DrawButton(NULL, FALSE);       
			return FALSE;
		}
    }
	return TRUE;
}

/*
 *	CCmbBxWinHost::OnLButtonDown(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the client edges of the combo box
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnLButtonDown(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnLButtonDown");

	// check if we should dropdown the list box
	POINT pt;
	POINTSTOPOINT(pt, lparam);
	
	// if we don't have focus then set the focus first
	if (!_fFocus)
		TxSetFocus();
	_fFocus = 1;

	// listbox is down so pop it back up
	if (_fListVisible)
	{
		return !HideListBox(TRUE, FALSE);		
	}
	else if (_cbType == kDropDownList || (_cbType == kDropDown && PtInRect(&_rcButton, pt)))
	{	
		// need to show listbox
		ShowListBox(TRUE);
		_fMousedown = TRUE;
					
		TxSetCapture(TRUE);			
		_fCapture = TRUE;
		return 0;
	}
	return 1;
}

/*
 *	CCmbBxWinHost::OnMouseWheel(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the client edges of the combo box
 *
 *	@rdesc
 *		LRESULT = Handled ? 0 : 1
 */
LRESULT CCmbBxWinHost::OnMouseWheel(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnMouseWheel");
	
    // Handle only scrolling.
    if (wparam & (MK_CONTROL | MK_SHIFT))
        return 1;

    // If the listbox is visible, send it the message to scroll.
    // if the listbox is 
	if (_fListVisible)
	{
		_plbHost->OnMouseWheel(wparam, lparam);
		return 0;
	}
		
    // If we're in extended UI mode or the edit control isn't yet created,
    // bail.
    if (_fExtendedUI)
        return 0;

    // Emulate arrow up/down messages to the edit control.
    int i = abs(((short)HIWORD(wparam))/WHEEL_DELTA);
    wparam = ((short)HIWORD(wparam) > 0) ? VK_UP : VK_DOWN;

    while (i-- > 0) 
        OnKeyDown(wparam, lparam);

	return 0;
}


/*
 *	CCmbBxWinHost::OnSetCursor(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Changes the cursor depending on where the cursor is
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
 LRESULT CCmbBxWinHost::OnSetCursor(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSetCursor");

	POINT pt;
	GetCursorPos(&pt);
	::ScreenToClient(_hwnd, &pt);

	if ((_cbType == kDropDownList) || 
		(_cbType == kDropDown && ((_fRightAlign) ? _rcButton.right >= pt.x : _rcButton.left <= pt.x)))
	{
		TxSetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)), NULL);
	}
	else
		_pserv->OnTxSetCursor(DVASPECT_CONTENT,	-1,	NULL, NULL, NULL, NULL,
			NULL, pt.x, pt.y);

	return TRUE;
}

/*
 *	CCmbBxWinHost::OnSetFocus(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the button and sends the WM_DRAWITEM message for owner draw
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
 LRESULT CCmbBxWinHost::OnSetFocus(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSetFocus");

    _fFocus = TRUE;

	// Hide the list box
	if (_fListVisible)		    	
    	HideListBox(TRUE, _bSelOk);
    else if (_fOwnerDraw && _cbType == kDropDownList)
    	CbMessageItemHandler(NULL, ITEM_MSG_DRAWCOMBO, 0, 0);
	else    
		DrawEditFocus(NULL);    // Draw the focus 

    // Notify the parent we have the focus iff this function
    // wasn't called in response to LBCB_TRACKING
    if (_fLBCBMessage)
    	_fLBCBMessage = 0;
    else
	    TxNotify(CBN_SETFOCUS, NULL);

	// we return 1 if we are owner draw or if
	// we are a kDropDownList, this is because
	// we have to prevent the message from being passed
	// to _pserv
    return (_cbType == kDropDownList);
}


/*
 *	CCmbBxWinHost::OnKillFocus(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the button and sends the WM_DRAWITEM message for owner draw
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
 LRESULT CCmbBxWinHost::OnKillFocus(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnKillFocus");

	// if we never had focus or if not list window just get out
	if (_hwndList == NULL)
	      return 0;
		
    if ((HWND)wparam != _hwndList) 
    {
		// We only give up the focus if the new window getting the focus
        // doesn't belong to the combo box.
	    
	    // The combo box is losing the focus.  Send buttonup clicks so that
	    // things release the mouse capture if they have it...  If the
	    // pwndListBox is null, don't do anything.  This occurs if the combo box
	    // is destroyed while it has the focus.
	    OnLButtonUp(0L, 0xFFFFFFFFL);

		if (_fListVisible)
			HideListBox(TRUE, FALSE);		
	}

	//bug fix #4013
	if (!_fFocus)
		return 0;
	_fFocus = FALSE;
	
	// Remove Focus Rect
	if (_cbType != kDropDownList)
	{		
		HiliteEdit(FALSE);

		// Hide any selections
		_pserv->TxSendMessage(EM_HIDESELECTION, 1, 0, NULL);
	}
	else if (_fOwnerDraw)
		CbMessageItemHandler(NULL, ITEM_MSG_DRAWCOMBO, 0, 0);
	else
		DrawEditFocus(NULL);
		
		
	TxNotify(CBN_KILLFOCUS, NULL); 

	if (_cbType == kDropDownList)
		return 1;
	return 0;
}


/*
 *	CCmbBxWinHost::OnSize(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the button and sends the WM_DRAWITEM message for owner draw
 *
 *	@rdesc
 *		BOOL = Processed ? FALSE : TRUE
 */
 LRESULT CCmbBxWinHost::OnSize(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnCbSize");

    // only deal with this message if we didn't generate the message and
    // the new size is a valid one
    if (!_fResizing && _hwndList)
    {
    	_fResizing = 1;
    	RECT rc;
    	GetWindowRect(_hwnd, &rc);
    	rc.right -= rc.left;
    	rc.bottom -= rc.top;    	
    	rc.left = rc.top = 0;
    	CbCalcControlRects(&rc, FALSE);
    	
    	// Need to resize the list box
		if (_cbType != kSimple)
			SetDropSize(&_rcList);
		_fResizing = 0;
    } 
	_pserv->TxSendMessage(WM_SIZE, wparam, lparam, NULL);
	CTxtWinHost::OnSize(_hwnd, wparam, (int)LOWORD(lparam), (int)HIWORD(lparam));	
	return FALSE;
}

/*
 *	CCmbBxWinHost::OnGetDlgCode(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the button and sends the WM_DRAWITEM message for owner draw
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
 LRESULT CCmbBxWinHost::OnGetDlgCode(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnGetDlgCode");

	// call the parents GetDlgCode first	
	LRESULT code = DLGC_WANTCHARS | DLGC_WANTARROWS;
	if (_cbType != kDropDownList)
		code |= DLGC_HASSETSEL;

	// If the listbox is dropped and the ENTER key is pressed,
	// we want this message so we can close up the listbox
	if ((lparam != 0) &&
	    (((LPMSG)lparam)->message == WM_KEYDOWN) &&
	    _fListVisible &&
	    ((wparam == VK_RETURN) || (wparam == VK_ESCAPE)))
	{
	    code |= DLGC_WANTMESSAGE;
	}
	_fInDialogBox = TRUE;
		
	return((LRESULT)code);
}

/*
 *	CCmbBxWinHost::OnSetTextEx(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the button and sends the WM_DRAWITEM message for owner draw
 *
 *	@rdesc
 *		BOOL = SUCCESSFUL ? TRUE : FALSE
 */
 LRESULT CCmbBxWinHost::OnSetTextEx(WPARAM wparam, LPARAM lparam)
 {
 	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSetTextEx");

 	WCHAR* psz = (WCHAR*)lparam;

 	while (*psz != L'\r')
 		psz++;
 	*psz = L'\0';

 	//Send message to host
 	_pserv->TxSendMessage(EM_SETTEXTEX, wparam, lparam, NULL);

	//Set the string back to old and send message to listbox
 	*psz = L'\r';
 	psz++;
 	return ::SendMessage(_hwndList, EM_SETTEXTEX, wparam, (LPARAM)psz);
 }

/*
 *	CCmbBxWinHost::OnPaint(WPARAM, LPARAM)
 *
 *	@mfunc
 *		Draws the button and sends the WM_DRAWITEM message for owner draw
 *
 *	@rdesc
 *		BOOL = processed ? 0 : 1
 */
 LRESULT CCmbBxWinHost::OnPaint(WPARAM wparam, LPARAM lparam)
{
	TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnPaint");

	PAINTSTRUCT ps;
	HPALETTE hpalOld = NULL;
	HDC hdc = BeginPaint(_hwnd, &ps);
	RECT rcClient;
	
	// Since we are using the CS_PARENTDC style, make sure
	// the clip region is limited to our client window.
	GetClientRect(_hwnd, &rcClient);
	
	// pass message on to the parentwindow if owner draw
	if (_cbType != kDropDownList || !_fOwnerDraw)
	{
		RECT rcFocus = rcClient;
		
		// Set up the palette for drawing our data
		if (_hpal)
		{
			hpalOld = SelectPalette(hdc, _hpal, TRUE);
			RealizePalette(hdc);
		}

		SaveDC(hdc);

		IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right,
			rcClient.bottom);

		// Fill-in the gap between the button and richedit control
		RECT rcGap;
		if (_fRightAlign)
		{
			rcGap.left = _rcButton.right;
			rcGap.right = rcGap.left + _xInset + 1;
		}
		else
		{
			rcGap.right = _rcButton.left;
			rcGap.left = rcGap.right - _xInset - 1;
		}
		rcGap.top = rcClient.top;
		rcGap.bottom = rcClient.bottom;			
		FillRect(hdc, &rcGap, (HBRUSH)(DWORD_PTR)(((_fDisabled) ? COLOR_BTNFACE : COLOR_WINDOW) + 1));
	
		if (_fFocus && _cbType == kDropDownList)		
		{	
			//First if there is a focus rect then remove the focus rect
			// shrink the focus rect by the inset
			rcFocus.top += _yInset;
			rcFocus.bottom -= _yInset;			
			
			if (_fRightAlign)
				rcFocus.left = _rcButton.right;
			else
				rcFocus.right = _rcButton.left;

			rcFocus.left += _xInset;
			rcFocus.right -= _xInset;

			// We need to erase the focus rect if we haven't already 
			// erased the background
			DrawFocusRect(hdc, &rcFocus);
		}		

		_pserv->TxDraw(
			DVASPECT_CONTENT,  		// Draw Aspect
			-1,						// Lindex
			NULL,					// Info for drawing optimazation
			NULL,					// target device information
			hdc,					// Draw device HDC
			NULL, 				   	// Target device HDC
			(const RECTL *) &rcClient,// Bounding client rectangle
			NULL, 					// Clipping rectangle for metafiles
			&ps.rcPaint,			// Update rectangle
			NULL, 	   				// Call back function
			NULL,					// Call back parameter
			TXTVIEW_ACTIVE);		// What view - the active one!

		// Restore palette if there is one
		if(hpalOld)
			SelectPalette(hdc, hpalOld, TRUE);

		RestoreDC(hdc, -1);

		if(TxGetEffects() == TXTEFFECT_SUNKEN && dwMajorVersion < VERS4)
			DrawSunkenBorder(_hwnd, hdc);

		//Redraw the focus rect, don't have to recalc since we already did above
		if (_fFocus && _cbType == kDropDownList)
			DrawFocusRect(hdc, &rcFocus);

		DrawButton(hdc, _fMousedown);
	}
	else
	{
		// We have to draw the button first because CbMessageItemHandler
		// will perform a IntersectClipRect which will prevent us from
		// drawing the button later
		DrawButton(hdc, _fMousedown);
		
		CbMessageItemHandler(hdc, ITEM_MSG_DRAWCOMBO, 0, 0);
	}
	EndPaint(_hwnd, &ps);

    return FALSE;
}


