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.
859 lines
23 KiB
859 lines
23 KiB
//
|
|
// Audio playback function impl.
|
|
//
|
|
//
|
|
#include "private.h"
|
|
#include "sapilayr.h"
|
|
#include "playback.h"
|
|
#include "immxutil.h"
|
|
#include "propstor.h"
|
|
#include "hwxink.h"
|
|
|
|
//
|
|
// ctor/dtor
|
|
//
|
|
|
|
CSapiPlayBack::CSapiPlayBack(CSapiIMX *psi)
|
|
{
|
|
m_psi = psi;
|
|
m_pIC = NULL;
|
|
m_cRef = 1;
|
|
}
|
|
|
|
CSapiPlayBack::~CSapiPlayBack()
|
|
{
|
|
SafeRelease(m_pIC);
|
|
}
|
|
|
|
//
|
|
// IUnknown
|
|
//
|
|
|
|
STDMETHODIMP CSapiPlayBack::QueryInterface(REFGUID riid, LPVOID *ppvObj)
|
|
{
|
|
Assert(ppvObj);
|
|
*ppvObj = NULL;
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_ITfFnPlayBack))
|
|
{
|
|
*ppvObj = SAFECAST(this, CSapiPlayBack *);
|
|
}
|
|
|
|
if (*ppvObj)
|
|
{
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CSapiPlayBack::AddRef(void)
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CSapiPlayBack::Release(void)
|
|
{
|
|
long cr;
|
|
|
|
cr = InterlockedDecrement(&m_cRef);
|
|
Assert(cr >= 0);
|
|
|
|
if (cr == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return cr;
|
|
}
|
|
|
|
//
|
|
// ITfFunction
|
|
//
|
|
|
|
STDMETHODIMP CSapiPlayBack::GetDisplayName(BSTR *pbstrName)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pbstrName)
|
|
{
|
|
*pbstrName = SysAllocString(L"PlayBack Voice");
|
|
if (!*pbstrName)
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CSapiPlayBack::IsEnabled(BOOL *pfEnable)
|
|
{
|
|
*pfEnable = TRUE;
|
|
return S_OK;
|
|
}
|
|
//
|
|
// CSapiPlayBack::FindSoundRange
|
|
//
|
|
// synopsis - finds matching range with sound data for the given
|
|
// text range
|
|
// callar is responsible for releasing the returned range object
|
|
//
|
|
HRESULT
|
|
CSapiPlayBack::FindSoundRange(TfEditCookie ec, ITfRange *pRange, ITfProperty **ppProp, ITfRange **ppPropRange, ITfRange **ppSndRange)
|
|
{
|
|
|
|
ITfProperty *pProp = NULL;
|
|
ITfRange *pPropRange = NULL;
|
|
|
|
Assert(pRange);
|
|
*ppProp = NULL;
|
|
|
|
HRESULT hr = m_pIC->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &pProp);
|
|
|
|
if (SUCCEEDED(hr) && pProp)
|
|
{
|
|
ITfRange *pRangeSize0;
|
|
|
|
pRange->Clone(&pRangeSize0);
|
|
pRangeSize0->Collapse(ec, TF_ANCHOR_START); // findrange would fail if this failed
|
|
hr = pProp->FindRange(ec, pRangeSize0, &pPropRange, TF_ANCHOR_START);
|
|
|
|
pRangeSize0->Release();
|
|
*ppProp = pProp;
|
|
pProp->AddRef();
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pPropRange)
|
|
{
|
|
ITfRange *pRangeForSound;
|
|
hr = pPropRange->Clone(&pRangeForSound);
|
|
if (ppSndRange && SUCCEEDED(hr))
|
|
{
|
|
hr = pRangeForSound->Clone(ppSndRange);
|
|
pRangeForSound->Release();
|
|
}
|
|
if (ppPropRange && pPropRange && SUCCEEDED(hr))
|
|
{
|
|
hr = pPropRange->Clone(ppPropRange);
|
|
}
|
|
|
|
SafeRelease(pPropRange);
|
|
}
|
|
else
|
|
{
|
|
if (ppPropRange)
|
|
*ppPropRange = NULL;
|
|
|
|
if (ppSndRange)
|
|
*ppSndRange = NULL;
|
|
}
|
|
|
|
SafeRelease(pProp);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ITfFnPlayBack
|
|
//
|
|
//
|
|
STDAPI CSapiPlayBack::QueryRange(ITfRange *pRange, ITfRange **ppNewRange, BOOL *pfPlayable)
|
|
{
|
|
//
|
|
// always ok because of TTS.
|
|
//
|
|
if (ppNewRange)
|
|
{
|
|
pRange->Clone(ppNewRange);
|
|
}
|
|
*pfPlayable = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
// ITfFnPlayBack
|
|
//
|
|
// play the audio stream attached to the range
|
|
// TODO: use TTS if:
|
|
// 1) the given range has less text
|
|
// then the stream prop
|
|
// 2) the audio property is not found
|
|
//
|
|
STDAPI CSapiPlayBack::Play(ITfRange *pRange)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
if ( !m_psi )
|
|
return E_FAIL;
|
|
|
|
SafeRelease(m_pIC);
|
|
m_psi->GetFocusIC(&m_pIC);
|
|
|
|
if (m_pIC)
|
|
{
|
|
CPlayBackEditSession *pes;
|
|
|
|
if (pes = new CPlayBackEditSession(this, m_pIC))
|
|
{
|
|
pes->_SetEditSessionData(ESCB_PLAYBK_PLAYSND, NULL, 0);
|
|
pes->_SetRange(pRange);
|
|
|
|
m_pIC->RequestEditSession(m_psi->_GetId(), pes, TF_ES_READ /*| TF_ES_SYNC */, &hr);
|
|
pes->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSapiPlayBack::_PlaySound(TfEditCookie ec, ITfRange *pRange)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LONG l;
|
|
ITfRange *pRangeForSound = NULL;
|
|
ITfRange *pRangeCurrent = NULL;
|
|
BOOL fEmpty;
|
|
ULONG ulStart, ulcElem;
|
|
|
|
// playback one phrase for no selection
|
|
pRange->IsEmpty(ec, &fEmpty);
|
|
if (fEmpty)
|
|
{
|
|
ITfProperty *pProp = NULL;
|
|
hr = FindSoundRange(ec, pRange, &pProp, NULL, &pRangeForSound);
|
|
if (SUCCEEDED(hr) && pRangeForSound)
|
|
{
|
|
pRange->ShiftStartToRange(ec, pRangeForSound, TF_ANCHOR_START);
|
|
pRange->ShiftEndToRange(ec, pRangeForSound, TF_ANCHOR_END);
|
|
pRangeForSound->Release();
|
|
}
|
|
SafeReleaseClear(pProp);
|
|
}
|
|
// setting up a range object on our own
|
|
|
|
hr = pRange->Clone(&pRangeCurrent);
|
|
|
|
while(SUCCEEDED(hr) && pRangeCurrent->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
|
|
{
|
|
ITfProperty *pProp = NULL;
|
|
// get the first dictated range
|
|
|
|
CDictRange *pDictRange = new CDictRange( );
|
|
|
|
if ( pDictRange )
|
|
{
|
|
hr = pDictRange->Initialize(ec, m_pIC, pRangeCurrent);
|
|
|
|
if ( SUCCEEDED(hr) && pDictRange->IsDictRangeFound( ))
|
|
{
|
|
// Found a dictated range.
|
|
pRangeForSound = pDictRange->GetDictRange( );
|
|
ulStart = pDictRange->GetStartElem( );
|
|
ulcElem = pDictRange->GetNumElem( );
|
|
pProp = pDictRange->GetProp( );
|
|
|
|
// if start anchor of pRangeForSound is larger than start anchor of pRangeCurrent,
|
|
// we need to send the text between these two anchors to spVoice first.
|
|
|
|
hr = pRangeCurrent->CompareStart(ec, pRangeForSound, TF_ANCHOR_START, &l);
|
|
|
|
if ( SUCCEEDED(hr) && l < 0 )
|
|
{
|
|
CComPtr<ITfRange> cpRangeText;
|
|
|
|
hr = pRangeCurrent->Clone(&cpRangeText);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = cpRangeText->ShiftEndToRange(ec, pRangeForSound, TF_ANCHOR_START);
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
hr = PlayTextData(ec, cpRangeText);
|
|
}
|
|
|
|
// Then play the audio data.
|
|
|
|
if (SUCCEEDED(hr) )
|
|
{
|
|
hr = PlayAudioData(ec, pRangeForSound, pProp, ulStart, ulcElem);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// There is no dictated phrase in this range.
|
|
// just speak all the rest text at once.
|
|
SafeRelease(pRangeForSound);
|
|
hr = pRangeCurrent->Clone(&pRangeForSound);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
hr = PlayTextData(ec, pRangeCurrent);
|
|
}
|
|
|
|
SafeReleaseClear(pProp);
|
|
|
|
// next range
|
|
pRangeCurrent->ShiftStartToRange(ec, pRangeForSound, TF_ANCHOR_END);
|
|
pRangeForSound->Release();
|
|
|
|
delete pDictRange;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
} // end of while
|
|
|
|
SafeRelease(pRangeCurrent);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CSapiPlayBack::PlayTextData
|
|
//
|
|
// Playing the text by default voice
|
|
//
|
|
// This is for Non-Dictated text. pRangeText contains all the Non-Dictated text
|
|
|
|
const GUID GUID_TS_SERVICE_DATAOBJECT={0x6086fbb5, 0xe225, 0x46ce, {0xa7, 0x70, 0xc1, 0xbb, 0xd3, 0xe0, 0x5d, 0x7b}};
|
|
const IID IID_ILineInfo = {0x9C1C5AD5,0xF22F,0x4DE4,{0xB4,0x53,0xA2,0xCC,0x48,0x2E,0x7C,0x33}};
|
|
|
|
HRESULT CSapiPlayBack::GetInkObjectText(TfEditCookie ec, ITfRange *pRange, BSTR *pbstrWord,UINT *pcchWord)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IDataObject> cpDataObject;
|
|
CComPtr<ILineInfo> cpLineInfo;
|
|
|
|
if ( !pRange || !pbstrWord || !pcchWord )
|
|
return E_FAIL;
|
|
|
|
*pbstrWord = NULL;
|
|
*pcchWord = 0;
|
|
|
|
hr = pRange->GetEmbedded(ec,
|
|
GUID_TS_SERVICE_DATAOBJECT,
|
|
IID_IDataObject,
|
|
(IUnknown **)&cpDataObject);
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = cpDataObject->QueryInterface(IID_ILineInfo, (void **)&cpLineInfo);
|
|
}
|
|
|
|
if ( hr == S_OK && cpLineInfo)
|
|
{
|
|
hr = cpLineInfo->TopCandidates(0, pbstrWord, pcchWord, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// it doesn't support ILineInfoi or IDataObject.
|
|
// But it is not an error, the code should not terminate here.
|
|
hr = S_OK;
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CSapiPlayBack::PlayTextData(TfEditCookie ec, ITfRange *pRangeText)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<ITfRange> cpRangeCloned;
|
|
BOOL fEmpty = TRUE;
|
|
CSpDynamicString dstrText;
|
|
CSpTask *psp;
|
|
WCHAR sz[128];
|
|
ULONG iIndex = 0;
|
|
ULONG ucch;
|
|
|
|
if ( m_psi == NULL ) return E_FAIL;
|
|
|
|
if ( !pRangeText ) return E_INVALIDARG;
|
|
|
|
hr = pRangeText->Clone(&cpRangeCloned);
|
|
|
|
// Get the text from the pRangeCloned
|
|
while(S_OK == hr && (S_OK == cpRangeCloned->IsEmpty(ec, &fEmpty)) && !fEmpty)
|
|
{
|
|
WCHAR szEach[2];
|
|
BOOL fHitInkObject = FALSE;
|
|
BSTR bstr = NULL;
|
|
|
|
fHitInkObject = FALSE;
|
|
hr = cpRangeCloned->GetText(ec, TF_TF_MOVESTART, szEach, ARRAYSIZE(szEach)-1, &ucch);
|
|
if (S_OK == hr && ucch > 0)
|
|
{
|
|
szEach[ucch] = L'\0';
|
|
if ( szEach[0] == TF_CHAR_EMBEDDED )
|
|
{
|
|
// This is an embedded object.
|
|
// Check to see if it is Ink Object. currently we support only Inkobject TTSed
|
|
CComPtr<ITfRange> cpRangeTmp;
|
|
|
|
// Shift the start anchor back by 1 char.
|
|
hr = cpRangeCloned->Clone(&cpRangeTmp);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
LONG cch;
|
|
hr = cpRangeTmp->ShiftStart(ec, -1, &cch, 0 );
|
|
}
|
|
|
|
if ( hr == S_OK )
|
|
hr = GetInkObjectText(ec, cpRangeTmp, &bstr,(UINT *)&ucch);
|
|
|
|
if ( hr == S_OK && ucch > 0 && bstr)
|
|
fHitInkObject = TRUE;
|
|
}
|
|
|
|
if ( fHitInkObject)
|
|
{
|
|
// Fill the previous text to dstrText.
|
|
if ( iIndex > 0 )
|
|
{
|
|
sz[iIndex] = L'\0';
|
|
dstrText.Append(sz);
|
|
iIndex = 0;
|
|
}
|
|
|
|
// Fill this Ink Object text
|
|
dstrText.Append(bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
else
|
|
{
|
|
if ( iIndex >= ARRAYSIZE(sz)-1 )
|
|
{
|
|
sz[ARRAYSIZE(sz)-1] = L'\0';
|
|
dstrText.Append(sz);
|
|
iIndex=0;
|
|
}
|
|
|
|
sz[iIndex] = szEach[0];
|
|
iIndex ++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hr is not S_OK or ucch is zero.
|
|
// we just want to exit here.
|
|
TraceMsg(TF_GENERAL, "PlayTextData: ucch=%d", ucch);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fill the last run of text.
|
|
if ( iIndex > 0 )
|
|
{
|
|
sz[iIndex] = L'\0';
|
|
dstrText.Append(sz);
|
|
iIndex = 0;
|
|
}
|
|
|
|
// Play the text through TTS service.
|
|
if ((hr == S_OK) && dstrText)
|
|
{
|
|
hr = m_psi->GetSpeechTask(&psp);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = psp->_SpeakText((WCHAR *)dstrText);
|
|
psp->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CSapiPlayBack::PlayAudioData
|
|
//
|
|
// Playing the sound by Aduio Data
|
|
//
|
|
// This is for Dictated text. pRangeAudio keeps the dictated text range.
|
|
|
|
HRESULT CSapiPlayBack::PlayAudioData(TfEditCookie ec, ITfRange *pRangeAudio, ITfProperty *pProp, ULONG ulStart, ULONG ulcElem )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CSpTask *psp;
|
|
VARIANT var;
|
|
|
|
if ( m_psi == NULL ) return E_FAIL;
|
|
|
|
if ( !pRangeAudio || !pProp ) return E_INVALIDARG;
|
|
|
|
hr = pProp->GetValue(ec, pRangeAudio, &var);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
Assert(var.vt == VT_UNKNOWN);
|
|
IUnknown *punk = var.punkVal;
|
|
if (punk)
|
|
{
|
|
// get the wrapper object
|
|
CRecoResultWrap *pWrap;
|
|
|
|
hr = punk->QueryInterface(IID_PRIV_RESULTWRAP, (void **)&pWrap);
|
|
if (S_OK == hr)
|
|
{
|
|
// hr = pWrap->_SpeakAudio(0,0); // better calculate the length accurately
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
hr = pWrap->GetResult(&cpResult);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
CComPtr<ISpStreamFormat> cpStream;
|
|
|
|
if ((ulStart == 0) && (ulcElem == 0))
|
|
{
|
|
// We don't set the start element and number of elems.
|
|
// it must be for the whole recoWrap.
|
|
ulStart = pWrap->GetStart();
|
|
ulcElem = pWrap->GetNumElements();
|
|
}
|
|
|
|
hr = cpResult->GetAudio(ulStart, ulcElem, &cpStream);
|
|
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = m_psi->GetSpeechTask(&psp);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psp->_SpeakAudio(cpStream);
|
|
psp->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
pWrap->Release();
|
|
}
|
|
punk->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CSapiPlayBack::GetDataID
|
|
//
|
|
// This method is incomplete for now, until we figure out
|
|
// the usage of dataid
|
|
//
|
|
HRESULT CSapiPlayBack::GetDataID(BSTR bstrCandXml, int nId, GUID *pguidData)
|
|
{
|
|
// 1) parse the list and find RANGEDATA
|
|
IXMLDOMNodeList *pNList = NULL;
|
|
IXMLDOMElement *pElm = NULL;
|
|
IXMLDOMNode *pNode;
|
|
VARIANT_BOOL bSuccessful;
|
|
|
|
HRESULT hr = EnsureIXMLDoc();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pIXMLDoc->loadXML(bstrCandXml, &bSuccessful);
|
|
}
|
|
|
|
// get <RANGEDATA> element
|
|
if (SUCCEEDED(hr) && bSuccessful)
|
|
{
|
|
BSTR bstrRange = SysAllocString(L"RANGEDATA");
|
|
if (bstrRange)
|
|
{
|
|
hr = m_pIXMLDoc->getElementsByTagName(bstrRange, &pNList);
|
|
SysFreeString(bstrRange);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pNList)
|
|
{
|
|
if (pNList->nextNode(&pNode) == S_OK)
|
|
hr = pNode->QueryInterface(IID_IXMLDOMElement,(void **)&pElm);
|
|
|
|
pNList->Release();
|
|
}
|
|
// then <MICROSOFTSPEECH> ...
|
|
if (SUCCEEDED(hr) && pElm)
|
|
{
|
|
BSTR bstrSpchPriv = SysAllocString(L"MICROSOFTSPEECH");
|
|
if (bstrSpchPriv)
|
|
{
|
|
hr = pElm->getElementsByTagName(bstrSpchPriv, &pNList);
|
|
SysFreeString(bstrSpchPriv);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
pElm->Release();
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pNList)
|
|
{
|
|
if (pNList->nextNode(&pNode) == S_OK)
|
|
hr = pNode->QueryInterface(IID_IXMLDOMElement,(void **)&pElm);
|
|
|
|
pNList->Release();
|
|
}
|
|
|
|
// <DATAID>
|
|
// right now assuming the speech element
|
|
// is put at the level of <rangedata>
|
|
// ignoring nId here
|
|
if (SUCCEEDED(hr) && pElm)
|
|
{
|
|
BSTR bstrDataId = SysAllocString(L"DATAID");
|
|
if (bstrDataId)
|
|
{
|
|
hr = pElm->getElementsByTagName(bstrDataId, &pNList);
|
|
SysFreeString(bstrDataId);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
pElm->Release();
|
|
}
|
|
// impl later...
|
|
// so, here we'll get the real dataid and be done
|
|
|
|
if (SUCCEEDED(hr) && pNList)
|
|
{
|
|
pNList->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSapiPlayBack::_PlaySoundSelection(TfEditCookie ec, ITfContext *pic)
|
|
{
|
|
ITfRange *pSelection;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (GetSelectionSimple(ec, pic, &pSelection) == S_OK)
|
|
{
|
|
hr = _PlaySound(ec, pSelection);
|
|
pSelection->Release();
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT CSapiPlayBack::EnsureIXMLDoc(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IXMLDOMDocument *pIXMLDocument;
|
|
if (!m_pIXMLDoc)
|
|
{
|
|
if (SUCCEEDED(hr = CoCreateInstance(CLSID_DOMDocument,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IXMLDOMDocument,
|
|
(void **) &pIXMLDocument)))
|
|
{
|
|
m_pIXMLDoc = pIXMLDocument;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Implementation for Class CDictRange
|
|
//
|
|
//
|
|
|
|
//
|
|
// ctor/dtor
|
|
//
|
|
CDictRange::CDictRange( ) : CBestPropRange( )
|
|
{
|
|
m_fFoundDictRange = FALSE;
|
|
m_pProp = NULL;
|
|
m_pDictatRange = NULL;
|
|
m_ulStart = 0;
|
|
m_ulcElem = 0;
|
|
}
|
|
|
|
|
|
CDictRange::~CDictRange( )
|
|
{
|
|
SafeRelease(m_pProp);
|
|
SafeRelease(m_pDictatRange);
|
|
}
|
|
|
|
|
|
ITfProperty *CDictRange::GetProp( )
|
|
{
|
|
if ( m_pProp )
|
|
m_pProp->AddRef( );
|
|
|
|
return m_pProp;
|
|
}
|
|
|
|
ITfRange *CDictRange::GetDictRange( )
|
|
{
|
|
if ( m_pDictatRange )
|
|
m_pDictatRange->AddRef( );
|
|
|
|
return m_pDictatRange;
|
|
}
|
|
|
|
|
|
HRESULT CDictRange::_GetOverlapRange(TfEditCookie ec, ITfRange *pRange1, ITfRange *pRange2, ITfRange **ppOverlapRange)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LONG l1=0;
|
|
LONG l2=0;
|
|
|
|
CComPtr<ITfRange> cpRangeOverlap;
|
|
|
|
if ( !pRange1 || !pRange2 || !ppOverlapRange )
|
|
return E_INVALIDARG;
|
|
|
|
*ppOverlapRange = NULL;
|
|
|
|
// Get the overlapping part of pRange and cpPropRange, and then calculate the
|
|
// best matched propRange.
|
|
|
|
hr = pRange1->Clone( &cpRangeOverlap );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
hr = pRange1->CompareStart(ec, pRange2, TF_ANCHOR_START, &l1);
|
|
|
|
if ( SUCCEEDED(hr) && l1 < 0 )
|
|
{
|
|
// Start anchor of pRange1 is before the start anchor of the
|
|
// pRange2.
|
|
hr = cpRangeOverlap->ShiftStartToRange(ec, pRange2, TF_ANCHOR_START);
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
hr = cpRangeOverlap->CompareEnd(ec, pRange2, TF_ANCHOR_END, &l2);
|
|
|
|
if ( SUCCEEDED(hr) && l2 > 0)
|
|
{
|
|
// End anchor of cpRangeOverlap is after the end anchor of the pRange2.
|
|
hr = cpRangeOverlap->ShiftEndToRange(ec, pRange2, TF_ANCHOR_END);
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
hr = cpRangeOverlap->Clone(ppOverlapRange);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CDictRange::Initialize
|
|
//
|
|
// synopsis - Get the necessary input parameters, (ec, pic, and pRange),
|
|
// and then search the first dictated range inside the given range.
|
|
// if it finds the first dictated range, update the related
|
|
// data memebers.
|
|
//
|
|
// if it doesn't find any dictated range, mark as not found.
|
|
//
|
|
HRESULT CDictRange::Initialize(TfEditCookie ec, ITfContext *pic, ITfRange *pRange)
|
|
{
|
|
CComPtr<ITfRange> cpPropRange = NULL;
|
|
HRESULT hr;
|
|
|
|
m_fFoundDictRange = FALSE;
|
|
m_pProp = NULL;
|
|
m_pDictatRange = NULL;
|
|
m_ulStart = 0;
|
|
m_ulcElem = 0;
|
|
|
|
if ( !pic || !pRange ) return E_INVALIDARG;
|
|
|
|
hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &m_pProp);
|
|
|
|
if ( SUCCEEDED(hr) && m_pProp)
|
|
{
|
|
CComPtr<ITfRange> cpRangeCurrent;
|
|
LONG cch;
|
|
|
|
hr = pRange->Clone(&cpRangeCurrent);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = cpRangeCurrent->Collapse(ec, TF_ANCHOR_START);
|
|
}
|
|
|
|
while ( SUCCEEDED(hr) && !m_fFoundDictRange )
|
|
{
|
|
cpPropRange.Release( );
|
|
hr = m_pProp->FindRange(ec, cpRangeCurrent, &cpPropRange, TF_ANCHOR_START);
|
|
|
|
if ( SUCCEEDED(hr) && cpPropRange )
|
|
{
|
|
// Found the first Dictated phrase.
|
|
CComPtr<ITfRange> cpRangeOverlap;
|
|
|
|
// Get the overlapping part of pRange and cpPropRange, and then calculate the
|
|
// best matched propRange.
|
|
|
|
hr = _GetOverlapRange(ec, pRange, cpPropRange, &cpRangeOverlap);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Calculate the best matched propRange and ulStart, ulcElem.
|
|
cpPropRange.Release( );
|
|
hr = _ComputeBestFitPropRange(ec, m_pProp, cpRangeOverlap, &cpPropRange, &m_ulStart, &m_ulcElem);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && (m_ulcElem > 0))
|
|
{
|
|
m_fFoundDictRange = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) && !m_fFoundDictRange)
|
|
{
|
|
// cpRangeCurrent shift forward one character. and try again.
|
|
|
|
hr = cpRangeCurrent->ShiftStart(ec, 1, &cch, NULL);
|
|
|
|
if ( SUCCEEDED(hr) && (cch==0))
|
|
{
|
|
// Hit a region or the end of doc.
|
|
// check to see if there is more region.
|
|
BOOL fNoRegion = TRUE;
|
|
|
|
hr = cpRangeCurrent->ShiftStartRegion(ec, TF_SD_FORWARD, &fNoRegion);
|
|
|
|
if ( fNoRegion )
|
|
{
|
|
TraceMsg(TF_GENERAL, "Reach to end of doc");
|
|
break;
|
|
}
|
|
else
|
|
TraceMsg(TF_GENERAL, "Shift over to another region!");
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// check to see if cpRangeCurrent is beyond of the pRange.
|
|
hr = pRange->CompareEnd(ec, cpRangeCurrent, TF_ANCHOR_END, &cch);
|
|
|
|
if ( SUCCEEDED(hr) && cch <= 0 )
|
|
{
|
|
// cpRangeCurrent Now is beyond of specified range, exit while statement.
|
|
TraceMsg(TF_GENERAL, "reach to the end of original range");
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && cpPropRange && m_fFoundDictRange)
|
|
{
|
|
hr = cpPropRange->Clone(&m_pDictatRange);
|
|
}
|
|
|
|
return hr;
|
|
}
|