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.
 
 
 
 
 
 

750 lines
20 KiB

//
//
// Sapilayr TIP CLearnFromDoc implementation.
//
//
#include "private.h"
#include "sapilayr.h"
#include "learndoc.h"
#include "cregkey.h"
// ----------------------------------------------------------------------------------------------------------
//
// Implementation for CLeanrFromDoc
//
// -----------------------------------------------------------------------------------------------------------
CLearnFromDoc::CLearnFromDoc(CSapiIMX *psi)
{
m_psi = psi;
m_pwszDocBlock = NULL;
_pic = NULL;
_pCSpTask = NULL;
_cpStartRange = NULL;
_cchBlockSize = -1;
_fMoreContent = FALSE;
_fLearnFromDoc = FALSE;
}
CLearnFromDoc::~CLearnFromDoc( )
{
if ( m_pwszDocBlock )
cicMemFree(m_pwszDocBlock);
_ClearDimList( );
};
void CLearnFromDoc::UpdateLearnDocState( )
{
DWORD dw;
GetCompartmentDWORD(m_psi->_tim, GUID_COMPARTMENT_SPEECH_LEARNDOC, &dw, FALSE);
if ( dw != 0 )
_fLearnFromDoc = TRUE;
else
_fLearnFromDoc = FALSE;
if ( _fLearnFromDoc == FALSE )
{
if ( m_pwszDocBlock )
{
cicMemFree(m_pwszDocBlock);
m_pwszDocBlock = NULL;
}
_UpdateRecoContextInterestSet(FALSE);
_ResetDimListFeedState( );
}
}
ULONG CLearnFromDoc::_GetDocBlockSize( )
{
if (_cchBlockSize == (ULONG)-1)
{
CMyRegKey regkey;
if (S_OK == regkey.Open(HKEY_LOCAL_MACHINE, c_szSapilayrKey, KEY_READ))
{
DWORD dw;
if (ERROR_SUCCESS==regkey.QueryValue(dw, c_szDocBlockSize))
{
_cchBlockSize = dw;
}
}
if (_cchBlockSize == (ULONG)-1)
{
_cchBlockSize = SIZE_DOCUMENT_BLOCK;
}
if ( _cchBlockSize < SIZE_FIRST_BLOCK )
_cchBlockSize = SIZE_FIRST_BLOCK; // the first block size is the minimize size.
}
return _cchBlockSize;
}
HRESULT CLearnFromDoc::HandleLearnFromDoc(ITfDocumentMgr *pDim )
{
HRESULT hr = S_OK;
CComPtr<ITfContext> cpic=NULL;
ITfDocumentMgr *dim;
if ( !m_psi )
return E_FAIL;
if ( m_psi->GetFocusIC(&cpic) && (GetLearnFromDoc( ) == TRUE))
{
if ( !pDim )
{
// Try to get the Dim from current focused IC.
hr = cpic->GetDocumentMgr(&dim);
}
else
{
dim = pDim;
dim->AddRef( );
}
if ( !dim )
hr = E_FAIL;
if ( S_OK == hr )
{
// Check to see if this doc has already been fed to SR LM engine.
BOOL fFedAlready = FALSE;
hr = _IsDimAlreadyFed(dim, &fFedAlready);
if ( (S_OK == hr) && !fFedAlready )
{
// Check to if the current doc is ReadOnly or not.
TF_STATUS docStatus;
hr = cpic->GetStatus(&docStatus);
if ( S_OK == hr && !(docStatus.dwStaticFlags & TF_SD_READONLY) )
{
ESDATA esData;
memset(&esData, 0, sizeof(ESDATA));
esData.pUnk = (IUnknown *)dim;
hr = m_psi->_RequestEditSession(ESCB_HANDLE_LEARNFROMDOC,TF_ES_READ, &esData, cpic);
}
}
dim->Release();
}
}
return hr;
}
//+---------------------------------------------------------------------------
//
// CSapiIMX::_HandleLearnFromDoc
//
// Let the speech engine learn from existing document content and have
// more accurate dictation recognition.
// This function will be called when user clicks the speech language bar menu
// and select Learn From Document... item.
//
//---------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_HandleLearnFromDoc(TfEditCookie ec,ITfContext *pic, ITfDocumentMgr *pDim )
{
HRESULT hr = S_OK;
ULONG cchBlock;
BOOL fFeedAlready;
// Get the Dictation Grammar
TraceMsg(TF_GENERAL, "_HandleLearnFromDoc() is called");
if ( m_psi == NULL)
return E_FAIL;
if ( !pDim || !pic )
return E_INVALIDARG;
_pic = pic;
m_psi->GetSpeechTask(&_pCSpTask, FALSE);
if ( _pCSpTask == NULL )
{
// _sptask is not yet initialized, just return.
TraceMsg(TF_GENERAL, "_HandleLearnFromDoc: _pCspTask is NULL");
goto CleanUp;
}
cchBlock = _GetDocBlockSize( );
if ( m_pwszDocBlock == NULL)
{
m_pwszDocBlock = (WCHAR *) cicMemAlloc( (cchBlock+1)*sizeof(WCHAR) );
if (m_pwszDocBlock == NULL)
{
TraceMsg(TF_GENERAL, "_HandleLearnFromDoc: m_pwszDocBlock Out of Memory");
hr = E_OUTOFMEMORY;
goto CleanUp;
}
}
fFeedAlready = FALSE;
hr = _IsDimAlreadyFed(pDim, &fFeedAlready);
if ( (hr != S_OK) || fFeedAlready )
{
// This DIM has already been fed to SR Engine.
// or we got problem to get the feed state for this dim.
// stop here.
goto CleanUp;
}
// Get the range for the document.
_cpStartRange.Release( );
hr = _pic->GetStart(ec, &_cpStartRange);
if ((hr != S_OK) || (_cpStartRange == NULL))
{
TraceMsg(TF_GENERAL, "_HandleLearnFromDoc: _cpStartRange is NULL");
goto CleanUp;
}
// Change the sptask interersting setting
_UpdateRecoContextInterestSet(TRUE);
// This is the first range of the document. just small size of block.
hr = _GetNextRangeContent(ec, SIZE_FIRST_BLOCK);
// Feed to SR Engine.
if ( (hr == S_OK) && _fMoreContent)
{
hr = _FeedContentRangeToSR( );
if ( hr == S_OK)
{
_SetDimFeedState(pDim, TRUE);
}
else
_fMoreContent = FALSE;
}
CleanUp:
return hr;
}
//+---------------------------------------------------------------------------
//
// _GetNextRangeContent
//
// Get the Next Range (block) of document content.
//
// update the flag to indicate if there is effective content
//
// ec: EditCookie
// cchSizeRange: Required block size
//
// At the first time, we just feed a very small size of charcters
// to the Engine so that it will not interfere with current dictation
// handling of engine.
//
// After we get the ADAPTATION notification from Engine, we will feed
// normal size of block ( specified in registry ) to engine.
//
//---------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_GetNextRangeContent(TfEditCookie ec, ULONG cchSizeRange)
{
HRESULT hr = S_OK;
LONG lShift = 0;
TraceMsg(TF_GENERAL, "_GetNextRangeContent is Called");
if ( m_pwszDocBlock == NULL)
{
m_pwszDocBlock = (WCHAR *) cicMemAlloc( (_cchBlockSize+1)*sizeof(WCHAR));
if (m_pwszDocBlock == NULL)
{
TraceMsg(TF_GENERAL, "_GetNextRangeContent: m_pwszDocBlock Out of Memory");
hr = E_OUTOFMEMORY;
return hr;
}
}
// assume there is no more content.
_fMoreContent = FALSE;
// This is Cicero App's Document, we use Cicero interfaces to get the whole document.
// We already get the cpStartRange.
hr = _cpStartRange->GetText(ec, TF_TF_MOVESTART | TF_TF_IGNOREEND, m_pwszDocBlock, cchSizeRange, &_cchContent );
if ( hr!= S_OK ) goto CleanUp;
// If the last character is not a word-break char for English case,
// we don't want to keep this half word for this chunk text, it will go to next
// chunk.
// we just want to shift back some charaters to hit a word-breaker.
WORD prilangid;
prilangid = PRIMARYLANGID(m_psi->GetLangID( ));
if ( prilangid != LANG_JAPANESE && prilangid != LANG_CHINESE )
{
if ( _cchContent > 0 )
{
while ( _cchContent > 0 )
{
if ( iswalpha(m_pwszDocBlock[_cchContent-1]) )
{
m_pwszDocBlock[_cchContent-1] = L'\0';
lShift ++;
_cchContent--;
}
else
break;
}
if ( lShift > 0 )
{
lShift *= ( -1 );
long cch;
_cpStartRange->ShiftEnd(ec, lShift, &cch, NULL);
}
}
}
TraceMsg(TF_GENERAL, "Text Content in Cicero Doc Buffer _cchContent=%d---------------->", _cchContent);
#ifdef DEBUG
{
ULONG x;
for (x=0; x<_cchContent; x++)
{
char szbuf[4];
szbuf[0] = (char)(m_pwszDocBlock[x]);
szbuf[1] = '\0';
OutputDebugString(szbuf);
}
OutputDebugString("\n");
}
#endif
TraceMsg(TF_GENERAL, "Text Content in CiceroDoc Over -------------------!");
if ( _cchContent > 0 || lShift > 0)
// There is more content.
_fMoreContent = TRUE;
else
_fMoreContent = FALSE;
CleanUp:
return hr;
}
//+---------------------------------------------------------------------------
//
// _FeedContentRangeToSR
//
// Feed the current Range (block) of document content to the SR Engine.
//
//---------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_FeedContentRangeToSR( )
{
HRESULT hr = S_OK;
WCHAR *pCoMemText;
CComPtr<ISpRecoContext> cpRecoCtxt;
TraceMsg(TF_GENERAL, "_FeedContentRangeToSR( ) is called");
hr = _pCSpTask->GetSAPIInterface(IID_ISpRecoContext, (void **)&cpRecoCtxt );
if ( (hr != S_OK) || (cpRecoCtxt == NULL) )
return E_FAIL;
// Feed this block of document content to speech dictation.
if ( (_cchContent > 0) && (m_pwszDocBlock != NULL))
{
pCoMemText = (WCHAR *)CoTaskMemAlloc((_cchContent+1)*sizeof(WCHAR));
if ( pCoMemText )
{
wcsncpy(pCoMemText, m_pwszDocBlock, _cchContent);
pCoMemText[_cchContent] = L'\0';
hr = cpRecoCtxt->SetAdaptationData(pCoMemText, _cchContent);
if ( hr != S_OK)
TraceMsg(TF_GENERAL, "_FeedContentRangeToSR: SetAdaptationData Failed");
}
}
return hr;
}
HRESULT CLearnFromDoc::_GetNextRangeEditSession( )
{
HRESULT hr = S_OK;
ESDATA esData;
Assert(m_psi);
memset(&esData, 0, sizeof(ESDATA));
esData.lData1 = (LONG_PTR)_cchBlockSize;
hr = m_psi->_RequestEditSession(ESCB_LEARNDOC_NEXTRANGE, TF_ES_READ, &esData, _pic);
return hr;
}
//+---------------------------------------------------------------------------
//
// _HandleNextRange
//
// Handle the Next Range (block) of document content, get and feed it and
// then update the status.
//
// ec: EditCookie
// cchSizeRange: Required block size
//
//---------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_HandleNextRange(TfEditCookie ec, ULONG cchSizeRange)
{
HRESULT hr = S_OK;
hr = _GetNextRangeContent(ec, cchSizeRange);
if ( hr == S_OK && _fMoreContent)
{
// This next range contains valid content, feed it to SR Engine.
hr = _FeedContentRangeToSR( );
}
if ( (hr != S_OK) || !_fMoreContent )
{
// Error happened or no more content, update the interest set.
hr = _UpdateRecoContextInterestSet(FALSE);
}
return hr;
}
//+-----------------------------------------------------------------------------------------
//
// _UpdateRecoContextInterestSet
//
// Update the RecoContext's interested notification event setting
//
// if fLearnFromDoc is TRUE, we are interested in getting notification of SPEI_ADAPTATION
// if fLearnFromDoc is FALSE, we are not interested in that notification
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_UpdateRecoContextInterestSet( BOOL fLearnFromDoc )
{
HRESULT hr = S_OK;
CComPtr<ISpRecoContext> cpRecoCtxt;
ULONGLONG ulInterest = SPFEI(SPEI_SOUND_START) |
SPFEI(SPEI_SOUND_END) |
SPFEI(SPEI_PHRASE_START) |
SPFEI(SPEI_RECOGNITION) |
SPFEI(SPEI_RECO_OTHER_CONTEXT) |
SPFEI(SPEI_HYPOTHESIS) |
SPFEI(SPEI_INTERFERENCE) |
SPFEI(SPEI_FALSE_RECOGNITION);
if ( _pCSpTask == NULL )
return hr;
hr = _pCSpTask->GetSAPIInterface(IID_ISpRecoContext, (void **)&cpRecoCtxt );
if ( (hr != S_OK) || (cpRecoCtxt == NULL) )
return E_FAIL;
if ( fLearnFromDoc )
ulInterest |= SPFEI(SPEI_ADAPTATION);
else
ulInterest &= ~(SPFEI(SPEI_ADAPTATION));
hr = cpRecoCtxt->SetInterest(ulInterest, ulInterest);
return hr;
}
//+-----------------------------------------------------------------------------------------
//
// _AddDimToList
//
// Add a DIM to the dim list, and set the feed state
//
// This function would be called by TIM_CODE_INITDIM callback.
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_AddDimToList(ITfDocumentMgr *pDim, BOOL fFed )
{
HRESULT hr = S_OK;
int nCnt = _rgDim.Count();
int i;
BOOL bFound;
DIMREF *dimRef;
TraceMsg(TF_GENERAL, "_AddDimToList is called");
if ( !pDim )
return E_INVALIDARG;
// Check to see if this dim is already added.
bFound = FALSE;
for (i=0; i < nCnt; i++)
{
dimRef = (DIMREF *)_rgDim.Get(i);
if ( dimRef->pDim == pDim )
{
bFound = TRUE;
TraceMsg(TF_GENERAL, "This dim has already been added to the dim list");
break;
}
}
if (bFound)
{
// Set the state.
dimRef->_fFeed = fFed;
}
else
{
dimRef = (DIMREF *)cicMemAllocClear(sizeof(DIMREF));
if ( dimRef == NULL)
return E_OUTOFMEMORY;
dimRef->_fFeed = fFed;
dimRef->pDim = pDim;
if (!_rgDim.Insert(nCnt, 1))
{
cicMemFree(dimRef);
return E_OUTOFMEMORY;
}
pDim->AddRef( );
_rgDim.Set(nCnt, dimRef);
}
return hr;
}
//+-----------------------------------------------------------------------------------------
//
// _RemoveDimFromList
//
// Remove a DIM from the internal dim list, and release the DIM itself.
//
// This function would be called by TIM_CODE_UNINITDIM callback.
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_RemoveDimFromList(ITfDocumentMgr *pDim)
{
HRESULT hr = S_OK;
int nCnt = _rgDim.Count();
int i;
DIMREF *dimRef;
TraceMsg(TF_GENERAL, "_RemoveDimFromList is called");
if ( pDim == NULL)
return E_INVALIDARG;
// Check to see if this dim is already added.
for (i=0; i < nCnt; i++)
{
dimRef = (DIMREF *)_rgDim.Get(i);
if ( dimRef->pDim == pDim )
{
// free the DIM.
(dimRef->pDim)->Release( );
dimRef->pDim = NULL;
// Remove it from the list
_rgDim.Remove(i, 1);
// Remove the structure itself.
cicMemFree(dimRef);
break;
}
}
return hr;
}
//+-----------------------------------------------------------------------------------------
//
// _SetDimFeedState
//
// Set the feed state for the specified DIM.
//
// fFed is TRUE means this DIM is already fed to the Engine.
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_SetDimFeedState(ITfDocumentMgr *pDim, BOOL fFed )
{
HRESULT hr = S_OK;
int nCnt = _rgDim.Count();
int i;
DIMREF *dimRef;
TraceMsg(TF_GENERAL, "_SetDimFeedState is called");
if ( pDim == NULL)
return E_INVALIDARG;
// Check to see if this dim is already added.
for (i=0; i < nCnt; i++)
{
dimRef = (DIMREF *)_rgDim.Get(i);
if ( dimRef->pDim == pDim )
{
// Set the feed state for this DIM.
dimRef->_fFeed = fFed;
break;
}
}
return hr;
}
//+-----------------------------------------------------------------------------------------
//
// _IsDimAlreadyFed
//
// Check to see if the dim is already fed to the Engine.
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_IsDimAlreadyFed(ITfDocumentMgr *pDim, BOOL *fFeed)
{
HRESULT hr = S_OK;
int nCnt = _rgDim.Count();
int i;
DIMREF *dimRef;
TraceMsg(TF_GENERAL, "_IsDimAlreadyFed is called");
if ( (pDim == NULL) || (fFeed == NULL))
return E_INVALIDARG;
*fFeed = FALSE;
// Check to see if this dim is already added.
for (i=0; i < nCnt; i++)
{
dimRef = (DIMREF *)_rgDim.Get(i);
if ( dimRef->pDim == pDim )
{
// Get the feed state for this DIM.
*fFeed = dimRef->_fFeed;
TraceMsg(TF_GENERAL, "IsDimAlreadyFed: pDim=%x, fFeed=%s", (UINT_PTR)pDim, *fFeed ? "TRUE":"FALSE");
break;
}
}
return hr;
}
// CleanUpConsider: above _IsDimAlreadyFed and _SetDimFeedState have similar code, we may supply a new internal base function, and let
// above two functions call it with different param set.
//+-----------------------------------------------------------------------------------------
//
// _ClearDimList
//
// Release all the DIMs in the DIM List, and clear the list itself.
//
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_ClearDimList( )
{
HRESULT hr = S_OK;
int nCnt = _rgDim.Count();
int i;
DIMREF *dimRef;
TraceMsg(TF_GENERAL, "_ClearDimList is called");
for (i=0; i < nCnt; i++)
{
dimRef = (DIMREF *)_rgDim.Get(i);
// free the DIM.
if ( dimRef->pDim)
{
(dimRef->pDim)->Release( );
dimRef->pDim = NULL;
}
// Remove it from the list
_rgDim.Remove(i, 1);
// Remove the structure itself.
cicMemFree(dimRef);
}
return hr;
}
//+-----------------------------------------------------------------------------------------
//
// _ResetDimListFeedState
//
// set feed state for all the dims in internal dim list as FALSE
//
// This function would be called when user turns off the Learn from Doc.
//
//-------------------------------------------------------------------------------------------+
HRESULT CLearnFromDoc::_ResetDimListFeedState( )
{
HRESULT hr = S_OK;
int nCnt = _rgDim.Count();
int i;
DIMREF *dimRef;
TraceMsg(TF_GENERAL, "_ResetDimListFeedState is called");
for (i=0; i < nCnt; i++)
{
dimRef = (DIMREF *)_rgDim.Get(i);
// Set the feed state for this DIM as FALSE
dimRef->_fFeed = FALSE;
}
return hr;
}
// CleanUpConsider: above _ClearDimList and _ResetDimListFeedState have similar code, we may supply a new internal base function, and let
// above two functions call it with different param set.