/*
 *	@doc TOM
 *
 *	@module tomdoc.cpp - Implement the ITextDocument interface on CTxtEdit |
 *	
 *		This module contains the implementation of the TOM ITextDocument
 *		class as well as the global TOM type-info routines
 *
 *	History: <nl>
 *		sep-95	MurrayS: stubs and auto-doc created <nl>
 *		nov-95	MurrayS: upgrade to top-level TOM interface
 *		dec-95	MurrayS: implemented file I/O methods
 *
 *	@future
 *		1. Implement Begin/EndEditCollection
 *
 *	Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
 */

#include "_common.h"
#include "_range.h"
#include "_edit.h"
#include "_disp.h"
#include "_rtfconv.h"
#include "_select.h"
#include "_font.h"
#include "_tomfmt.h"

ASSERTDATA

// TOM Type Info HRESULT and pointers
HRESULT		g_hrGetTypeInfo = NOERROR;
ITypeInfo *	g_pTypeInfoDoc;
ITypeInfo *	g_pTypeInfoSel;
ITypeInfo *	g_pTypeInfoFont;
ITypeInfo *	g_pTypeInfoPara;
ITypeLib  *	g_pTypeLib;

BYTE szUTF8BOM[] = {0xEF, 0xBB, 0xBF};		// UTF-8 for 0xFEFF

//------------------------ Global TOM Type Info Methods -----------------------------

/*
 *	GetTypeInfoPtrs()
 *
 *	@func
 *		Ensure that global TOM ITypeInfo ptrs are valid (else g_pTypeInfoDoc
 *		is NULL).  Return NOERROR immediately if g_pTypeInfoDoc is not NULL,
 *		i.e., type info ptrs are already valid.
 *
 *	@rdesc
 *		HRESULT = (success) ? NOERROR
 *				: (HRESULT from LoadTypeLib or GetTypeInfoOfGuid)
 *
 *	@comm
 *		This routine should be called by any routine that uses the global
 *		type info ptrs, e.g., IDispatch::GetTypeInfo(), GetIDsOfNames, and
 *		Invoke.  That way if noone is using the type library info, it doesn't
 *		have to be loaded.
 *
 */
HRESULT GetTypeInfoPtrs()
{
	HRESULT	hr;
	CLock	lock;							// Only one thread at a time...
	WCHAR	szModulePath[MAX_PATH];



	if(g_pTypeInfoDoc)						// Type info ptrs already valid
		return NOERROR;

	if(g_hrGetTypeInfo != NOERROR)			// Tried to get before and failed
		return g_hrGetTypeInfo;

	// Obtain the path to this module's executable file
	if (W32->GetModuleFileName(hinstRE, szModulePath, MAX_PATH))
	{
		// Provide the full-path name so LoadTypeLib will not register
		// the type library
		hr = LoadTypeLib(szModulePath, &g_pTypeLib);
		if(hr != NOERROR)
			goto err;

		// Get ITypeInfo pointers with g_pTypeInfoDoc last
		hr = g_pTypeLib->GetTypeInfoOfGuid(IID_ITextSelection, &g_pTypeInfoSel);
		if(hr == NOERROR)
		{
			g_pTypeLib->GetTypeInfoOfGuid(IID_ITextFont,	 &g_pTypeInfoFont);
			g_pTypeLib->GetTypeInfoOfGuid(IID_ITextPara,	 &g_pTypeInfoPara);
			g_pTypeLib->GetTypeInfoOfGuid(IID_ITextDocument, &g_pTypeInfoDoc);

			if(g_pTypeInfoFont && g_pTypeInfoPara && g_pTypeInfoDoc)
				return NOERROR;					// Got 'em all
		}
	}
	hr = E_FAIL;

err:
	Assert("Error getting type info pointers");

	g_pTypeInfoDoc	= NULL;					// Type info ptrs not valid
	g_hrGetTypeInfo	= hr;					// Save HRESULT in case called
	return hr;								//  again
}

/*
 *	ReleaseTypeInfoPtrs()
 *
 *	@func
 *		Release TOM type info ptrs in case they have been defined.
 *		Called when RichEdit dll is being unloaded.
 */
void ReleaseTypeInfoPtrs()
{
	if(g_pTypeInfoDoc)
	{
		g_pTypeInfoDoc->Release();
		g_pTypeInfoSel->Release();
		g_pTypeInfoFont->Release();
		g_pTypeInfoPara->Release();
	}
	if(g_pTypeLib)
		g_pTypeLib->Release();
}

/*
 *	GetTypeInfo(iTypeInfo, &pTypeInfo, ppTypeInfo)
 *
 *	@func
 *		IDispatch helper function to check parameter validity and set
 *		*ppTypeInfo = pTypeInfo if OK
 *
 *	@rdesc
 *		HRESULT
 */
HRESULT GetTypeInfo(
	UINT		iTypeInfo,		//@parm Index of type info to return
	ITypeInfo *&pTypeInfo,		//@parm Address of desired type info ptr
	ITypeInfo **ppTypeInfo)		//@parm Out parm to receive type info
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetTypeInfo");

	if(!ppTypeInfo)
		return E_INVALIDARG;

	*ppTypeInfo = NULL;

	if(iTypeInfo > 1)
		return DISP_E_BADINDEX;

	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
	if(hr == NOERROR)
	{
		*ppTypeInfo = pTypeInfo;				// Have to use reference in
		pTypeInfo->AddRef();					//  case defined in this call
	}
	return hr;
}

/*
 *	MyRead(hfile, pbBuffer, cb, pcb)
 *
 *	@func
 *		Callback function for converting a file into an editstream for
 *		input.
 *
 *	@rdesc
 *		(DWORD)HRESULT
 */
DWORD CALLBACK MyRead(DWORD_PTR hfile, BYTE *pbBuffer, long cb, long *pcb)
{
	if(!hfile)								// No handle defined
		return (DWORD)E_FAIL;

	Assert(pcb);
	*pcb = 0;

	if(!ReadFile((HANDLE)hfile, (void *)pbBuffer, (DWORD)cb, (DWORD *)pcb, NULL))
		return HRESULT_FROM_WIN32(GetLastError());

	return (DWORD)NOERROR;
}

/*
 *	MyWrite(hfile, pbBuffer, cb, pcb)
 *
 *	@func
 *		Callback function for converting a file into an editstream for
 *		output.
 *
 *	@rdesc
 *		(DWORD)HRESULT
 */
DWORD CALLBACK MyWrite(DWORD_PTR hfile, BYTE *pbBuffer, long cb, long *pcb)
{
	if(!hfile)								// No handle defined
		return (DWORD)E_FAIL;

	Assert(pcb);
	*pcb = 0;

	if(!WriteFile((HANDLE)hfile, (void *)pbBuffer, (DWORD)cb, (DWORD *)pcb, NULL))
		return HRESULT_FROM_WIN32(GetLastError());

	return (DWORD)(*pcb ? NOERROR : E_FAIL);
}


//-----------------CTxtEdit IUnknown methods: see textserv.cpp -----------------------------


//------------------------ CTxtEdit IDispatch methods -------------------------

/*
 *	CTxtEdit::GetTypeInfoCount(pcTypeInfo)
 *
 *	@mfunc
 *		Get the number of TYPEINFO elements (1)
 *
 *	@rdesc
 *		HRESULT = (pcTypeInfo) ? NOERROR : E_INVALIDARG;
 */
STDMETHODIMP CTxtEdit::GetTypeInfoCount(
	UINT *pcTypeInfo)	//@parm Out parm to receive count
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetTypeInfoCount");

	if(!pcTypeInfo)
		return E_INVALIDARG;

	*pcTypeInfo = 1;
	return NOERROR;
}

/*
 *	CTxtEdit::GetTypeInfo(iTypeInfo, lcid, ppTypeInfo)
 *
 *	@mfunc
 *		Return ptr to type information object for ITextDocument interface
 *
 *	@rdesc
 *		HRESULT
 */
STDMETHODIMP CTxtEdit::GetTypeInfo(
	UINT		iTypeInfo,		//@parm Index of type info to return
	LCID		lcid,			//@parm Local ID of type info
	ITypeInfo **ppTypeInfo)		//@parm Out parm to receive type info
 {
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetTypeInfo");

	return ::GetTypeInfo(iTypeInfo, g_pTypeInfoDoc, ppTypeInfo);
}

/*
 *	CTxtEdit::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid)
 *
 *	@mfunc
 *		Get DISPIDs for all TOM methods and properties
 *
 *	@rdesc
 *		HRESULT
 *
 *	@devnote
 *		This routine tries to find DISPIDs using the type information for
 *		ITextDocument. If that fails, it asks the selection to find the
 *		DISPIDs.
 */
STDMETHODIMP CTxtEdit::GetIDsOfNames(
	REFIID		riid,			//@parm Interface ID to interpret names for
	OLECHAR **	rgszNames,		//@parm Array of names to be mapped
	UINT		cNames,			//@parm Count of names to be mapped
	LCID		lcid,			//@parm Local ID to use for interpretation
	DISPID *	rgdispid)		//@parm Out parm to receive name mappings
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetIDsOfNames");

	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
	if(hr != NOERROR)
		return hr;
		
	hr = g_pTypeInfoDoc->GetIDsOfNames(rgszNames, cNames, rgdispid);

	if(hr == NOERROR)							// Succeeded in finding an
		return NOERROR;							//  ITextDocument method

	IDispatch *pSel = (IDispatch *)GetSel();	// See if the selection knows
												//  the desired method
	if(!pSel)
		return hr;								// No selection

	return pSel->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
}

/*
 *	CTxtEdit::Invoke(dispidMember, riid, lcid, wFlags, pdispparams,
 *					  pvarResult, pexcepinfo, puArgError)
 *	@mfunc
 *		Invoke members for all TOM DISPIDs, i.e., for ITextDocument,
 *		ITextSelection, ITextRange, ITextFont, and ITextPara.  TOM DISPIDs
 *		for all but ITextDocument are delegated to the selection object.
 *
 *	@rdesc
 *		HRESULT
 *
 *	@devnote
 *		This routine trys to invoke ITextDocument members if the DISPID is
 *		in the range 0 thru 0xff.  It trys to invoke ITextSelection members if
 *		the DISPID is in the range 0x100 thru 0x4ff (this includes
 *		ITextSelection, ITextRange, ITextFont, and ITextPara).  It returns
 *		E_MEMBERNOTFOUND for DISPIDs outside these ranges.
 */
STDMETHODIMP CTxtEdit::Invoke(
	DISPID		dispidMember,	//@parm Identifies member function
	REFIID		riid,			//@parm Pointer to interface ID
	LCID		lcid,			//@parm Locale ID for interpretation
	USHORT		wFlags,			//@parm Flags describing context of call
	DISPPARAMS *pdispparams,	//@parm Ptr to method arguments
	VARIANT *	pvarResult,		//@parm Out parm for result (if not NULL)
	EXCEPINFO * pexcepinfo,		//@parm Out parm for exception info
	UINT *		puArgError)		//@parm Out parm for error
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Invoke");

	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
	if(hr != NOERROR)
		return hr;
		
	if((DWORD)dispidMember < 0x100)				// ITextDocment method
		return g_pTypeInfoDoc->Invoke((IDispatch *)this, dispidMember, wFlags,
							 pdispparams, pvarResult, pexcepinfo, puArgError);

	IDispatch *pSel = (IDispatch *)GetSel();	// See if the selection has
												//  the desired method
	if(pSel && (DWORD)dispidMember <= 0x4ff)
		return pSel->Invoke(dispidMember, riid, lcid, wFlags,
							 pdispparams, pvarResult, pexcepinfo, puArgError);

	return DISP_E_MEMBERNOTFOUND;
}


//--------------------- ITextDocument Methods/Properties -----------------------

/*
 *	ITextDocument::BeginEditCollection()
 *
 *	@mfunc
 *		Method that turns on undo grouping
 *
 *	@rdesc
 *		HRESULT = (undo enabled) ? NOERROR : S_FALSE
 */
STDMETHODIMP CTxtEdit::BeginEditCollection ()
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::BeginEditCollection");

	return E_NOTIMPL;
}

/*
 *	ITextDocument::EndEditCollection() 
 *
 *	@mfunc
 *		Method that turns off undo grouping
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::EndEditCollection () 
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::EndEditCollection");

	return E_NOTIMPL;
}

/*
 *	ITextDocument::Freeze(long *pValue) 
 *
 *	@mfunc
 *		Method to increment the freeze count. If this count is nonzero,
 *		screen updating is disabled.  This allows a sequence of editing
 *		operations to be performed without the performance loss and
 *		flicker of screen updating.  See Unfreeze() to decrement the
 *		freeze count.
 *
 *	@rdesc
 *		HRESULT = (screen updating disabled) ? NOERROR : S_FALSE
 *
 *	@todo
 *		What about APIs like EM_LINEFROMCHAR that don't yet know how to
 *		react to a frozen display?
 */
STDMETHODIMP CTxtEdit::Freeze (
	long *pCount)		//@parm Out parm to receive updated freeze count
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Freeze");

    CCallMgr callmgr(this);

	if(_pdp)
	{
		CCallMgr callmgr(this);

		_pdp->Freeze();
		if(_pdp->IsFrozen())
			_cFreeze++;
		else
			_cFreeze = 0;
	}

	if(pCount)
		*pCount = _cFreeze;

	return _cFreeze ? NOERROR : S_FALSE;
}

/*
 *	ITextDocument::GetDefaultTabStop (pValue) 
 *
 *	@mfunc
 *		Property get method that gets the default tab stop to be
 *		used whenever the explicit tabs don't extend far enough.
 *
 *	@rdesc
 *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
 */
STDMETHODIMP CTxtEdit::GetDefaultTabStop (
	float *	pValue)		//@parm Out parm to receive default tab stop
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetDefaultTabStop");

	if(!pValue)
		return E_INVALIDARG;
                                                                        
	const LONG lTab = GetDefaultTab();

	*pValue = TWIPS_TO_FPPTS(lTab);

	return NOERROR;
}

/*
 *	CTxtEdit::GetName (pName)
 *
 *	@mfunc
 *		Retrieve ITextDocument filename
 *
 *	@rdesc
 *		HRESULT = (!<p pName>) ? E_INVALIDARG :
 *				  (no name) ? S_FALSE :
 *				  (if not enough RAM) ? E_OUTOFMEMORY : NOERROR
 */
STDMETHODIMP CTxtEdit::GetName (
	BSTR * pName)		//@parm Out parm to receive filename
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetName");

	if(!pName)
		return E_INVALIDARG;

	*pName = NULL;
	if(!_pDocInfo || !_pDocInfo->_pName)
		return S_FALSE;

	*pName = SysAllocString(_pDocInfo->_pName);
	
	return *pName ? NOERROR : E_OUTOFMEMORY;
}

/*
 *	ITextDocument::GetSaved (pValue) 
 *
 *	@mfunc
 *		Property get method that gets whether this instance has been
 *		saved, i.e., no changes since last save
 *
 *	@rdesc
 *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
 *
 *	@comm
 *		Next time to aid C/C++ clients, we ought to make pValue optional
 *		and return S_FALSE if doc isn't saved, i.e., like our other
 *		boolean properties (see, e.g., ITextRange::IsEqual())
 */
STDMETHODIMP CTxtEdit::GetSaved (
	long *	pValue)		//@parm Out parm to receive Saved property
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetSaved");

	if(!pValue)
		return E_INVALIDARG;

	*pValue = _fSaved ? tomTrue : tomFalse;
	return NOERROR;
}

/*
 *	ITextDocument::GetSelection (ITextSelection **ppSel) 
 *
 *	@mfunc
 *		Property get method that gets the active selection. 
 *
 *	@rdesc
 *		HRESULT = (!ppSel) ? E_INVALIDARG :
 *				  (if active selection exists) ? NOERROR : S_FALSE
 */
STDMETHODIMP CTxtEdit::GetSelection (
	ITextSelection **ppSel)	//@parm Out parm to receive selection pointer
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetSelection");

	if(!ppSel)
		return E_INVALIDARG;

	CTxtSelection *psel = GetSel();

	*ppSel = (ITextSelection *)psel;

	if( psel )
	{
		(*ppSel)->AddRef();
		return NOERROR;
	}

	return S_FALSE;
}

/*
 *	CTxtEdit::GetStoryCount(pCount)
 *
 *	@mfunc
 *		Get count of stories in this document.
 *
 *	@rdesc
 *		HRESULT = (!<p pCount>) ? E_INVALIDARG : NOERROR
 */
STDMETHODIMP CTxtEdit::GetStoryCount (
	LONG *pCount)		//@parm Out parm to receive count of stories
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetStoryCount");

	if(!pCount)
		return E_INVALIDARG;

	*pCount = 1;
	return NOERROR;
}

/*
 *	ITextDocument::GetStoryRanges(ITextStoryRanges **ppStories) 
 *
 *	@mfunc
 *		Property get method that gets the story collection object
 *		used to enumerate the stories in a document.  Only invoke this
 *		method if GetStoryCount() returns a value greater than one.
 *
 *	@rdesc
 *		HRESULT = (if Stories collection exists) ? NOERROR : E_NOTIMPL
 */
STDMETHODIMP CTxtEdit::GetStoryRanges (
	ITextStoryRanges **ppStories) 	//@parm Out parm to receive stories ptr
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetStoryRanges");

	return E_NOTIMPL;
}

/*
 *	ITextDocument::New() 
 *
 *	@mfunc
 *		Method that closes the current document and opens a document
 *		with a default name.  If changes have been made in the current
 *		document since the last save and document file information exists,
 *		the current document is saved.
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::New ()
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::New");

	CloseFile(TRUE);	 					// Save and close file
	return SetText(NULL, 0, CP_ULE);
}

/*
 *	ITextDocument::Open(pVar, Flags, CodePage)
 *
 *	@mfunc
 *		Method that opens the document specified by pVar.
 *
 *	@rdesc
 *		HRESULT = (if success) ? NOERROR : E_OUTOFMEMORY
 *
 *	@future
 *		Handle IStream
 */
STDMETHODIMP CTxtEdit::Open (
	VARIANT *	pVar,		//@parm Filename or IStream
	long		Flags,		//@parm Read/write, create, and share flags
	long		CodePage)	//@parm Code page to use
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Open");

	LONG		cb;								// Byte count for RTF check
	EDITSTREAM	es		= {0, NOERROR, MyRead};
	BOOL		fReplaceSel = Flags & tomPasteFile;
	HCURSOR		hcur;
	LRESULT		lres;
	TCHAR		szType[10];

	if(!pVar || CodePage && !IsUnicodeCP(CodePage) && !IsValidCodePage(CodePage))
		return E_INVALIDARG;					// IsValidCodePage(0) fails
												//  even tho CP_ACP = 0 (!)
	if((Flags & 0xF) >= tomHTML)				// RichEdit only handles auto,
		return E_NOTIMPL;						//  plain text, & RTF formats

	if(!fReplaceSel)							// If not replacing selection,
		New();									//  save current file and
												//  delete current text
	CDocInfo * pDocInfo = GetDocInfo();
	if(!pDocInfo)
		return E_OUTOFMEMORY;

	pDocInfo->_wFlags = (WORD)(Flags & ~0xf0);	// Save flags (w/o creation)

	// Process access, share, and create flags
	DWORD dwAccess = (Flags & tomReadOnly)
		? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);

	DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
	if(Flags & tomShareDenyRead)
		dwShare &= ~FILE_SHARE_READ;

	if(Flags & tomShareDenyWrite)
		dwShare &= ~FILE_SHARE_WRITE;

	DWORD dwCreate = (Flags >> 4) & 0xf;
	if(!dwCreate)								// Flags nibble 2 must contain
		dwCreate = OPEN_ALWAYS;					//  CreateFile's dwCreate

	if(pVar->vt == VT_BSTR && SysStringLen(pVar->bstrVal))
	{
		es.dwCookie = (DWORD_PTR)CreateFile(pVar->bstrVal, dwAccess, dwShare,
							 NULL, dwCreate, FILE_ATTRIBUTE_NORMAL, NULL);
		if((HANDLE)es.dwCookie == INVALID_HANDLE_VALUE)
			return HRESULT_FROM_WIN32(GetLastError());

		if(!fReplaceSel)						// If not replacing selection,
		{										//  allocate new pName
			pDocInfo->_pName = SysAllocString(pVar->bstrVal);
			if (!pDocInfo->_pName)
				return E_OUTOFMEMORY;
			pDocInfo->_hFile = (HANDLE)es.dwCookie;
			pDocInfo->_wFlags |= tomTruncateExisting;	// Setup Saves
		}
	}
	else
	{
		// FUTURE: check for IStream; if not, then fail
		return E_INVALIDARG;
	}

	Flags &= 0xf;								// Isolate conversion flags

	// Get first few bytes of file to check for RTF and Unicode BOM
	(*es.pfnCallback)(es.dwCookie, (LPBYTE)szType, 10, &cb);

	Flags = (!Flags || Flags == tomRTF) && IsRTF((char *)szType, cb)
		  ? tomRTF : tomText;

	LONG j = 0;									// Default rewind to 0
	if (Flags == tomRTF)						// RTF
		Flags = SF_RTF;							// Setup EM_STREAMIN for RTF
	else
	{											// If it starts with
		Flags = SF_TEXT;						// Setup EM_STREAMIN for text
		if(cb > 1 && *(WORD *)szType == BOM)	//  Unicode byte-order mark
		{										//  (BOM) file is Unicode, so
			Flags = SF_TEXT | SF_UNICODE;		//  use Unicode code page and
			j = 2;								//  bypass the BOM
		}										
		else if(cb > 1 && *(WORD *)szType == RBOM)// Big Endian BOM
		{										//  BOM
			Flags = SF_TEXT | SF_USECODEPAGE | (CP_UBE << 16);
			j = 2;								// Bypass the BOM
		}										
		else if(cb > 2 && W32->IsUTF8BOM((BYTE *)szType))
		{
			Flags = SF_TEXT | SF_USECODEPAGE | (CP_UTF8 << 16);
			j = 3;
		}
		else if(CodePage == CP_ULE)
			Flags = SF_TEXT | SF_UNICODE;

		else if(CodePage)
			Flags = SF_TEXT | SF_USECODEPAGE | (CodePage << 16);
	}

	SetFilePointer((HANDLE)es.dwCookie, j, NULL, FILE_BEGIN);	// Rewind

	if(fReplaceSel)
		Flags |= SFF_SELECTION;

	Flags |= SFF_KEEPDOCINFO;

	hcur = TxSetCursor(LoadCursor(NULL, IDC_WAIT));
	TxSendMessage(EM_STREAMIN, Flags, (LPARAM)&es, &lres);
	TxSetCursor(hcur);

	if(dwShare == (FILE_SHARE_READ | FILE_SHARE_WRITE) || fReplaceSel)
	{											// Full sharing or replaced
		CloseHandle((HANDLE)es.dwCookie);		//  selection, so close file
		if(!fReplaceSel)						// If replacing selection,
			pDocInfo->_hFile = NULL;				//  leave _pDocInfo->_hFile
	}
	_fSaved = fReplaceSel ? FALSE : TRUE;		// No changes yet unless
	return (HRESULT)es.dwError;
}

/*
 *	ITextDocument::Range(long cpFirst, long cpLim, ITextRange **ppRange)  
 *
 *	@mfunc
 *		Method that gets a text range on the active story of the document
 *
 *	@rdesc
 *		HRESULT = (!ppRange) ? E_INVALIDARG : 
 *				  (if success) ? NOERROR : E_OUTOFMEMORY
 */
STDMETHODIMP CTxtEdit::Range (
	long cpFirst, 				//@parm	Non active end of new range
	long cpLim, 				//@parm Active end of new range
	ITextRange ** ppRange)		//@parm Out parm to receive range
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Range");

	if(!ppRange)
		return E_INVALIDARG;

	*ppRange = new CTxtRange(this, cpFirst, cpFirst - cpLim);
	
	if( *ppRange )
	{
		(*ppRange)->AddRef();		// CTxtRange() doesn't AddRef() because
		return NOERROR;				//  it's used internally for things
	}								//  besides TOM

	return E_OUTOFMEMORY;
}

/*
 *	ITextDocument::RangeFromPoint(long x, long y, ITextRange **ppRange) 
 *
 *	@mfunc
 *		Method that gets the degenerate range corresponding (at or nearest)
 *		to the point with the screen coordinates x and y.
 *
 *	@rdesc
 *		HRESULT = (!ppRange) ? E_INVALIDARG :
 *				  (if out of RAM) ? E_OUTOFMEMORY :
 *				  (if range exists) ? NOERROR : S_FALSE
 */
STDMETHODIMP CTxtEdit::RangeFromPoint (
	long	x,				//@parm Horizontal coord of point to use
	long	y,				//@parm	Vertical   coord of point to use
	ITextRange **ppRange)	//@parm Out parm to receive range
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::RangeFromPoint");

	if(!ppRange)
		return E_INVALIDARG;

	*ppRange = (ITextRange *) new CTxtRange(this, 0, 0);

	if(!*ppRange)
		return E_OUTOFMEMORY;

	(*ppRange)->AddRef();				// CTxtRange() doesn't AddRef()
	return (*ppRange)->SetPoint(x, y, 0, 0);
}

/*
 *	ITextDocument::Redo(long Count, long *pCount) 
 *
 *	@mfunc
 *		Method to perform the redo operation Count times
 *
 *	@rdesc
 *		HRESULT = (if Count redos performed) ? NOERROR : S_FALSE
 */
STDMETHODIMP CTxtEdit::Redo (
	long	Count,		//@parm Number of redo operations to perform
	long *	pCount)		//@parm Number of redo operations performed
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Redo");
	CCallMgr	callmgr(this);

	LONG i = 0;

	// Freeze the display during the execution of the anti-events
	CFreezeDisplay fd(_pdp);
	HRESULT hr = NOERROR;

	for ( ; i < Count; i++)					//  loop does nothing
	{
		hr = PopAndExecuteAntiEvent(_predo, 0);
		if(hr != NOERROR)
			break;
	}

	if(pCount)
		*pCount = i;

	return hr == NOERROR && i != Count ? S_FALSE : hr;
}

/*
 *	ITextDocument::Save(pVar, Flags, CodePage) 
 *
 *	@mfunc
 *		Method that saves this ITextDocument to the target pVar,
 *		which is a VARIANT that can be a filename, an IStream, or NULL.  If
 *		NULL, the filename given by this document's name is used.  It that,
 *		in turn, is NULL, the method fails.  If pVar specifies a filename,
 *		that name should replace the current Name property.
 *
 *	@rdesc
 *		HRESULT = (!pVar) ? E_INVALIDARG : 
 *				  (if success) ? NOERROR : E_FAIL
 *
 *	@devnote
 *		This routine can be called with NULL arguments
 */
STDMETHODIMP CTxtEdit::Save (
	VARIANT *	pVar,		//@parm Save target (filename or IStream)
	long		Flags,		//@parm Read/write, create, and share flags
	long		CodePage)	//@parm Code page to use
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Save");

	LONG		cb;			// Byte count for writing Unicode BOM
	EDITSTREAM	es		= {0, NOERROR, MyWrite};
	BOOL		fChange	= FALSE;				// No doc info change yet
	HCURSOR		hcur;
	CDocInfo *	pDocInfo = GetDocInfo();

	if(CodePage && !IsUnicodeCP(CodePage) && !IsValidCodePage(CodePage) ||
	   (DWORD)Flags > 0x1fff || Flags & tomReadOnly)
	{
		return E_INVALIDARG;
	}
	if((Flags & 0xf) >= tomHTML)				// RichEdit only handles auto,
		return E_NOTIMPL;						//  plain text, & RTF formats

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	if (pVar && pVar->vt == VT_BSTR &&			// Filename string
		pVar->bstrVal &&
		SysStringLen(pVar->bstrVal) &&			// NonNULL filename specified
		(!pDocInfo->_pName ||
		 OLEstrcmp(pVar->bstrVal, pDocInfo->_pName)))
	{											// Filename differs
		fChange = TRUE;							// Force write to new file
		CloseFile(FALSE);						// Close current file; no save
		pDocInfo->_pName = SysAllocString(pVar->bstrVal);
		if(!pDocInfo->_pName)
			return E_OUTOFMEMORY;
		pDocInfo->_wFlags &= ~0xf0;				// Kill previous create mode
	}

	DWORD flags = pDocInfo->_wFlags;
	if(!(Flags & 0xF))							// If convert flags are 0,									
		Flags |= flags & 0xF;					//  use values in doc info
	if(!(Flags & 0xF0))							// If create flags are 0,									
		Flags |= flags & 0xF0;					//  use values in doc info
	if(!(Flags & 0xF00))						// If share flags are 0,									
		Flags |= flags & 0xF00;					//  use values in doc info
	if(!CodePage)								// If code page is 0,
		CodePage = pDocInfo->_wCpg;				//  use code page in doc info

	if((DWORD)Flags != flags ||					// If flags or code page	
	   (WORD)CodePage != pDocInfo->_wCpg)		//  changed, force write
	{
		fChange = TRUE;
	}
	pDocInfo->_wFlags = (WORD)Flags;				// Save flags

	// Yikes, nowhere to save.  bail-out now
	if(!_pDocInfo->_pName)
		return E_FAIL;

	if(_fSaved && !fChange)						// No changes, so assume
		return NOERROR;							//  saved file is up to date

	DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
	if(Flags & tomShareDenyRead)
		dwShare &= ~FILE_SHARE_READ;

	if(Flags & tomShareDenyWrite)
		dwShare &= ~FILE_SHARE_WRITE;

	DWORD dwCreate = (Flags >> 4) & 0xF;
	if(!dwCreate)
		dwCreate = CREATE_NEW;

	if(pDocInfo->_hFile)
	{
		CloseHandle(pDocInfo->_hFile);			// Close current file handle
		pDocInfo->_hFile = NULL;
	}

	es.dwCookie = (DWORD_PTR)CreateFile(pDocInfo->_pName, GENERIC_READ | GENERIC_WRITE, dwShare, NULL,
							dwCreate, FILE_ATTRIBUTE_NORMAL, NULL);
	if((HANDLE)es.dwCookie == INVALID_HANDLE_VALUE)
		return HRESULT_FROM_WIN32(GetLastError());

	pDocInfo->_hFile = (HANDLE)es.dwCookie;

	Flags &= 0xF;								// Isolate conversion flags
	if(Flags == tomRTF)							// RTF
		Flags = SF_RTF;							// Setup EM_STREAMOUT for RTF
	else
	{											
		Flags = SF_TEXT;						// Setup EM_STREAMOUT for text
		if(IsUnicodeCP(CodePage) || CodePage == CP_UTF8)
		{										// If Unicode, start file with
			LONG  j = 2;						//  Unicode byte order mark
			WORD  wBOM = BOM;
			WORD  wRBOM = RBOM;
			BYTE *pb = (BYTE *)&wRBOM;			// Default Big Endian Unicode				
											
			if(CodePage == CP_UTF8)
			{
				j = 3;
				pb = szUTF8BOM;
			}
			else if(CodePage == CP_ULE)			// Little Endian Unicode
			{
				Flags = SF_TEXT | SF_UNICODE;
				pb = (BYTE *)&wBOM;
			}
			(*es.pfnCallback)(es.dwCookie, pb, j, &cb);
		}
	}
	if(CodePage && CodePage != CP_ULE)
		Flags |= SF_USECODEPAGE | (CodePage << 16);

	hcur = TxSetCursor(LoadCursor(NULL, IDC_WAIT));
	TxSendMessage(EM_STREAMOUT, Flags, (LPARAM)&es, NULL);
	TxSetCursor(hcur);

	if(dwShare == (FILE_SHARE_READ | FILE_SHARE_WRITE))
	{											// Full sharing, so close
		CloseHandle(pDocInfo->_hFile);			//  current file handle
		pDocInfo->_hFile = NULL;
	}
	_fSaved = TRUE;								// File is saved
	return (HRESULT)es.dwError;
}

/*
 *	ITextDocument::SetDefaultTabStop (Value) 
 *
 *	@mfunc
 *		Property set method that sets the default tab stop to be
 *		used whenever the explicit tabs don't extend far enough.
 *
 *	@rdesc
 *		HRESULT = (Value < 0) ? E_INVALIDARG : NOERROR
 */
STDMETHODIMP CTxtEdit::SetDefaultTabStop (
	float Value)		//@parm Out parm to receive default tab stop
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetDefaultTabStop");

	if(Value <= 0)
		return E_INVALIDARG;

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	pDocInfo->_dwDefaultTabStop = FPPTS_TO_TWIPS(Value);

	_pdp->UpdateView();
	return NOERROR;
}

/*
 *	ITextDocument::SetSaved (Value) 
 *
 *	@mfunc
 *		Property set method that sets whether this instance has been
 *		saved, i.e., no changes since last save
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::SetSaved (
	long	Value)		//@parm New value of Saved property
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetSaved");

	_fSaved = Value ? TRUE : FALSE;
	return NOERROR;
}

/*
 *	ITextDocument::Undo(Count, *pCount) 
 *
 *	@mfunc
 *		Method to perform the undo operation Count times or to control
 *		the nature of undo processing. Count = 0 stops undo processing
 *		and discards any saved undo states.  Count = -1 turns on undo
 *		processing with the default undo limit.  Count = tomSuspend
 *		suspends undo processing, but doesn't discard saved undo states,
 *		and Count = tomResume resumes undo processing with the undo states
 *		active when Count = tomSuspend was given.
 *
 *	@rdesc
 *		HRESULT = (if Count undos performed) ? NOERROR : S_FALSE
 */
STDMETHODIMP CTxtEdit::Undo (
	long	Count,		//@parm Count of undo operations to perform
						//		0 stops undo processing
						//		-1 turns restores it
	long *	pCount)		//@parm Number of undo operations performed
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Undo");
	CCallMgr callmgr(this);

	LONG i = 0;

	// Freeze display during execution of anti-events
	CFreezeDisplay fd(_pdp);
	HRESULT hr = NOERROR;
											// Note: for Count <= 0,
	for ( ; i < Count; i++)					//  loop does nothing
	{
		hr = PopAndExecuteAntiEvent(_pundo, 0);
		if(hr != NOERROR)
			break;
	}

	if(pCount)
		*pCount = i;

	if(Count <= 0)							
		i = HandleSetUndoLimit(Count);

	return hr == NOERROR && i != Count ? S_FALSE : hr;
}

/*
 *	ITextDocument::Unfreeze(pCount) 
 *
 *	@mfunc
 *		Method to decrement freeze count.  If this count goes to zero,
 *		screen updating is enabled.  This method cannot decrement the
 *		count below zero.
 *
 *	@rdesc
 *		HRESULT = (screen updating enabled) ? NOERROR : S_FALSE
 *
 *	@devnote
 *		The display maintains its own private reference count which may
 *		temporarily exceed the reference count of this method.  So even
 *		if this method indicates that the display is unfrozen, it may be
 *		for a while longer.
 */
STDMETHODIMP CTxtEdit::Unfreeze (
	long *pCount)		//@parm Out parm to receive updated freeze count
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Unfreeze");

    CCallMgr callmgr(this);

	if(_cFreeze)
	{
		CCallMgr callmgr(this);

		AssertSz(_pdp && _pdp->IsFrozen(),
			"CTxtEdit::Unfreeze: screen not frozen but expected to be");
		_cFreeze--;
		_pdp->Thaw();
	}

	if(pCount)
		*pCount = _cFreeze;

	return _cFreeze ? S_FALSE : NOERROR;
}


//----------------------- ITextDocument2 Methods -----------------------
/*
 *	ITextDocument2::AttachMsgFilter(pFilter) 
 *
 *	@mfunc
 *		Method to attach a new message filter to the edit instance.
 *      All window messages received by the edit instance will be forwarded
 *      to the message filter.  The message filter must be bound to the document
 *		before it can be used (Refer to the ITextMessageFilter API).
 *
 *	@rdesc
 *		HRESULT = filter succesfully attached ? NOERROR : QI failure.
 */
STDMETHODIMP CTxtEdit::AttachMsgFilter (
	IUnknown *pFilter)		//@parm the IUnknown for the new message filter
{
	ITextMsgFilter *pMsgFilter = NULL;

	HRESULT hr = pFilter->QueryInterface(IID_ITextMsgFilter, (void **)&pMsgFilter);
	if (SUCCEEDED(hr))
	{
		if (_pMsgFilter)
			_pMsgFilter->AttachMsgFilter(pMsgFilter);
		else
			_pMsgFilter = pMsgFilter;
	}
	return hr;
}

/*
 *	ITextDocument2::GetEffectColor(Index, pcr)
 *
 *	@mfunc
 *		Method to retrieve the COLORREF color used in special attribute
 *		displays.  The first 15 values are for special underline colors 1 - 15.
 *      Later we may define indices for other effects, e.g., URLs, strikeout.
 *		If an index between 1 and 15 hasn't been defined by an appropriate
 *		call to ITextDocument2:SetEffectColor(), the corresponding WORD
 *		default color value given by g_Colors[] is returned.
 *
 *	@rdesc
 *		HRESULT = (valid active color index)
 *				? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::GetEffectColor(
	long	  Index,		//@parm Which special color to get
	COLORREF *pcr)			//@parm Out parm for color
{
	if(!pcr)
		return E_INVALIDARG;

	if(!IN_RANGE(1, Index, 15))
	{
		*pcr = (COLORREF)tomUndefined;
		return E_INVALIDARG;
	}

	*pcr = g_Colors[Index];

	CDocInfo *pDocInfo = GetDocInfo();
	if(!pDocInfo)
		return E_OUTOFMEMORY;
	
	Index--;
	if (Index < pDocInfo->_cColor &&
		pDocInfo->_prgColor[Index] != (COLORREF)tomUndefined)
	{
		*pcr = pDocInfo->_prgColor[Index];
	}
	return NOERROR;
}

/*
 *	ITextDocument2::SetEffectColor(Index, cr) 
 *
 *	@mfunc
 *		Method to save the Index'th special document color.  Indices
 *      1 - 15 are defined for underlining.  Later we may define
 *		indices for other effects, e.g., URLs, strikeout.
 *
 *	@rdesc
 *		HRESULT = (valid index)
 *				? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::SetEffectColor(
	long	 Index,			//@parm Which special color to set
	COLORREF cr)			//@parm Color to use
{
	CDocInfo *pDocInfo = GetDocInfo();
	if(!pDocInfo)
		return E_OUTOFMEMORY;

	Index--;
	if(!IN_RANGE(0, Index, 14))
		return E_INVALIDARG;

	if(Index >= pDocInfo->_cColor)
	{
		LONG	  cColor   = (Index + 4) & ~3;		// Round up to 4
		COLORREF *prgColor = (COLORREF *)PvReAlloc(pDocInfo->_prgColor,
												  cColor*sizeof(COLORREF));
		if(!prgColor)
			return E_OUTOFMEMORY;

		for(LONG i = pDocInfo->_cColor; i < cColor; i++)
			prgColor[i] = (COLORREF)tomUndefined;

		pDocInfo->_cColor   = (char)cColor;
		pDocInfo->_prgColor = prgColor;
	}
	pDocInfo->_prgColor[Index] = cr;	
	return NOERROR;
}

/*
 *	ITextDocument2::SetCaretType(CaretType) 
 *
 *	@mfunc
 *		Method to sllow programmatic control over the caret type.
 *      The form of the control is TBD as is its interaction with
 *      existing formatting (e.g. font size and italics).
 *
 *	@rdesc
 *		HRESULT = caret type is one we understand ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::SetCaretType(
	long CaretType)		//@parm specification of caret type to use
{
	// For now, just care about Korean block craet.
	if (CaretType == tomKoreanBlockCaret)
		_fKoreanBlockCaret = TRUE;
	else if (CaretType == tomNormalCaret)
		_fKoreanBlockCaret = FALSE;
	else
		return E_INVALIDARG;

	if (_psel && _psel->IsCaretShown() && _fFocus)
	{
		_psel->CreateCaret();
		TxShowCaret(TRUE);
	}
	return NOERROR;
}

/*
 *	ITextDocument2::GetCaretType(pCaretType)
 *
 *	@mfunc
 *		Method to retrieve the previously set caret type.
 *      TBD.  Can one get it without setting it?
 *
 *	@rdesc
 *		HRESULT = caret info OK ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::GetCaretType(
	long *pCaretType)		//@parm current caret type specification
{
	if (!pCaretType)
		return E_INVALIDARG;

	*pCaretType = _fKoreanBlockCaret ? tomKoreanBlockCaret : tomNormalCaret;
	return NOERROR;
}

/*
 *	ITextDocument2::GetImmContext(pContext) 
 *
 *	@mfunc
 *		Method to retrieve the IMM context from our host.
 *
 *	@rdesc
 *		HRESULT = ImmContext available ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::GetImmContext(
	long *pContext)		//@parm Imm context
{
	if (!pContext)
		return E_INVALIDARG;

	*pContext = 0;

	if (!_fInOurHost)
	{
		// ask host for Imm Context
		HIMC hIMC = TxImmGetContext();
		
		*pContext = (long) hIMC;			
	}
	
	return *pContext ? NOERROR : S_FALSE;
}

/*
 *	ITextDocument2::ReleaseImmContext(Context) 
 *
 *	@mfunc
 *		Method to release the IMM context.
 *
 *	@rdesc
 *		HRESULT = ImmContext available ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::ReleaseImmContext(
	long Context)		//@parm Imm context to be release
{
	if (!_fInOurHost)
	{
		// ask host to release Imm Context
		TxImmReleaseContext((HIMC)Context);
		
		return NOERROR;			
	}
	
	return S_FALSE;
}

/*
 *	ITextDocument2::GetPreferredFont(cp, lCodePage, lOption, lCurCodePage, lCurFontSize,
 *		,pFontName, pPitchAndFamily, pNewFontSize) 
 *
 *	@mfunc
 *		Method to retrieve the preferred font name and pitch and family at a 
 *		given cp and codepage.
 *
 *	@rdesc
 *		HRESULT = FontName available ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::GetPreferredFont(
	long cp,				//@parm cp
	long lCodepage,			//@parm codepage preferred
	long lOption,			//@parm option for matching current font
	long lCurCodePage,		//@parm current codepage
	long lCurFontSize,		//@parm current font size
	BSTR *pFontName,		//@parm preferred fontname
	long *pPitchAndFamily,	//@parm pitch and family 
	long *plNewFontSize)	//@parm new font size preferred 
{
	if (!pFontName || !IN_RANGE(IGNORE_CURRENT_FONT, lOption, MATCH_FONT_SIG))
		return E_INVALIDARG;

	if (!IsAutoFont())		// EXIT if auto font is turned off
		return S_FALSE;

	CRchTxtPtr	rtp(this, 0);
	CCFRunPtr	rp(rtp);
	short		iFont;
	short		yHeight;
	BYTE		bPitchAndFamily;
	BYTE		iCharRep = CharRepFromCodePage(lCodepage);

	rp.Move(cp);
	if (rp.GetPreferredFontInfo(
			iCharRep,
			iCharRep,
			iFont,
			yHeight,
			bPitchAndFamily,
			-1,
			lOption))
	{
		if (*pFontName)
			wcscpy(*pFontName, GetFontName((LONG)iFont));
		else
		{
			*pFontName = SysAllocString(GetFontName((LONG)iFont));
			if (!*pFontName)
				return E_OUTOFMEMORY;
		}	

		if (pPitchAndFamily)
			*pPitchAndFamily = bPitchAndFamily;

		// Calc the new font size if needed
		if (plNewFontSize)
		{
			*plNewFontSize = lCurFontSize;
			if (_fAutoFontSizeAdjust && lCodepage != lCurCodePage)
				*plNewFontSize = yHeight / TWIPS_PER_POINT;			// Set the preferred size
		}
		return S_OK;
	}
	return E_FAIL;
}

/*
 *	ITextDocument2::GetNotificationMode( long *plMode ) 
 *
 *	@mfunc
 *		Method to retrieve the current notification mode.
 *
 *	@rdesc
 *		HRESULT = notification mode available ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::GetNotificationMode(
	long *plMode)		//@parm current notification mode
{
	if (!plMode)
		return E_INVALIDARG;

	*plMode = _fSuppressNotify ? tomFalse : tomTrue;

	return NOERROR;
}

/*
 *	ITextDocument2::SetNotificationMode(lMode) 
 *
 *	@mfunc
 *		Method to set the current notification mode.
 *
 *	@rdesc
 *		HRESULT = notification mode set ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::SetNotificationMode(
	long lMode)		//@parm new notification mode
{
	if (lMode == tomFalse)
		_fSuppressNotify = 1;
	else if  (lMode == tomTrue)
		_fSuppressNotify = 0;
	else
		return E_INVALIDARG;

	return NOERROR;
}

/*
 *	ITextDocument2::GetClientRect(Type, pLeft, pTop,pRight, pBottom ) 
 *
 *	@mfunc
 *		Method to retrieve the client rect and inset adjustment.
 *
 *	@rdesc
 *		HRESULT = notification mode set ? NOERROR : E_INVALIDARG
 */
STDMETHODIMP CTxtEdit::GetClientRect(
	long Type,				//@parm option
	long *pLeft,			//@parm left
	long *pTop,				//@parm top
	long *pRight,			//@parm right
	long *pBottom)			//@parm bottom
{
	if (!pLeft || !pTop || !pRight || !pBottom)
		return E_INVALIDARG;
	
	RECT rcArea;
	TxGetClientRect(&rcArea); 
	
	if ( Type & tomIncludeInset )
	{
		// Ajdust veiw inset
		RECTUV rcInset;
		TxGetViewInset( &rcInset, NULL );
		rcArea.right 	-= rcInset.right;
		rcArea.bottom 	-= rcInset.bottom;
		rcArea.left 	+= rcInset.left;
		rcArea.top 		+= rcInset.top;
	}

	// Caller wants screen coordinates?
	if ( !(Type & tomClientCoord) )
	{
		POINT	ptTopLeft = {rcArea.left, rcArea.top};
		POINT	ptBottomRight = {rcArea.right, rcArea.bottom};

		if (!TxClientToScreen(&ptTopLeft) ||
			!TxClientToScreen(&ptBottomRight))
			return E_FAIL;			// It is unexpected for this to happen

		*pLeft		= ptTopLeft.x;
		*pTop		= ptTopLeft.y;
		*pRight		= ptBottomRight.x;
		*pBottom	= ptBottomRight.y;
	}
	else
	{
		*pLeft		= rcArea.left;
		*pTop		= rcArea.top;
		*pRight		= rcArea.right;
		*pBottom	= rcArea.bottom;
	}

	return NOERROR;
}

/*
 *	ITextDocument2::GetSelectionEx(ppSel) 
 *
 *	@mfunc
 *		Method to retrieve selection.
 *
 *	@rdesc
 *		HRESULT = selection ? NOERROR : S_FALSE
 */
STDMETHODIMP CTxtEdit::GetSelectionEx(
	ITextSelection **ppSel)			//@parm  Get Selection object
{	
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetSelectionEx");

	if (_fInPlaceActive)
		return GetSelection (ppSel);

	Assert("Getting selection while not active");

	if(!ppSel)
		return E_INVALIDARG;

	*ppSel = NULL;
	return S_FALSE;
}

/*
 *	ITextDocument2::GetWindow(phWnd) 
 *
 *	@mfunc
 *		Method to get the host window
 *
 *	@rdesc
 *		HRESULT = NOERROR if host return hWnd
 */
STDMETHODIMP CTxtEdit::GetWindow(
	long *phWnd)				//@parm hWnd
{
	if (!phWnd)
		return E_INVALIDARG;

	return TxGetWindow((HWND *)phWnd);
}

/*
 *	ITextDocument2::GetFEFlags(pFEFlags) 
 *
 *	@mfunc
 *		Method to get Host FE flags
 *
 *	@rdesc
 *		HRESULT = NOERROR if host returns FE Flags
 */
STDMETHODIMP CTxtEdit::GetFEFlags(
	long *pFEFlags)			//@parm FE Flags
{
	return TxGetFEFlags(pFEFlags);
}

/*
 *	ITextDocument2::UpdateWindow(void) 
 *
 *	@mfunc
 *		Method to update the RE window
 *
 *	@rdesc
 *		HRESULT = NOERROR 
 */
STDMETHODIMP CTxtEdit::UpdateWindow(void)
{
	TxUpdateWindow();
	return NOERROR;
}

/*
 *	ITextDocument2::CheckTextLimit(long cch, long *pcch) 
 *
 *	@mfunc
 *		Method to check if the count of characters to be added would
 *		exceed max. text limit.  The number of characters exced is returned
 *		in pcch
 *
 *	@rdesc
 *		HRESULT = NOERROR 
 */
STDMETHODIMP CTxtEdit::CheckTextLimit(
	long cch,			//@parm count of characters to be added			
	long *pcch)			//@parm return the number of characters exced text limit
{
	if(!pcch)
		return E_INVALIDARG;

	*pcch = 0;
	if (cch > 0)
	{
		DWORD	cchNew = (DWORD)(GetAdjustedTextLength() + cch);
		if(cchNew > TxGetMaxLength())
			*pcch = cchNew - TxGetMaxLength();
	}

	return NOERROR;
}

/*
 *	ITextDocument2::IMEInProgress(lMode) 
 *
 *	@mfunc
 *		Method for IME message filter to inform client that IME composition
 *		is in progress.
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::IMEInProgress(
	long lMode)		//@parm current IME composition status
{
	if (lMode == tomFalse)
		_fIMEInProgress = 0;
	else if  (lMode == tomTrue)
		_fIMEInProgress = 1;

	return NOERROR;
}

/*
 *	ITextDocument2::SysBeep(void) 
 *
 *	@mfunc
 *		Method to generate system beep.
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::SysBeep(void)
{	
	Beep();
	return NOERROR;
}

/*
 *	ITextDocument2::Update(lMode) 
 *
 *	@mfunc
 *		Method for update the selection or caret.  If lMode is tomTrue, then
 *		scroll the caret into view.
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::Update(
	long lMode)		//@parm current IME composition status
{
	if (!_psel)
		return S_FALSE;

	_psel->Update(lMode == tomTrue ? TRUE : FALSE);

	return NOERROR;
}

/*
 *	ITextDocument2::Notify(lNotify) 
 *
 *	@mfunc
 *		Method for notifying the host for certain IME events
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::Notify(
	long lNotify)		//@parm Notification code
{
	TxNotify(lNotify, NULL);

	return NOERROR;
}

/*
 *	ITextDocument2::GetDocumentFont(ppITextFont) 
 *
 *	@mfunc
 *		Method for getting the default document font
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::GetDocumentFont(
	ITextFont **ppFont)		//@parm get ITextFont object
{
	CTxtFont	*pTxtFont;

	if(!ppFont)
		return E_INVALIDARG;

	pTxtFont =  new CTxtFont(NULL);

	if (pTxtFont)
	{
		pTxtFont->_CF = *GetCharFormat(-1);
		pTxtFont->_dwMask = CFM_ALL2;
	}

	*ppFont = (ITextFont *) pTxtFont;

	return *ppFont ? NOERROR : E_OUTOFMEMORY;
}

/*
 *	ITextDocument2::GetDocumentPara(ppITextPara) 
 *
 *	@mfunc
 *		Method for getting the default document para
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::GetDocumentPara(
	ITextPara **ppPara)		//@parm get ITextPara object
{
	CTxtPara	*pTxtPara;

	if(!ppPara)
		return E_INVALIDARG;

	pTxtPara = new CTxtPara(NULL);

	if (pTxtPara)
	{
		pTxtPara->_PF = *GetParaFormat(-1);
		pTxtPara->_dwMask = PFM_ALL2;
		pTxtPara->_PF._bTabCount = 0;
		pTxtPara->_PF._iTabs = -1;
	}

	*ppPara = (ITextPara *) pTxtPara;
	return *ppPara ? NOERROR : E_OUTOFMEMORY;
}

/*
 *	ITextDocument2::GetCallManager(ppVoid) 
 *
 *	@mfunc
 *		Method for getting the call manager
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::GetCallManager(
	IUnknown **ppVoid)		//@parm get CallMgr object
{
	CCallMgr	*pCallMgr;

	if(!ppVoid)
		return E_INVALIDARG;

	pCallMgr = new CCallMgr(this);

	*ppVoid = (IUnknown *) pCallMgr;
	return *ppVoid ? NOERROR : E_OUTOFMEMORY;
}

/*
 *	ITextDocument2::ReleaseCallManager(ppITextPara) 
 *
 *	@mfunc
 *		Method for getting the default document para
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
STDMETHODIMP CTxtEdit::ReleaseCallManager(
	IUnknown *pVoid)		//@parm Call Manager object
{
	CCallMgr	*pCallMgr;

	if(!pVoid)
		return E_INVALIDARG;

	pCallMgr = (CCallMgr *)pVoid;

	delete pCallMgr;

	return NOERROR;
}

//----------------------- ITextDocument Helper Functions -----------------------
/*
 *	CTxtEdit::CloseFile (bSave)
 *
 *	@mfunc
 *		Method that closes the current document. If changes have been made
 *		in the current document since the last save and document file
 *		information exists, the current document is saved.
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
HRESULT CTxtEdit::CloseFile (
	BOOL bSave)
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::Close");

	CDocInfo *pDocInfo = _pDocInfo;

	if(pDocInfo)
	{
		if(bSave)									// Save current file if
			Save(NULL, 0, 0);						//  any changes made
	
		// FUTURE(BradO):  This code is very similar to the destructor code.
		// We have a problem here in that some of the CDocInfo information
		// should persist from Open to Close to Open (ex. default tab stop)
		// mixed with other per-Open/Close info.  A better job of abstracting
		// these two types of info would really clean up this code.

		if(pDocInfo->_pName)
		{
			SysFreeString(pDocInfo->_pName);		// Free filename BSTR
			pDocInfo->_pName = NULL;
		}

		if(pDocInfo->_hFile)
		{
			CloseHandle(pDocInfo->_hFile);			// Close file if open
			pDocInfo->_hFile = NULL;
		}
		pDocInfo->_wFlags = 0;
		pDocInfo->_wCpg = 0;

		pDocInfo->_lcid = 0;
		pDocInfo->_lcidfe = 0;

		if(pDocInfo->_lpstrLeadingPunct)
		{
			FreePv(pDocInfo->_lpstrLeadingPunct);
			pDocInfo->_lpstrLeadingPunct = NULL;
		}

		if(pDocInfo->_lpstrFollowingPunct)
		{
			FreePv(pDocInfo->_lpstrFollowingPunct);
			pDocInfo->_lpstrFollowingPunct = NULL;
		}
	}
	return NOERROR;
}

/*
 *	CTxtEdit::SetDefaultLCID (lcid) 
 *
 *	@mfunc
 *		Property set method that sets the default LCID
 *
 *	@rdesc
 *		HRESULT = NOERROR
 *
 *	@comm
 *		This property should be part of TOM
 */
HRESULT CTxtEdit::SetDefaultLCID (
	LCID lcid)		//@parm New default LCID value
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetDefaultLCID");

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	pDocInfo->_lcid = lcid;
	return NOERROR;
}

/*
 *	CTxtEdit::GetDefaultLCID (pLCID) 
 *
 *	@mfunc
 *		Property get method that gets the default LCID
 *
 *	@rdesc
 *		HRESULT = (!pLCID) ? E_INVALIDARG : NOERROR
 */
HRESULT CTxtEdit::GetDefaultLCID (
	LCID *pLCID)		//@parm Out parm with default LCID value
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetDefaultLCID");

	if(!pLCID)
		return E_INVALIDARG;

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	*pLCID = _pDocInfo->_lcid;
	return NOERROR;
}

/*
 *	CTxtEdit::SetDefaultLCIDFE (lcid) 
 *
 *	@mfunc
 *		Property set method that sets the default FE LCID
 *
 *	@rdesc
 *		HRESULT = NOERROR
 *
 *	@comm
 *		This property should be part of TOM
 */
HRESULT CTxtEdit::SetDefaultLCIDFE (
	LCID lcid)		//@parm New default LCID value
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetDefaultLCIDFE");

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	pDocInfo->_lcidfe = lcid;
	return NOERROR;
}

/*
 *	CTxtEdit::GetDefaultLCIDFE (pLCID) 
 *
 *	@mfunc
 *		Property get method that gets the default FE LCID
 *
 *	@rdesc
 *		HRESULT = (!pLCID) ? E_INVALIDARG : NOERROR
 */
HRESULT CTxtEdit::GetDefaultLCIDFE (
	LCID *pLCID)		//@parm Out parm with default LCID value
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetDefaultLCID");

	if(!pLCID)
		return E_INVALIDARG;

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	*pLCID = _pDocInfo->_lcidfe;
	return NOERROR;
}

/*
 *	CTxtEdit::SetDocumentType(bDocType) 
 *
 *	@mfunc
 *		Property set method that sets the document's type (none-\ltrdoc-\rtldoc)
 *
 *	@rdesc
 *		HRESULT = NOERROR
 */
HRESULT CTxtEdit::SetDocumentType (
	LONG DocType)		//@parm New document-type value
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetDocumentType");

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	pDocInfo->_bDocType = (BYTE)DocType;
	return NOERROR;
}

/*
 *	CTxtEdit::GetDocumentType (pDocType) 
 *
 *	@mfunc
 *		Property get method that gets the document type
 *
 *	@rdesc
 *		HRESULT = (!pDocType) ? E_INVALIDARG : NOERROR
 */
HRESULT CTxtEdit::GetDocumentType (
	LONG *pDocType)		//@parm Out parm with document type value
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetDocumentType");

	if(!pDocType)
		return E_INVALIDARG;

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)								// Doc info doesn't exist
		return E_OUTOFMEMORY;

	*pDocType = _pDocInfo->_bDocType;
	return NOERROR;
}

/*
 *	CTxtEdit::GetLeadingPunct (plpstrLeadingPunct)
 *
 *	@mfunc
 *		Retrieve leading kinsoku punctuation for document
 *
 *	@rdesc
 *		HRESULT = (!<p plpstrLeadingPunct>) ? E_INVALIDARG :
 *				  (no leading punct) ? S_FALSE :
 *				  (if not enough RAM) ? E_OUTOFMEMORY : NOERROR
 */
HRESULT CTxtEdit::GetLeadingPunct (
	LPSTR * plpstrLeadingPunct)		//@parm Out parm to receive leading 
								//	kinsoku punctuation
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetLeadingPunct");

	if(!plpstrLeadingPunct)
		return E_INVALIDARG;

	*plpstrLeadingPunct = NULL;
	if(!_pDocInfo || !_pDocInfo->_lpstrLeadingPunct)
		return S_FALSE;

	*plpstrLeadingPunct = _pDocInfo->_lpstrLeadingPunct;
	
	return NOERROR;
}

/*
 *	CTxtEdit::SetLeadingPunct (lpstrLeadingPunct)
 *
 *	@mfunc
 *		Set leading kinsoku punctuation for document
 *
 *	@rdesc
 *		HRESULT = (if not enough RAM) ? E_OUTOFMEMORY : NOERROR
 */
HRESULT CTxtEdit::SetLeadingPunct (
	LPSTR lpstrLeadingPunct)	//@parm In parm containing leading 
								//	kinsoku punctuation
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetLeadingPunct");

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)
		return E_OUTOFMEMORY;

	if(pDocInfo->_lpstrLeadingPunct)
		FreePv(pDocInfo->_lpstrLeadingPunct);

	if(lpstrLeadingPunct && *lpstrLeadingPunct)
		pDocInfo->_lpstrLeadingPunct = lpstrLeadingPunct;
	else
	{
		pDocInfo->_lpstrLeadingPunct = NULL;
		return E_INVALIDARG;
	}
	return NOERROR;
}

/*
 *	CTxtEdit::GetFollowingPunct (plpstrFollowingPunct)
 *
 *	@mfunc
 *		Retrieve following kinsoku punctuation for document
 *
 *	@rdesc
 *		HRESULT = (!<p plpstrFollowingPunct>) ? E_INVALIDARG :
 *				  (no following punct) ? S_FALSE :
 *				  (if not enough RAM) ? E_OUTOFMEMORY : NOERROR
 */
HRESULT CTxtEdit::GetFollowingPunct (
	LPSTR * plpstrFollowingPunct)		//@parm Out parm to receive following 
								//	kinsoku punctuation
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::GetFollowingPunct");

	if(!plpstrFollowingPunct)
		return E_INVALIDARG;

	*plpstrFollowingPunct = NULL;
	if(!_pDocInfo || !_pDocInfo->_lpstrFollowingPunct)
		return S_FALSE;

	*plpstrFollowingPunct = _pDocInfo->_lpstrFollowingPunct;
	
	return NOERROR;
}

/*
 *	CTxtEdit::SetFollowingPunct (lpstrFollowingPunct)
 *
 *	@mfunc
 *		Set following kinsoku punctuation for document
 *
 *	@rdesc
 *		HRESULT = (if not enough RAM) ? E_OUTOFMEMORY : NOERROR
 */
HRESULT CTxtEdit::SetFollowingPunct (
	LPSTR lpstrFollowingPunct)		//@parm In parm containing following 
									//	kinsoku punctuation
{
	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtEdit::SetFollowingPunct");

	CDocInfo *pDocInfo = GetDocInfo();

	if(!pDocInfo)
		return E_OUTOFMEMORY;

	if(pDocInfo->_lpstrFollowingPunct)
		FreePv(pDocInfo->_lpstrFollowingPunct);

	if(lpstrFollowingPunct && *lpstrFollowingPunct)
		pDocInfo->_lpstrFollowingPunct = lpstrFollowingPunct;
	else
	{
		pDocInfo->_lpstrFollowingPunct = NULL;
		return E_INVALIDARG;
	}
	return NOERROR;
}

/*
 *	CTxtEdit::InitDocInfo()
 *
 *	@mfunc	initialize doc info structure
 *
 *	@rdesc
 *		HRESULT
 */
HRESULT CTxtEdit::InitDocInfo()
{
	_wZoomNumerator = _wZoomDenominator = 0;// Turn off zoom
	
	// Reset Vertical style
	DWORD dwBits = 0;

	_phost->TxGetPropertyBits(TXTBIT_VERTICAL, &dwBits);

	if (dwBits & TXTBIT_VERTICAL)
	{
		_fUseAtFont = TRUE;
		HandleSetTextFlow(tflowSW);
	}
	else
	{
		_fUseAtFont = FALSE;
		HandleSetTextFlow(tflowES);
	}

	if(_pDocInfo)
	{
		_pDocInfo->Init();
		return NOERROR;
	}

	return GetDocInfo() ? NOERROR : E_OUTOFMEMORY;
}

/*
 *	CTxtEdit::GetBackgroundType()
 *
 *	@mfunc
 *		Get background type
 *
 *	@rdesc
 *		Background type (only for main display)
 */
LONG CTxtEdit::GetBackgroundType()
{
	return _pDocInfo ? _pDocInfo->_nFillType : -1;
}

/*
 *	CTxtEdit::TxGetBackColor()
 *
 *	@mfunc
 *		Get background color
 *
 *	@rdesc
 *		Background color (only for main display)
 */
COLORREF CTxtEdit::TxGetBackColor() const
{
	return (_pDocInfo && _pDocInfo->_nFillType == 0)
		? _pDocInfo->_crColor : _phost->TxGetSysColor(COLOR_WINDOW);
}

//----------------------- CDocInfo related Functions -----------------------
/*
 *	CDocInfo::Init()
 *
 *	@mfunc
 *		Initializer for CDocInfo
 *
 *	@comment
 *		It is assumed that CDocInfo created by a new operator that zeroes
 *		the structure. This initializer is called by the constructor and
 *		by CTxtEdit::InitDocInfo().
 */
void CDocInfo::Init()
{
	_wCpg = (WORD)GetACP();
	_lcid = GetSystemDefaultLCID();

	if(IsFELCID(_lcid))
	{
		_lcidfe = _lcid;
		_lcid = MAKELCID(sLanguageEnglishUS, SORT_DEFAULT);
	}

	_dwDefaultTabStop = lDefaultTab;
	_bDocType = 0;
	InitBackground();
}

/*
 *	CDocInfo::InitBackground()
 *
 *	@mfunc
 *		Background initializer for CDocInfo
 */
void CDocInfo::InitBackground()
{
	_nFillType = -1;
	_sFillAngle = 0;
	if(_hBitmapBack)
		DeleteObject(_hBitmapBack);
	GlobalFree(_hdata);
	_hdata = NULL;
	_hBitmapBack = NULL;
}

/*
 *	CDocInfo::~CDocInfo
 *
 *	@mfunc	destructor for the docinfo class
 */
CDocInfo::~CDocInfo()
{
	if(_pName)
		SysFreeString(_pName);

	if(_hFile)
		CloseHandle(_hFile);

	FreePv(_lpstrLeadingPunct);
	FreePv(_lpstrFollowingPunct);
	FreePv(_prgColor);
	if(_hBitmapBack)
		DeleteObject(_hBitmapBack);
	GlobalFree(_hdata);
}

/*
 *	CTxtEdit::GetDocInfo ()
 *
 *	@mfunc
 *		If _pDocInfo is NULL, equate it to a new CDocInfo.  In either case
 *		return _pDocInfo
 *
 *	@rdesc
 *		CTxtEdit::_pDocInfo, the ptr to the CDocInfo object
 */
CDocInfo * CTxtEdit::GetDocInfo()
{
	TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::GetDocInfo");

	if (!_pDocInfo)
		_pDocInfo = new CDocInfo();

	// It is the caller's responsiblity to notice that an error occurred
	// in the allocation of the CDocInfo object.
	return _pDocInfo;
}