Click here to Skip to main content
15,886,258 members
Articles / Desktop Programming / MFC

Using Windowless RichEdit Controls

Rate me:
Please Sign up or sign in to vote.
4.81/5 (21 votes)
10 Oct 2006CPOL5 min read 126.9K   7.4K   66  
How to use the windowless RichEdit control, one of Microsoft's less well-documented APIs.
#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;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions