Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

344 lines
10 KiB

//
// compose.cpp
//
// Composition code.
//
#include "globals.h"
#include "mark.h"
#include "editsess.h"
class CCompositionEditSession : public CEditSessionBase
{
public:
CCompositionEditSession(ITfContext *pContext, CMarkTextService *pMark) : CEditSessionBase(pContext)
{
_pMark = pMark;
_pMark->AddRef();
}
~CCompositionEditSession()
{
_pMark->Release();
}
// ITfEditSession
STDMETHODIMP DoEditSession(TfEditCookie ec);
private:
CMarkTextService *_pMark;
};
class CTerminateCompositionEditSession : public CEditSessionBase
{
public:
CTerminateCompositionEditSession(CMarkTextService *pMark, ITfContext *pContext) : CEditSessionBase(pContext)
{
_pMark = pMark;
_pMark->AddRef();
}
~CTerminateCompositionEditSession()
{
_pMark->Release();
}
// ITfEditSession
STDMETHODIMP DoEditSession(TfEditCookie ec)
{
_pMark->_TerminateComposition(ec);
return S_OK;
}
private:
CMarkTextService *_pMark;
};
//+---------------------------------------------------------------------------
//
// _TerminateCompositionInContext
//
//----------------------------------------------------------------------------
void CMarkTextService::_TerminateCompositionInContext(ITfContext *pContext)
{
CTerminateCompositionEditSession *pEditSession;
HRESULT hr;
if (pEditSession = new CTerminateCompositionEditSession(this, pContext))
{
pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE, &hr);
pEditSession->Release();
}
}
//+---------------------------------------------------------------------------
//
// _Menu_OnComposition
//
// Callback for the "Start/End Composition" menu item.
// If we have a composition, end it. Otherwise start a new composition over
// the selection of the current focus context.
//----------------------------------------------------------------------------
/* static */
void CMarkTextService::_Menu_OnComposition(CMarkTextService *_this)
{
ITfDocumentMgr *pFocusDoc;
ITfContext *pContext;
CCompositionEditSession *pCompositionEditSession;
HRESULT hr;
// get the focus document
if (_this->_pThreadMgr->GetFocus(&pFocusDoc) != S_OK)
return;
// we want the topmost context, since the main doc context could be
// superceded by a modal tip context
if (pFocusDoc->GetTop(&pContext) != S_OK)
{
pContext = NULL;
goto Exit;
}
if (pCompositionEditSession = new CCompositionEditSession(pContext, _this))
{
// we need a document write lock
// the CCompositionEditSession will do all the work when the
// CCompositionEditSession::DoEditSession method is called by the context
pContext->RequestEditSession(_this->_tfClientId, pCompositionEditSession, TF_ES_READWRITE | TF_ES_ASYNCDONTCARE, &hr);
pCompositionEditSession->Release();
}
Exit:
SafeRelease(pContext);
pFocusDoc->Release();
}
//+---------------------------------------------------------------------------
//
// DoEditSession
//
//----------------------------------------------------------------------------
STDAPI CCompositionEditSession::DoEditSession(TfEditCookie ec)
{
ITfInsertAtSelection *pInsertAtSelection;
ITfContextComposition *pContextComposition;
ITfComposition *pComposition;
ITfRange *pRangeComposition;
ITfRange *pRangeInsert;
ITfContext *pCompositionContext;
HRESULT hr;
BOOL fEqualContexts;
// get an interface on the context we can use to deal with compositions
if (_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition) != S_OK)
return E_FAIL;
hr = E_FAIL;
pInsertAtSelection = NULL;
if (_pMark->_IsComposing())
{
// we have a composition, let's terminate it
// it's possible our current composition is in another context...let's find out
fEqualContexts = TRUE;
if (_pMark->_GetComposition()->GetRange(&pRangeComposition) == S_OK)
{
if (pRangeComposition->GetContext(&pCompositionContext) == S_OK)
{
fEqualContexts = IsEqualUnknown(pCompositionContext, _pContext);
if (!fEqualContexts)
{
// need an edit session in the composition context
_pMark->_TerminateCompositionInContext(pCompositionContext);
}
pCompositionContext->Release();
}
pRangeComposition->Release();
}
// if the composition is in pContext, we already have an edit cookie
if (fEqualContexts)
{
_pMark->_TerminateComposition(ec);
}
}
else
{
// let's start a new composition over the current selection
// this is totally contrived, a real text service would have
// some meaningful logic to trigger this
// first, test where a keystroke would go in the document if we did an insert
// we need a special interface to insert text at the selection
if (_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK)
{
pInsertAtSelection = NULL;
goto Exit;
}
if (pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert) != S_OK)
goto Exit;
// start the composition
if (pContextComposition->StartComposition(ec, pRangeInsert, _pMark, &pComposition) != S_OK)
{
pComposition = NULL;
}
pRangeInsert->Release();
// _pComposition may be NULL even if StartComposition return S_OK, this mean the application
// rejected the composition
if (pComposition != NULL)
{
_pMark->_SetComposition(pComposition);
// underline the composition text to give the user some feedback UI
_pMark->_SetCompositionDisplayAttributes(ec);
}
}
// if we make it here, we've succeeded
hr = S_OK;
Exit:
SafeRelease(pInsertAtSelection);
pContextComposition->Release();
return hr;
}
//+---------------------------------------------------------------------------
//
// OnCompositionTerminated
//
// Callback for ITfCompositionSink. The system calls this method whenever
// someone other than this service ends a composition.
//----------------------------------------------------------------------------
STDAPI CMarkTextService::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition)
{
// we already have the composition cached, so we can ignore pComposition...
// all this service wants to do is clear the display property
_ClearCompositionDisplayAttributes(ecWrite);
// releae our cached composition
SafeReleaseClear(_pComposition);
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _ClearCompositionDisplayAttributes
//
//----------------------------------------------------------------------------
void CMarkTextService::_ClearCompositionDisplayAttributes(TfEditCookie ec)
{
ITfRange *pRangeComposition;
ITfContext *pContext;
ITfProperty *pDisplayAttributeProperty;
// we need a range and the context it lives in
if (_pComposition->GetRange(&pRangeComposition) != S_OK)
return;
if (pRangeComposition->GetContext(&pContext) != S_OK)
{
pContext = NULL;
goto Exit;
}
// get our the display attribute property
if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
goto Exit;
// clear the value over the range
pDisplayAttributeProperty->Clear(ec, pRangeComposition);
pDisplayAttributeProperty->Release();
Exit:
pRangeComposition->Release();
SafeRelease(pContext);
}
//+---------------------------------------------------------------------------
//
// _SetCompositionDisplayAttributes
//
//----------------------------------------------------------------------------
BOOL CMarkTextService::_SetCompositionDisplayAttributes(TfEditCookie ec)
{
ITfRange *pRangeComposition;
ITfContext *pContext;
ITfProperty *pDisplayAttributeProperty;
VARIANT var;
HRESULT hr;
// we need a range and the context it lives in
if (_pComposition->GetRange(&pRangeComposition) != S_OK)
return FALSE;
hr = E_FAIL;
if (pRangeComposition->GetContext(&pContext) != S_OK)
{
pContext = NULL;
goto Exit;
}
// get our the display attribute property
if (pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty) != S_OK)
goto Exit;
// set the value over the range
// the application will use this guid atom to lookup the acutal rendering information
var.vt = VT_I4; // we're going to set a TfGuidAtom
var.lVal = _gaDisplayAttribute; // our cached guid atom for c_guidMarkDisplayAttribute
hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var);
pDisplayAttributeProperty->Release();
Exit:
pRangeComposition->Release();
SafeRelease(pContext);
return (hr == S_OK);
}
//+---------------------------------------------------------------------------
//
// _InitDisplayAttributeGuidAtom
//
// Because it's expensive to map our display attribute GUID to a TSF
// TfGuidAtom, we do it once when Activate is called.
//----------------------------------------------------------------------------
BOOL CMarkTextService::_InitDisplayAttributeGuidAtom()
{
ITfCategoryMgr *pCategoryMgr;
HRESULT hr;
if (CoCreateInstance(CLSID_TF_CategoryMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITfCategoryMgr,
(void**)&pCategoryMgr) != S_OK)
{
return FALSE;
}
hr = pCategoryMgr->RegisterGUID(c_guidMarkDisplayAttribute, &_gaDisplayAttribute);
pCategoryMgr->Release();
return (hr == S_OK);
}