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.
639 lines
17 KiB
639 lines
17 KiB
//
|
|
// reconv.cpp
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "globals.h"
|
|
#include "tim.h"
|
|
#include "ic.h"
|
|
#include "helpers.h"
|
|
#include "fnrecon.h"
|
|
#include "funcprv.h"
|
|
#include "ptrary.h"
|
|
#include "immxutil.h"
|
|
#include "proputil.h"
|
|
#include "rprop.h"
|
|
#include "range.h"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ::GrowEmptyRangeByOne
|
|
//
|
|
// Helper to enlarge empty ranges by shifting the end anchor + 1.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT GrowEmptyRangeByOne(CInputContext *pic, ITfRange *range)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pic->_DoPseudoSyncEditSession(TF_ES_READ, PSEUDO_ESCB_GROWRANGE, range, &hr) != S_OK || hr != S_OK)
|
|
{
|
|
Assert(0);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT GrowEmptyRangeByOneCallback(TfEditCookie ec, ITfRange *range)
|
|
{
|
|
BOOL fEmpty;
|
|
LONG l;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Check the length of the given range.
|
|
// If the given range is 0 length, we try to find the owner of
|
|
// the next char.
|
|
//
|
|
range->IsEmpty(ec, &fEmpty);
|
|
if (fEmpty)
|
|
{
|
|
hr = range->ShiftEnd(ec, +1, &l, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CFunction
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CFunction::CFunction(CFunctionProvider *pFuncPrv)
|
|
{
|
|
_pFuncPrv = pFuncPrv;
|
|
_pFuncPrv->AddRef();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// dtor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CFunction::~CFunction()
|
|
{
|
|
SafeRelease(_pFuncPrv);
|
|
CleanUpOwnerRange();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CleanUpOwnerRange
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CFunction::CleanUpOwnerRange()
|
|
{
|
|
CRangeOwnerList *pRangeOwner;
|
|
while (pRangeOwner = _listRangeOwner.GetFirst())
|
|
{
|
|
_listRangeOwner.Remove(pRangeOwner);
|
|
delete pRangeOwner;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// BuildOwnerRangeList
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CFunction::BuildOwnerRangeList(CInputContext *pic, ITfRange *pRange)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BUILDOWNERRANGELISTQUEUEINFO qInfo;
|
|
BOOL bRet = TRUE;
|
|
|
|
qInfo.pFunc = this;
|
|
qInfo.pRange = pRange;
|
|
|
|
if (pic->_DoPseudoSyncEditSession(TF_ES_READ, PSEUDO_ESCB_BUILDOWNERRANGELIST, &qInfo, &hr) != S_OK || hr != S_OK)
|
|
{
|
|
Assert(0);
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// BuildOwnerRangeListCallback
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CFunction::BuildOwnerRangeListCallback(TfEditCookie ec, CInputContext *pic, ITfRange *pRange)
|
|
{
|
|
CProperty *pProp;
|
|
IEnumTfRanges *pEnumPropRange;
|
|
CRange *pRangeP = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (pic->_pPropTextOwner == NULL)
|
|
goto ExitOk;
|
|
|
|
pProp = pic->_pPropTextOwner;
|
|
|
|
CleanUpOwnerRange();
|
|
|
|
//
|
|
// if pRange is NULL, we build owner list for entire dcoument.
|
|
// we will enumerate all property ranges.
|
|
//
|
|
if (pRange)
|
|
{
|
|
if ((pRangeP = GetCRange_NA(pRange)) == NULL)
|
|
goto Exit;
|
|
}
|
|
|
|
if (SUCCEEDED(pProp->EnumRanges(ec, &pEnumPropRange, pRange)))
|
|
{
|
|
ITfRange *pPropRange;
|
|
while (pEnumPropRange->Next(1, &pPropRange, NULL) == S_OK)
|
|
{
|
|
TfGuidAtom guidOwner;
|
|
CRangeOwnerList *pRangeOwner;
|
|
ITfRange *pRangeTmp;
|
|
CRange *pRangeTmpP;
|
|
BOOL bKnownOwner = FALSE;
|
|
|
|
pPropRange->Clone(&pRangeTmp);
|
|
|
|
GetGUIDPropertyData(ec, pProp, pPropRange, &guidOwner);
|
|
|
|
//
|
|
// check if this guidOwner already appeared in the range.
|
|
//
|
|
pRangeOwner = _listRangeOwner.GetFirst();
|
|
while(pRangeOwner)
|
|
{
|
|
if (guidOwner == pRangeOwner->_guidOwner)
|
|
{
|
|
bKnownOwner = TRUE;
|
|
}
|
|
pRangeOwner = pRangeOwner->GetNext();
|
|
}
|
|
|
|
//
|
|
// get CRange.
|
|
//
|
|
if ((pRangeTmpP = GetCRange_NA(pRangeTmp)) == NULL)
|
|
goto NoCRange;
|
|
|
|
//
|
|
// if pRangeP is NULL, we build owner list for entire document.
|
|
// So we don't have to adjust pRangeTmp.
|
|
//
|
|
if (pRangeP)
|
|
{
|
|
if (CompareAnchors(pRangeTmpP->_GetStart(), pRangeP->_GetStart()) < 0)
|
|
{
|
|
// move pRangeTmp's start to match pRange
|
|
pRangeTmpP->_GetStart()->ShiftTo(pRangeP->_GetStart());
|
|
// insure pRangeTmp's end is no greater than pRange's end
|
|
if (CompareAnchors(pRangeTmpP->_GetEnd(), pRangeP->_GetEnd()) > 0)
|
|
{
|
|
pRangeTmpP->_GetEnd()->ShiftTo(pRangeP->_GetEnd());
|
|
}
|
|
}
|
|
else if (CompareAnchors(pRangeTmpP->_GetEnd(), pRangeP->_GetEnd()) > 0)
|
|
{
|
|
pRangeTmpP->_GetEnd()->ShiftTo(pRangeP->_GetEnd());
|
|
}
|
|
}
|
|
|
|
pRangeOwner = new CRangeOwnerList(guidOwner, pRangeTmp, bKnownOwner);
|
|
_listRangeOwner.Add(pRangeOwner);
|
|
|
|
NoCRange:
|
|
pPropRange->Release();
|
|
pRangeTmp->Release();
|
|
}
|
|
pEnumPropRange->Release();
|
|
}
|
|
|
|
ExitOk:
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CFnReconversion
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CFnReconversion::CFnReconversion(CFunctionProvider *pFuncPrv) :CFunction(pFuncPrv)
|
|
{
|
|
_pReconvCache = NULL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// dtor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CFnReconversion::~CFnReconversion()
|
|
{
|
|
SafeRelease(_pReconvCache);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetDisplayName
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CFnReconversion::GetDisplayName(BSTR *pbstrName)
|
|
{
|
|
*pbstrName = SysAllocString(L"Reconversion");
|
|
return *pbstrName != NULL ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CFnReconversion::GetReconversion
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CFnReconversion::GetReconversion(ITfRange *pRange, ITfCandidateList **ppCandList)
|
|
{
|
|
if (ppCandList == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
*ppCandList = NULL;
|
|
|
|
if (pRange == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
return Internal_GetReconversion(pRange, ppCandList, NULL, RF_GETRECONVERSION, NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CFnReconversion::QueryRange
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CFnReconversion::QueryRange(ITfRange *pRange, ITfRange **ppNewRange, BOOL *pfConvertable)
|
|
{
|
|
if (ppNewRange != NULL)
|
|
{
|
|
*ppNewRange = NULL;
|
|
}
|
|
if (pfConvertable != NULL)
|
|
{
|
|
*pfConvertable = FALSE;
|
|
}
|
|
if (pRange == NULL ||
|
|
ppNewRange == NULL ||
|
|
pfConvertable == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return Internal_GetReconversion(pRange, NULL, ppNewRange, RF_QUERYRECONVERT, pfConvertable);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CFnReconversion::Reconvert
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CFnReconversion::Reconvert(ITfRange *pRange)
|
|
{
|
|
if (pRange == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
return Internal_GetReconversion(pRange, NULL, NULL, RF_RECONVERT, NULL);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CFnReconversion::Internal_GetReconversion
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CFnReconversion::Internal_GetReconversion(ITfRange *pRange, ITfCandidateList **ppCandList, ITfRange **ppNewRange, RECONVFUNC rf, BOOL *pfConvertable)
|
|
{
|
|
BOOL bReleaseCache = FALSE;
|
|
HRESULT hr = E_FAIL;
|
|
ITfRange *pRangeTmp = NULL;
|
|
ITfRange *pNewRange = NULL;
|
|
ITfContext *pic = NULL;
|
|
|
|
if (FAILED(pRange->Clone(&pRangeTmp)))
|
|
goto Exit;
|
|
|
|
if (FAILED(pRangeTmp->GetContext(&pic)))
|
|
goto Exit;
|
|
|
|
//
|
|
// when RF_QUERYRECONVERT, we alwasy create new Reconv cache.
|
|
// we will keep using this chace unless another RF_QUERYRECONVERT comes.
|
|
//
|
|
if (rf == RF_QUERYRECONVERT)
|
|
SafeReleaseClear(_pReconvCache);
|
|
|
|
if (!_pReconvCache)
|
|
{
|
|
CInputContext *pcic = GetCInputContext(pic);
|
|
if (pcic)
|
|
{
|
|
QueryAndGetFunction(pcic, pRangeTmp, &_pReconvCache, &pNewRange);
|
|
pcic->Release();
|
|
}
|
|
|
|
//
|
|
// when it's not RF_QUERYRECONVERT and there was no cache,
|
|
// we don't hold the reconv cache.
|
|
//
|
|
if (rf != RF_QUERYRECONVERT)
|
|
bReleaseCache = TRUE;
|
|
}
|
|
|
|
if (!_pReconvCache)
|
|
{
|
|
hr = S_OK;
|
|
goto Exit;
|
|
}
|
|
|
|
switch (rf)
|
|
{
|
|
case RF_GETRECONVERSION:
|
|
if ((hr = _pReconvCache->GetReconversion(pRangeTmp, ppCandList)) != S_OK)
|
|
{
|
|
*ppCandList = NULL;
|
|
}
|
|
break;
|
|
|
|
case RF_RECONVERT:
|
|
hr = _pReconvCache->Reconvert(pRangeTmp);
|
|
break;
|
|
|
|
case RF_QUERYRECONVERT:
|
|
if (!pNewRange)
|
|
{
|
|
if ((hr = _pReconvCache->QueryRange(pRangeTmp, ppNewRange, pfConvertable)) != S_OK)
|
|
{
|
|
*ppNewRange = NULL;
|
|
*pfConvertable = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppNewRange = pNewRange;
|
|
(*ppNewRange)->AddRef();
|
|
*pfConvertable = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Assert(hr == S_OK);
|
|
|
|
Exit:
|
|
if (bReleaseCache || FAILED(hr))
|
|
SafeReleaseClear(_pReconvCache);
|
|
|
|
SafeRelease(pRangeTmp);
|
|
SafeRelease(pNewRange);
|
|
SafeRelease(pic);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CFnReconversion::QueryAndGetFunction(CInputContext *pic, ITfRange *pRange, ITfFnReconversion **ppFunc, ITfRange **ppRange)
|
|
{
|
|
IEnumTfFunctionProviders *pEnumFuncPrv;
|
|
ITfFunctionProvider *pFuncPrv;
|
|
CRangeOwnerList *pRangeOwner;
|
|
HRESULT hr = E_FAIL;
|
|
ITfRange *pRangeTmp = NULL;
|
|
CThreadInputMgr *ptim;
|
|
|
|
*ppFunc = NULL;
|
|
|
|
if ((ptim = CThreadInputMgr::_GetThis()) == NULL)
|
|
goto Exit;
|
|
|
|
if (pRange)
|
|
{
|
|
//
|
|
// To find the properr function provider, we use pRangeTmp.
|
|
//
|
|
if (FAILED(pRange->Clone(&pRangeTmp)))
|
|
goto Exit;
|
|
|
|
//
|
|
// Check the length of the given range.
|
|
// If the given range is 0 length, we try to find the owner of
|
|
// the next char.
|
|
//
|
|
if (GrowEmptyRangeByOne(pic, pRangeTmp) != S_OK)
|
|
goto Exit;
|
|
}
|
|
|
|
if (!BuildOwnerRangeList(pic, pRangeTmp))
|
|
goto Exit;
|
|
|
|
pRangeOwner = _listRangeOwner.GetFirst();
|
|
|
|
if (pRangeOwner)
|
|
{
|
|
GUID guid;
|
|
|
|
if (SUCCEEDED(MyGetGUID(pRangeOwner->_guidOwner, &guid)))
|
|
{
|
|
CTip *ptip;
|
|
|
|
//
|
|
// A way to get the TextOwner's reconversion function.
|
|
//
|
|
// - find a function provider of the ower.
|
|
// - Do QI the text owner TIP.
|
|
// - CoCreate the text onwer CLSID.
|
|
//
|
|
if (SUCCEEDED(ptim->GetFunctionProvider(guid, &pFuncPrv)))
|
|
{
|
|
hr = pFuncPrv->GetFunction(GUID_NULL,
|
|
IID_ITfFnReconversion,
|
|
(IUnknown **)ppFunc);
|
|
|
|
SafeReleaseClear(pFuncPrv);
|
|
}
|
|
else if (ptim->_GetCTipfromGUIDATOM(pRangeOwner->_guidOwner, &ptip) && ptip->_pTip)
|
|
{
|
|
hr = ptip->_pTip->QueryInterface(IID_ITfFnReconversion,
|
|
(void **)ppFunc);
|
|
}
|
|
else
|
|
{
|
|
hr = CoCreateInstance(guid,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITfFnReconversion,
|
|
(void**)ppFunc);
|
|
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
*ppFunc = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if there is no owner or the owner of the first range does not
|
|
// have ITfFunction, we may find someone who has
|
|
// ITfFunction.
|
|
//
|
|
if (!(*ppFunc) &&
|
|
SUCCEEDED(ptim->EnumFunctionProviders(&pEnumFuncPrv)))
|
|
{
|
|
while (!(*ppFunc) && pEnumFuncPrv->Next(1, &pFuncPrv, NULL) == S_OK)
|
|
{
|
|
GUID guid;
|
|
|
|
BOOL fSkip = TRUE;
|
|
if (SUCCEEDED(pFuncPrv->GetType(&guid)))
|
|
{
|
|
if (!IsEqualGUID(guid, GUID_SYSTEM_FUNCTIONPROVIDER))
|
|
fSkip = FALSE;
|
|
}
|
|
|
|
if(!fSkip)
|
|
{
|
|
hr = pFuncPrv->GetFunction(GUID_NULL, IID_ITfFnReconversion, (IUnknown **)ppFunc);
|
|
|
|
if ((SUCCEEDED(hr) && *ppFunc))
|
|
{
|
|
BOOL fConvertable = FALSE;
|
|
hr = (*ppFunc)->QueryRange(pRange, ppRange, &fConvertable);
|
|
if (FAILED(hr) || !fConvertable)
|
|
{
|
|
(*ppFunc)->Release();
|
|
*ppFunc = NULL;
|
|
}
|
|
}
|
|
}
|
|
SafeReleaseClear(pFuncPrv);
|
|
}
|
|
pEnumFuncPrv->Release();
|
|
}
|
|
|
|
Exit:
|
|
SafeRelease(pRangeTmp);
|
|
return (*ppFunc) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CFnAbort
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CFnAbort::CFnAbort(CFunctionProvider *pFuncPrv) : CFunction(pFuncPrv)
|
|
{
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// dtor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CFnAbort::~CFnAbort()
|
|
{
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetDisplayName
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CFnAbort::GetDisplayName(BSTR *pbstrName)
|
|
{
|
|
*pbstrName = SysAllocString(L"Abort");
|
|
return *pbstrName != NULL ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CFnAbort::Abort
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CFnAbort::Abort(ITfContext *pic)
|
|
{
|
|
CThreadInputMgr *ptim;
|
|
HRESULT hr = E_FAIL;
|
|
int i;
|
|
int nCnt;
|
|
|
|
if (!pic)
|
|
return E_INVALIDARG;
|
|
|
|
if ((ptim = CThreadInputMgr::_GetThis()) == NULL)
|
|
goto Exit;
|
|
|
|
//
|
|
// notify all tips with ITfFnAbort to abort any pending conversion.
|
|
//
|
|
nCnt = ptim->_GetTIPCount();
|
|
for (i = 0; i < nCnt; i++)
|
|
{
|
|
ITfFnAbort *pAbort;
|
|
const CTip *ptip = ptim->_GetCTip(i);
|
|
|
|
if (!ptip->_pFuncProvider)
|
|
continue;
|
|
|
|
if (SUCCEEDED(ptip->_pFuncProvider->GetFunction(GUID_NULL,
|
|
IID_ITfFnAbort,
|
|
(IUnknown **)&pAbort)))
|
|
{
|
|
pAbort->Abort(pic);
|
|
pAbort->Release();
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|