#include "stdafx.h"
#include "RichDrawText.h"
// Link against riched20.lib
#pragma comment(lib,"riched20.lib")
// The IID in riched20.lib in the Plaform SDK appears to be incorrect.
// This one is from the Microsoft Knowledge Base, article Q270161.
const IID IID_ITextServices = {
// 8d33f740-cf58-11ce-a89d-00aa006cadc5
0x8d33f740, 0xcf58, 0x11ce, {0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}
};
CRichDrawText::CRichDrawText()
{
// Get the current font settings
NONCLIENTMETRICS ncm;
::ZeroMemory(&ncm,sizeof ncm);
ncm.cbSize = sizeof ncm;
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof ncm,&ncm,0);
// Work out the name and point size of the font
CStringW strFontName(ncm.lfMessageFont.lfFaceName);
HDC hDC = ::GetDC(NULL);
int iPointSize = -1 * ::MulDiv(ncm.lfMessageFont.lfHeight,72,::GetDeviceCaps(hDC,LOGPIXELSY));
::ReleaseDC(NULL,hDC);
// Create a default character format
::ZeroMemory(&m_CharFormat,sizeof m_CharFormat);
m_CharFormat.cbSize = sizeof m_CharFormat;
m_CharFormat.dwMask = CFM_BOLD|CFM_CHARSET|CFM_COLOR|CFM_FACE|CFM_ITALIC|CFM_OFFSET|
CFM_PROTECTED|CFM_SIZE|CFM_STRIKEOUT|CFM_UNDERLINE;
m_CharFormat.yHeight = 20 * iPointSize;
m_CharFormat.crTextColor = ::GetSysColor(COLOR_BTNTEXT);
m_CharFormat.bPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
wcscpy(m_CharFormat.szFaceName,strFontName);
// Create a default paragraph format
::ZeroMemory(&m_ParaFormat,sizeof m_ParaFormat);
m_ParaFormat.cbSize = sizeof m_ParaFormat;
m_ParaFormat.dwMask = PFM_ALIGNMENT|PFM_NUMBERING|PFM_OFFSET|PFM_OFFSETINDENT|
PFM_RIGHTINDENT|PFM_RTLPARA|PFM_STARTINDENT|PFM_TABSTOPS;
m_ParaFormat.wAlignment = PFA_LEFT;
// Get an interface to the windowless rich edit control
CComPtr<IUnknown> unknown;
HRESULT hr = ::CreateTextServices(NULL,&m_xTextHost,&unknown);
ASSERT(SUCCEEDED(hr));
// Convert the returned interface to ITextDocument and ITextServices
m_TextDoc = unknown;
m_TextServ = unknown;
}
void CRichDrawText::SetText(LPCWSTR text)
{
// Get a range for the whole of the text in the control
CComPtr<ITextRange> range;
m_TextDoc->Range(0,0,&range);
range->MoveEnd(tomStory,1,NULL);
// Replace the whole text
range->SetText(CComBSTR(text));
}
void CRichDrawText::Range(long cpFirst, long cpLim, ITextRange** ppRange)
{
// Get the given range of text in the control
m_TextDoc->Range(cpFirst,cpLim,ppRange);
}
void CRichDrawText::SizeText(CDC& dc, CRect& rect)
{
LONG w = rect.Width();
LONG h = 0;
// Given a rectange of a particular width, work out how much vertical space
// is needed to display the text in the windowless control.
SIZEL extent = { -1, -1 };
HRESULT hr = m_TextServ->TxGetNaturalSize(DVASPECT_CONTENT,
dc.GetSafeHdc(),0,NULL,TXTNS_FITTOCONTENT,&extent,&w,&h);
ASSERT(SUCCEEDED(hr));
if (FAILED(hr))
h = 0;
// Adjust the height of the rectangle
rect.bottom = rect.top+h;
}
void CRichDrawText::DrawText(CDC& dc, const CRect& rect)
{
// Draw the text in the windowless control onto the given device context,
// within the given bounding rectangle.
RECTL rc = { rect.left, rect.top, rect.right, rect.bottom };
HRESULT hr = m_TextServ->TxDraw(DVASPECT_CONTENT,0,NULL,NULL,dc.GetSafeHdc(),0,
&rc,NULL,NULL,NULL,0,0);
ASSERT(SUCCEEDED(hr));
}
BEGIN_INTERFACE_MAP(CRichDrawText, CCmdTarget)
INTERFACE_PART(CRichDrawText, IID_ITextHost, TextHost)
END_INTERFACE_MAP()
STDMETHODIMP_(ULONG) CRichDrawText::XTextHost::AddRef()
{
return 1;
}
STDMETHODIMP_(ULONG) CRichDrawText::XTextHost::Release()
{
return 1;
}
STDMETHODIMP CRichDrawText::XTextHost::QueryInterface(REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE(CRichDrawText, TextHost)
return (HRESULT)pThis->InternalQueryInterface(&iid,ppvObj);
}
HDC CRichDrawText::XTextHost::TxGetDC()
{
return 0;
}
INT CRichDrawText::XTextHost::TxReleaseDC(HDC hdc)
{
return 0;
}
BOOL CRichDrawText::XTextHost::TxShowScrollBar(INT fnBar, BOOL fShow)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxEnableScrollBar(INT fuSBFlags, INT fuArrowflags)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxSetScrollRange(INT fnBar, LONG nMinPos, INT nMaxPos, BOOL fRedraw)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxSetScrollPos(INT fnBar, INT nPos, BOOL fRedraw)
{
return FALSE;
}
void CRichDrawText::XTextHost::TxInvalidateRect(LPCRECT prc, BOOL fMode)
{
}
void CRichDrawText::XTextHost::TxViewChange(BOOL fUpdate)
{
}
BOOL CRichDrawText::XTextHost::TxCreateCaret(HBITMAP hbmp, INT xWidth, INT yHeight)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxShowCaret(BOOL fShow)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxSetCaretPos(INT x, INT y)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxSetTimer(UINT idTimer, UINT uTimeout)
{
return FALSE;
}
void CRichDrawText::XTextHost::TxKillTimer(UINT idTimer)
{
}
void CRichDrawText::XTextHost::TxScrollWindowEx(INT dx, INT dy, LPCRECT lprcScroll,
LPCRECT lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate, UINT fuScroll)
{
}
void CRichDrawText::XTextHost::TxSetCapture(BOOL fCapture)
{
}
void CRichDrawText::XTextHost::TxSetFocus()
{
}
void CRichDrawText::XTextHost::TxSetCursor(HCURSOR hcur, BOOL fText)
{
}
BOOL CRichDrawText::XTextHost::TxScreenToClient(LPPOINT lppt)
{
return FALSE;
}
BOOL CRichDrawText::XTextHost::TxClientToScreen(LPPOINT lppt)
{
return FALSE;
}
HRESULT CRichDrawText::XTextHost::TxActivate(LONG* plOldState)
{
return E_FAIL;
}
HRESULT CRichDrawText::XTextHost::TxDeactivate(LONG lNewState)
{
return E_FAIL;
}
HRESULT CRichDrawText::XTextHost::TxGetClientRect(LPRECT prc)
{
return E_FAIL;
}
HRESULT CRichDrawText::XTextHost::TxGetViewInset(LPRECT prc)
{
// Set zero sized margins
*prc = CRect(0,0,0,0);
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxGetCharFormat(const CHARFORMATW **ppCF)
{
METHOD_PROLOGUE(CRichDrawText, TextHost)
// Return the default character format set up in the constructor
*ppCF = &(pThis->m_CharFormat);
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxGetParaFormat(const PARAFORMAT **ppPF)
{
METHOD_PROLOGUE(CRichDrawText, TextHost)
// Return the default paragraph format set up in the constructor
*ppPF = &(pThis->m_ParaFormat);
return S_OK;
}
COLORREF CRichDrawText::XTextHost::TxGetSysColor(int nIndex)
{
// Pass requests for colours on to Windows
return ::GetSysColor(nIndex);
}
HRESULT CRichDrawText::XTextHost::TxGetBackStyle(TXTBACKSTYLE *pstyle)
{
// Do not erase what is underneath the drawing area
*pstyle = TXTBACK_TRANSPARENT;
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxGetMaxLength(DWORD *plength)
{
// Set the maximum size of text to be arbitrarily large
*plength = 1024*1024*16;
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxGetScrollBars(DWORD *pdwScrollBar)
{
// Do not allow scrollbars
*pdwScrollBar = 0;
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxGetPasswordChar(TCHAR *pch)
{
return S_FALSE;
}
HRESULT CRichDrawText::XTextHost::TxGetAcceleratorPos(LONG *pcp)
{
*pcp = -1;
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxGetExtent(LPSIZEL lpExtent)
{
return E_NOTIMPL;
}
HRESULT CRichDrawText::XTextHost::OnTxCharFormatChange(const CHARFORMATW * pcf)
{
return E_FAIL;
}
HRESULT CRichDrawText::XTextHost::OnTxParaFormatChange(const PARAFORMAT * ppf)
{
return E_FAIL;
}
HRESULT CRichDrawText::XTextHost::TxGetPropertyBits(DWORD dwMask, DWORD *pdwBits)
{
// Set the windowless control as being multiple lines of wrapping rich text
DWORD bits = TXTBIT_MULTILINE|TXTBIT_RICHTEXT|TXTBIT_WORDWRAP;
*pdwBits = bits & dwMask;
return S_OK;
}
HRESULT CRichDrawText::XTextHost::TxNotify(DWORD iNotify, void *pv)
{
// Claim to have handled the notifcation, even though we always ignore it
return S_OK;
}
HIMC CRichDrawText::XTextHost::TxImmGetContext()
{
return 0;
}
void CRichDrawText::XTextHost::TxImmReleaseContext(HIMC himc)
{
}
HRESULT CRichDrawText::XTextHost::TxGetSelectionBarWidth(LONG *lSelBarWidth)
{
// No selection bar
*lSelBarWidth = 0;
return S_OK;
}