// Feel free to use this code in your own applications.
// The Author does not guarantee anything about this code.
// Author : Yves Maurer

// FormattedTextDraw.cpp : Implementation of CFormattedTextDraw

#include "headers.h"


const IID IID_ITextServices = { // 8d33f740-cf58-11ce-a89d-00aa006cadc5
    0x8d33f740,
    0xcf58,
    0x11ce,
    {0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}
  };

const IID IID_ITextHost = { // c5bdd8d0-d26e-11ce-a89e-00aa006cadc5
    0xc5bdd8d0,
    0xd26e,
    0x11ce,
    {0xa8, 0x9e, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}
  };

static const IID IID_ITextDocument = {
	0x8CC497C0,
	0xA1DF,
	0x11CE,
	{0x80,0x98,0x00,0xAA,0x00,0x47,0xBE,0x5D}
  };

/////////////////////////////////////////////////////////////////////////////
// CallBack functions
DWORD CALLBACK EditStreamInCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
	PCOOKIE pCookie = (PCOOKIE) dwCookie;
	if (pCookie->isUnicode)
	{
		if ((pCookie->dwSize - pCookie->dwCount)*sizeof(WCHAR) < (DWORD)cb)
			*pcb = (pCookie->dwSize - pCookie->dwCount)*sizeof(WCHAR);
		else
			*pcb = cb&~1UL;
		CopyMemory(pbBuff, pCookie->unicode+pCookie->dwCount, *pcb);
		pCookie->dwCount += *pcb/sizeof(WCHAR);
	} else
	{
		if(pCookie->dwSize - pCookie->dwCount < (DWORD)cb)
			*pcb = pCookie->dwSize - pCookie->dwCount;
		else
			*pcb = cb;
		CopyMemory(pbBuff, pCookie->ansi+pCookie->dwCount, *pcb);
		pCookie->dwCount += *pcb;
	}

	return 0;	//	callback succeeded - no errors
}

/////////////////////////////////////////////////////////////////////////////
// CFormattedTextDraw
HRESULT CFormattedTextDraw::putRTFTextA(char *newVal)
{
	HRESULT hr;
	LRESULT lResult = 0;
	EDITSTREAM editStream;

	if (!m_spTextServices) 
		return S_FALSE;

	m_editCookie.isUnicode = false;
	m_editCookie.ansi = newVal;
	m_editCookie.dwSize = lstrlenA(m_editCookie.ansi);
	m_editCookie.dwCount = 0;
	editStream.dwCookie = (DWORD_PTR) &m_editCookie;
	editStream.dwError = 0;
	editStream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamInCallback;
	hr = m_spTextServices->TxSendMessage(EM_STREAMIN, (WPARAM)(SF_RTF), (LPARAM)&editStream, &lResult);
	return S_OK;
}

HRESULT CFormattedTextDraw::putRTFTextW(WCHAR *newVal)
{

	HRESULT hr;
	LRESULT lResult = 0;
	EDITSTREAM editStream;

	if (!m_spTextServices) 
		return S_FALSE;

	m_editCookie.isUnicode = true;
	m_editCookie.unicode = newVal;
	m_editCookie.dwSize = lstrlenW(m_editCookie.unicode);
	m_editCookie.dwCount = 0;
	editStream.dwCookie = (DWORD_PTR) &m_editCookie;
	editStream.dwError = 0;
	editStream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamInCallback;
	hr = m_spTextServices->TxSendMessage(EM_STREAMIN, (WPARAM)(SF_RTF|SF_UNICODE), (LPARAM)&editStream, &lResult);
	return S_OK;

}

HRESULT CFormattedTextDraw::putTextA(char *newVal)
{
	HRESULT hr;
	LRESULT lResult = 0;
	EDITSTREAM editStream;

	if (!m_spTextServices) 
		return S_FALSE;

	m_editCookie.isUnicode = false;
	m_editCookie.ansi = newVal;
	m_editCookie.dwSize = lstrlenA(m_editCookie.ansi);
	m_editCookie.dwCount = 0;
	editStream.dwCookie = (DWORD_PTR) &m_editCookie;
	editStream.dwError = 0;
	editStream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamInCallback;
	hr = m_spTextServices->TxSendMessage(EM_STREAMIN, (WPARAM)(SF_TEXT), (LPARAM)&editStream, &lResult);
	
	CHARFORMAT cf;
	cf.cbSize = sizeof(cf);
	cf.dwMask = CFM_FACE|CFM_BOLD;
	cf.dwEffects = 0;
	wsprintf(cf.szFaceName, _T("MS Shell Dlg"));
	m_spTextServices->TxSendMessage(EM_SETCHARFORMAT, (WPARAM)(SCF_ALL), (LPARAM)&cf, &lResult);

	return S_OK;
}

HRESULT CFormattedTextDraw::putTextW(WCHAR *newVal)
{

	HRESULT hr;
	LRESULT lResult = 0;
	EDITSTREAM editStream;

	if (!m_spTextServices) 
		return S_FALSE;

	m_editCookie.isUnicode = true;
	m_editCookie.unicode = newVal;
	m_editCookie.dwSize = lstrlenW(m_editCookie.unicode);
	m_editCookie.dwCount = 0;
	editStream.dwCookie = (DWORD_PTR) &m_editCookie;
	editStream.dwError = 0;
	editStream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamInCallback;
	hr = m_spTextServices->TxSendMessage(EM_STREAMIN, (WPARAM)(SF_TEXT|SF_UNICODE), (LPARAM)&editStream, &lResult);
	
	CHARFORMAT cf;
	cf.cbSize = sizeof(cf);
	cf.dwMask = CFM_FACE|CFM_BOLD;
	cf.dwEffects = 0;
	wsprintf(cf.szFaceName, _T("MS Shell Dlg"));
	m_spTextServices->TxSendMessage(EM_SETCHARFORMAT, (WPARAM)(SCF_ALL), (LPARAM)&cf, &lResult);

	return S_OK;

}

HRESULT CFormattedTextDraw::Draw(void *hdcDraw, RECT *prc)
{
	LOGFONT lf;
	GetObject(GetCurrentObject((HDC)hdcDraw, OBJ_FONT), sizeof(lf), &lf);

	LRESULT lResult;
	CHARFORMAT cf;
	cf.cbSize = sizeof(cf);
	cf.dwMask = CFM_FACE/*|CFM_COLOR*/|CFM_CHARSET|CFM_SIZE|
		(lf.lfWeight>=FW_BOLD?CFM_BOLD:0)|
		(lf.lfItalic?CFM_ITALIC:0)|
		(lf.lfUnderline?CFM_UNDERLINE:0)|
		(lf.lfStrikeOut?CFM_STRIKEOUT:0);
	cf.dwEffects = CFE_BOLD|CFE_ITALIC|CFE_STRIKEOUT|CFE_UNDERLINE;
	cf.crTextColor = GetTextColor((HDC)hdcDraw);
	cf.bCharSet = lf.lfCharSet;
	cf.yHeight = 1440 * abs(lf.lfHeight) / GetDeviceCaps((HDC)hdcDraw, LOGPIXELSY);
	wsprintf(cf.szFaceName, lf.lfFaceName);
	m_spTextServices->TxSendMessage(EM_SETCHARFORMAT, (WPARAM)(SCF_ALL), (LPARAM)&cf, &lResult);

	m_spTextServices->TxDraw(
	    DVASPECT_CONTENT,  		// Draw Aspect
		0,						// Lindex
		NULL,					// Info for drawing optimization
		NULL,					// target device information
		(HDC) hdcDraw,			// Draw device HDC
		NULL,			 	   	// Target device HDC
		(RECTL *) prc,			// Bounding client rectangle
		NULL,					// Clipping rectangle for metafiles
		(RECT *) NULL,			// Update rectangle
		NULL, 	   				// Call back function
		NULL,					// Call back parameter
		TXTVIEW_INACTIVE);		// What view of the object could be TXTVIEW_ACTIVE
	return S_OK;
}

HRESULT CFormattedTextDraw::Create()
{
	return CreateTextServicesObject();
}

HRESULT CFormattedTextDraw::get_NaturalSize(void *hdcDraw, long *Width, long *Height)
{
	LOGFONT lf;
	GetObject(GetCurrentObject((HDC)hdcDraw, OBJ_FONT), sizeof(lf), &lf);

	LRESULT lResult;
	CHARFORMAT cf;
	cf.cbSize = sizeof(cf);
	cf.dwMask = CFM_FACE/*|CFM_COLOR*/|CFM_CHARSET|CFM_SIZE|
		(lf.lfWeight>=FW_BOLD?CFM_BOLD:0)|
		(lf.lfItalic?CFM_ITALIC:0)|
		(lf.lfUnderline?CFM_UNDERLINE:0)|
		(lf.lfStrikeOut?CFM_STRIKEOUT:0);
	cf.dwEffects = CFE_BOLD|CFE_ITALIC|CFE_STRIKEOUT|CFE_UNDERLINE;
	cf.crTextColor = GetTextColor((HDC)hdcDraw);
	cf.bCharSet = lf.lfCharSet;
	cf.yHeight = 1440 * abs(lf.lfHeight) / GetDeviceCaps((HDC)hdcDraw, LOGPIXELSY);
	wsprintf(cf.szFaceName, lf.lfFaceName);

	if (!m_spTextServices)
		return S_FALSE;

	m_spTextServices->TxSendMessage(EM_SETCHARFORMAT, (WPARAM)(SCF_ALL), (LPARAM)&cf, &lResult);

	SIZEL szExtent;
//	HDC	hdcDraw;
//	hdcDraw = GetDC(NULL);
	*Height = 1;
	szExtent.cx = *Width;
	szExtent.cy = *Height;
	if (m_spTextServices->TxGetNaturalSize(DVASPECT_CONTENT, (HDC)hdcDraw, NULL, NULL, TXTNS_FITTOCONTENT, &szExtent, Width, Height) != S_OK)
	{
//		ReleaseDC(NULL, hdcDraw);
		return S_FALSE;
	}

//	ReleaseDC(NULL, hdcDraw);
	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// ITextHost functions
HDC CFormattedTextDraw::TxGetDC()
{
	return NULL;
}

INT CFormattedTextDraw::TxReleaseDC(HDC hdc)
{
	return 1;
}

BOOL CFormattedTextDraw::TxShowScrollBar(INT fnBar, BOOL fShow)
{
	return FALSE;
}

BOOL CFormattedTextDraw::TxEnableScrollBar(INT fuSBFlags, INT fuArrowflags)
{
	return FALSE;
}

BOOL CFormattedTextDraw::TxSetScrollRange(INT fnBar, LONG nMinPos, INT nMaxPos, BOOL fRedraw)
{
	return FALSE;
}

BOOL CFormattedTextDraw::TxSetScrollPos(INT fnBar, INT nPos, BOOL fRedraw)
{
	return FALSE;
}

void CFormattedTextDraw::TxInvalidateRect(LPCRECT prc, BOOL fMode)
{
}

void CFormattedTextDraw::TxViewChange(BOOL fUpdate)
{
}

BOOL CFormattedTextDraw::TxCreateCaret(HBITMAP hbmp, INT xWidth, INT yHeight)
{
	return FALSE;
}

BOOL CFormattedTextDraw::TxShowCaret(BOOL fShow)
{
	return FALSE;
}

BOOL CFormattedTextDraw::TxSetCaretPos(INT x, INT y)
{
	return FALSE;
}

BOOL CFormattedTextDraw::TxSetTimer(UINT idTimer, UINT uTimeout)
{
	return FALSE;
}

void CFormattedTextDraw::TxKillTimer(UINT idTimer)
{
}

void CFormattedTextDraw::TxScrollWindowEx(INT dx, INT dy, LPCRECT lprcScroll, LPCRECT lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate, UINT fuScroll)
{
}

void CFormattedTextDraw::TxSetCapture(BOOL fCapture)
{
}

void CFormattedTextDraw::TxSetFocus()
{
}


void CFormattedTextDraw::TxSetCursor(HCURSOR hcur, BOOL fText)
{
	if (fText)
	{
		SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
	} else
	{
		SetCursor(hcur);
	}
}

BOOL CFormattedTextDraw::TxScreenToClient(LPPOINT lppt)
{
	if (!m_hwndParent) return FALSE;
//	BOOL result = ScreenToClient(m_hwndParent, lppt);
//	lppt->x -= m_rcClient.left;
//	lppt->y -= m_rcClient.left;
	return ScreenToClient(m_hwndParent, lppt);
}

BOOL CFormattedTextDraw::TxClientToScreen(LPPOINT lppt)
{
	if (!m_hwndParent) return FALSE;
//	BOOL result = ;
//	lppt->x -= m_rcClient.left;
//	lppt->y -= m_rcClient.left;
	return ClientToScreen(m_hwndParent, lppt);
}

HRESULT	CFormattedTextDraw::TxActivate(LONG * plOldState)
{
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxDeactivate(LONG lNewState)
{
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetClientRect(LPRECT prc)
{
	*prc = m_rcClient;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetViewInset(LPRECT prc)
{
	*prc = m_rcViewInset;
	return S_OK;
}

HRESULT CFormattedTextDraw::TxGetCharFormat(const CHARFORMATW **ppCF)
{
	*ppCF = m_pCF;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetParaFormat(const PARAFORMAT **ppPF)
{
	*ppPF = &m_PF;
	return S_OK;
}

COLORREF CFormattedTextDraw::TxGetSysColor(int nIndex)
{
	return GetSysColor(nIndex);
}

HRESULT	CFormattedTextDraw::TxGetBackStyle(TXTBACKSTYLE *pstyle)
{
	*pstyle = TXTBACK_TRANSPARENT;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetMaxLength(DWORD *plength)
{
	*plength = m_dwMaxLength;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetScrollBars(DWORD *pdwScrollBar)
{
	*pdwScrollBar = m_dwScrollbar;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetPasswordChar(TCHAR *pch)
{
	return S_FALSE;
}

HRESULT	CFormattedTextDraw::TxGetAcceleratorPos(LONG *pcp)
{
	*pcp = -1;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetExtent(LPSIZEL lpExtent)
{
	return E_NOTIMPL;
}

HRESULT CFormattedTextDraw::OnTxCharFormatChange(const CHARFORMATW * pcf)
{
	memcpy(m_pCF, pcf, pcf->cbSize);
	return S_OK;
}

HRESULT	CFormattedTextDraw::OnTxParaFormatChange(const PARAFORMAT * ppf)
{
	memcpy(&m_PF, ppf, ppf->cbSize);
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxGetPropertyBits(DWORD dwMask, DWORD *pdwBits)
{
	*pdwBits = m_dwPropertyBits;
	return S_OK;
}

HRESULT	CFormattedTextDraw::TxNotify(DWORD iNotify, void *pv)
{
	return S_OK;
}

HIMC CFormattedTextDraw::TxImmGetContext()
{
	return NULL;
}

void CFormattedTextDraw::TxImmReleaseContext(HIMC himc)
{
}

HRESULT	CFormattedTextDraw::TxGetSelectionBarWidth(LONG *lSelBarWidth)
{
	*lSelBarWidth = 100;
	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// custom functions

HRESULT CFormattedTextDraw::CharFormatFromHFONT(CHARFORMAT2W* pCF, HFONT hFont)
// Takes an HFONT and fills in a CHARFORMAT2W structure with the corresponding info
{

	HWND hWnd;
	LOGFONT lf;
	HDC hDC;
	LONG yPixPerInch;

	// Get LOGFONT for default font
	if (!hFont)
		hFont = (HFONT) GetStockObject(SYSTEM_FONT);

	// Get LOGFONT for passed hfont
	if (!GetObject(hFont, sizeof(LOGFONT), &lf))
		return E_FAIL;

	// Set CHARFORMAT structure
	memset(pCF, 0, sizeof(CHARFORMAT2W));
	pCF->cbSize = sizeof(CHARFORMAT2W);

	hWnd = GetDesktopWindow();
	hDC = GetDC(hWnd);
	yPixPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
	pCF->yHeight = -lf.lfHeight * LY_PER_INCH / yPixPerInch;
	ReleaseDC(hWnd, hDC);

	pCF->yOffset = 0;
	pCF->crTextColor = 0;

	pCF->dwEffects = CFM_EFFECTS | CFE_AUTOBACKCOLOR;
	pCF->dwEffects &= ~(CFE_PROTECTED | CFE_LINK | CFE_AUTOCOLOR);

	if(lf.lfWeight < FW_BOLD)
		pCF->dwEffects &= ~CFE_BOLD;

	if (!lf.lfItalic)
		pCF->dwEffects &= ~CFE_ITALIC;

	if (!lf.lfUnderline)
		pCF->dwEffects &= ~CFE_UNDERLINE;

	if (!lf.lfStrikeOut)
		pCF->dwEffects &= ~CFE_STRIKEOUT;

	pCF->dwMask = CFM_ALL | CFM_BACKCOLOR | CFM_STYLE;
	pCF->bCharSet = lf.lfCharSet;
	pCF->bPitchAndFamily = lf.lfPitchAndFamily;


	lstrcpyW(pCF->szFaceName, lf.lfFaceName);


	return S_OK;
}

HRESULT CFormattedTextDraw::InitDefaultCharFormat()
{
	return CharFormatFromHFONT(m_pCF, NULL);
}

HRESULT CFormattedTextDraw::InitDefaultParaFormat()
{
	memset(&m_PF, 0, sizeof(PARAFORMAT2));
	m_PF.cbSize = sizeof(PARAFORMAT2);
	m_PF.dwMask = PFM_ALL;
	m_PF.wAlignment = PFA_LEFT;
	m_PF.cTabCount = 1;
	m_PF.rgxTabs[0] = lDefaultTab;
	return S_OK;
}

HRESULT CFormattedTextDraw::CreateTextServicesObject()
{
	HRESULT hr;
	IUnknown *spUnk;

	hr = MyCreateTextServices(NULL, static_cast<ITextHost*>(this), &spUnk);
	if (hr == S_OK) {
		hr = spUnk->QueryInterface(IID_ITextServices, (void**)&m_spTextServices);
		hr = spUnk->QueryInterface(IID_ITextDocument, (void**)&m_spTextDocument);
		spUnk->Release();
	}
	return hr;
}