2025-11-28 00:35:46 +09:00

815 lines
28 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright © Microsoft Corporation. All rights reserved
/******************************************************************************
* resultcontainer.cpp
* This module contains the definition of CResultContainer.
* CResultContainer makes all of the recognition-object-
* specific SAPI5 calls
******************************************************************************/
#include "stdafx.h"
#include "resultcontainer.h"
/**********************************************************************
* CResultContainer::CResultContainer *
*------------------------------------*
* Description:
* Constructor for the CResultContainer class.
* A CResultContainer is created by the first CDictationRun
* to be created for a given result object.
**********************************************************************/
CResultContainer::CResultContainer( ISpRecoResult &rResult,
CDictationRun &rFirstOwner,
CPhraseReplacement &rPhraseReplacement ) :
m_cpRecoResult( &rResult ),
m_pPhraseReplacement( &rPhraseReplacement ),
m_pOwnerListHead( NULL ),
m_cLastRequestedAltsReturned( 0 ),
m_ulStartOfLastRequestedAlts( 0 ),
m_cElementsInLastRequestedAlts( 0 )
{
// The owner should go onto the list
m_pOwnerListHead = new OWNERNODE;
if ( m_pOwnerListHead )
{
m_pOwnerListHead->pOwner = &rFirstOwner;
m_pOwnerListHead->pNext = NULL;
}
// If the allocation failed, all calls that need it
// will return E_OUTOFMEMORY
// Zero out the requested phrase alternates array
memset( (void *) m_apLastRequestedPhraseAlts, 0,
ALT_REQUEST_COUNT * sizeof( ISpPhraseAlt *) );
} /* CResultContainer::CResultContainer */
/**********************************************************************
* CResultContainer::~CResultContainer *
*-------------------------------------*
* Description:
* Destructor for the CResultContainer class.
* This object gets destroyed when there are no longer
* any CDictationRuns referencing it.
**********************************************************************/
CResultContainer::~CResultContainer()
{
_ASSERTE( !m_pOwnerListHead );
if ( m_pPhraseReplacement )
{
delete m_pPhraseReplacement;
}
// Release any ISpPhraseAlt's that are still around from the
// last call to ISpPhrase::GetAlternates()
for (int i = 0; i < ALT_REQUEST_COUNT; i++)
{
if (m_apLastRequestedPhraseAlts[i])
{
m_apLastRequestedPhraseAlts[i]->Release();
}
}
} /* CResultContainer::~CResultContainer */
/**********************************************************************
* CResultContainer::AddOwner *
*----------------------------*
* Description:
* Called whenever a new CDictationRun is created to use this
* same RecoResult. Adds that CDictationRun to the front of
* the list of owners.
* Return:
* S_OK
* E_OUTOFMEMORY
**********************************************************************/
HRESULT CResultContainer::AddOwner( CDictationRun &rNewOwner )
{
if ( !m_pOwnerListHead )
{
_ASSERTE( false );
return E_UNEXPECTED;
}
OWNERNODE *pNode = new OWNERNODE;
if ( pNode )
{
pNode->pNext = m_pOwnerListHead;
pNode->pOwner = &rNewOwner;
m_pOwnerListHead = pNode;
return S_OK;
}
else
{
return E_OUTOFMEMORY;
}
} /* CResultContainer::AddOwner */
/**********************************************************************
* CResultContainer::DeleteOwner *
*-------------------------------*
* Description:
* Called whenever a DictationRun using this RecoResult
* has been deleted. Removes the CDictationRun from the
* list and deletes itself if that was the last owner
**********************************************************************/
void CResultContainer::DeleteOwner( CDictationRun &rOldOwner )
{
_ASSERTE( m_pOwnerListHead );
if ( !m_pOwnerListHead )
{
return;
}
OWNERNODE **ppNode;
for ( ppNode = &m_pOwnerListHead;
(*ppNode) && ((*ppNode)->pOwner != &rOldOwner);
ppNode = &((*ppNode)->pNext) )
;
if ( *ppNode )
{
// Found: Delete it
OWNERNODE *pNodeToDelete = *ppNode;
*ppNode = (*ppNode)->pNext;
delete pNodeToDelete;
}
else
{
// Should be on the list!
_ASSERTE( false );
}
if ( !m_pOwnerListHead )
{
// There are no more CDictationRuns referencing this
delete this;
}
} /* CResultContainer::DeleteOwner */
/**********************************************************************
* CResultContainer::SpeakAudio *
*------------------------------*
* Description:
* Calls ISpRecoResult::SpeakAudio() on the recoresult.
* Converts the arguments to pre-replacement elements
* Return:
* S_OK
* S_FALSE if no elements are to be spoken.
* Return value of CPhraseReplacement conversion routines
* Return value of ISpRecoResult::SpeakAudio()
**********************************************************************/
HRESULT CResultContainer::SpeakAudio( ULONG ulStartElement,
ULONG cElements )
{
if (!cElements)
{
return S_FALSE;
}
// Convert the start and end to phrase element indices
// as seen from the result object's point of view
ULONG ulPreReplStart;
ULONG ulPreReplEnd;
ULONG cPreReplElts;
HRESULT hr = m_pPhraseReplacement->PostToPreReplacementIndex(
ulStartElement, &ulPreReplStart );
if (SUCCEEDED(hr))
{
hr = m_pPhraseReplacement->PostToPreReplacementIndex(
ulStartElement + cElements, &ulPreReplEnd );
}
// Call ISpRecoResult::SpeakAudio()
if (SUCCEEDED(hr))
{
cPreReplElts = ulPreReplEnd - ulPreReplStart;
hr = m_cpRecoResult->SpeakAudio(
ulPreReplStart, cPreReplElts, SPF_ASYNC, NULL );
}
return hr;
} /* CResultContainer::SpeakAudio */
/**********************************************************************
* CResultContainer::GetAlternatesText *
*-------------------------------------*
* Description:
* Called to get the text of the alternates for
* a specific range of elements in this result.
* ppszCoMemText is expected to point to a buffer
* of size at least sizeof( WCHAR * ) * ulRequestCount,
* and the text in it will be CoTaskMemAlloced.
* Return:
* S_OK
* S_FALSE if there are no alternates
* E_POINTER
* return value of CResultContainer::GetAlternates()
* return value of CResultContainer::GetAltText()
**********************************************************************/
HRESULT CResultContainer::GetAlternatesText( ULONG ulStartElement,
ULONG cElements,
ULONG ulRequestCount,
__out_ecount_part(ulRequestCount, *pcPhrasesReturned) WCHAR **ppszCoMemText,
__out ULONG *pcPhrasesReturned )
{
if ( !ppszCoMemText || !pcPhrasesReturned )
{
return E_POINTER;
}
// Get as many alternates as we can
ULONG cAvailableAlts;
HRESULT hr = GetAlternates( ulStartElement, cElements, ulRequestCount,
&cAvailableAlts );
if ( FAILED( hr ) )
{
return hr;
}
// Sanity check: We didn't get more than we asked for
_ASSERTE( cAvailableAlts <= ulRequestCount );
if ( cAvailableAlts > ulRequestCount )
{
return E_UNEXPECTED;
}
if ( !cAvailableAlts )
{
// No alternates
*pcPhrasesReturned = 0;
*ppszCoMemText = NULL;
return S_FALSE;
}
// Null out the pointers to text buffers first
memset(ppszCoMemText, 0, ulRequestCount * sizeof(WCHAR *));
// Get the text
for ( ULONG ulAlt = 0;
SUCCEEDED(hr) && (ulAlt < cAvailableAlts);
ulAlt++ )
{
// ppszCoMemText[ulAlt] is the ulAlt'th (WCHAR *)
// in the array.
// Its address will give us a WCHAR ** which will
// point to a buffer that is to be CoTaskMemAlloced
// by GetAltText()
hr = GetAltText( ulStartElement, cElements, ulAlt,
&(ppszCoMemText[ulAlt]), NULL );
}
*pcPhrasesReturned = cAvailableAlts;
return hr;
} /* CResultContainer::GetAlternatesText */
/**********************************************************************
* CResultContainer::GetAlternates *
*---------------------------------*
* Description:
* Gets the ISpPhraseAlts for this range of elements
* (indices are given taking replacement into account).
* The phrase alternates are pointed to by
* m_apLastRequestedPhraseAlts.
* It is very likely that GetAlternates() would be called
* numerous times consecutively for the same set of
* alternates. Thus, these are cached between calls.
* Return:
* S_OK
* E_INVALIDARG: Out-of-bounds elements
* Return value of CPhraseReplacement conversion routines
* Return value of ISpRecoResult::GetAlternates
**********************************************************************/
HRESULT CResultContainer::GetAlternates( ULONG ulStartElement,
ULONG cElements,
ULONG ulRequestCount,
__out_opt ULONG *pcPhrasesReturned )
{
// First check to see if we have these alternates cached.
// We check that there is at least one alternate cached,
// that the alternates are for the same set of phrase elements,
// and that there are enough.
if ( m_apLastRequestedPhraseAlts[0] &&
( m_ulStartOfLastRequestedAlts == ulStartElement ) &&
( m_cElementsInLastRequestedAlts == cElements ) &&
( m_cLastRequestedAltsReturned >= ulRequestCount) )
{
if ( pcPhrasesReturned )
{
// We already have these alternates, so don't bother
// getting them again
*pcPhrasesReturned = __min(
ulRequestCount, m_cLastRequestedAltsReturned );
}
return S_OK;
}
if ( (ulStartElement + cElements) >
m_pPhraseReplacement->GetNumReplacementElements() )
{
// Out of bounds
return E_INVALIDARG;
}
// Convert the start and end to phrase element indices
// as seen from the result object's point of view
ULONG ulPreReplStart;
ULONG ulPreReplLast;
HRESULT hr = m_pPhraseReplacement->PostToPreReplacementIndex(
ulStartElement, &ulPreReplStart );
if ( SUCCEEDED(hr) )
{
hr = m_pPhraseReplacement->PostToPreReplacementIndex(
ulStartElement + cElements, &ulPreReplLast );
}
ULONG cPreReplElements;
if ( SUCCEEDED(hr) )
{
cPreReplElements = ulPreReplLast - ulPreReplStart;
}
// Release any ISpPhraseAlts that are around from last time
for (int i = 0; i < ALT_REQUEST_COUNT; i++)
{
if ( m_apLastRequestedPhraseAlts[i] )
{
m_apLastRequestedPhraseAlts[i]->Release();
m_apLastRequestedPhraseAlts[i] = 0;
}
}
// Get as many alternates as we can by calling
// ISpRecoResult::GetAlternates()
ULONG cPhrasesReturned;
if ( SUCCEEDED(hr) )
{
hr = m_cpRecoResult->GetAlternates( ulPreReplStart,
cPreReplElements, ulRequestCount, m_apLastRequestedPhraseAlts,
&cPhrasesReturned );
}
if ( SUCCEEDED(hr) && pcPhrasesReturned )
{
*pcPhrasesReturned = cPhrasesReturned;
}
// Cache information about this alternate request
m_ulStartOfLastRequestedAlts = ulStartElement;
m_cElementsInLastRequestedAlts = cElements;
if ( SUCCEEDED(hr) )
{
m_cLastRequestedAltsReturned = cPhrasesReturned;
}
return hr;
} /* CResultContainer::GetAlternates */
/**********************************************************************
* CResultContainer::GetAltInfo *
*------------------------------*
* Description:
* Given the alternates index, gets the info for the
* alternate (i.e. which elements it replaces in the
* parent and how many elements it has).
* Returns element indices TAKING REPLACEMENTS INTO
* ACCOUNT, unless the fReturnPostReplIndices flag has
* been set to false (it is set to true by default).
*
* If the end of the alternate falls in the middle of
* a replacement, then the result indices are expanded
* to include the entire replacement.
* Return:
* S_OK
* E_FAIL if we couldn't get ulAlternateIndex alternates
* Return value of CResultContainer::GetAlternates()
* Return value of ISpPhraseAlt::GetAltInfo()
* Return value of
* CResultContainer::ExpandToIncludeWholeReplacements()
* Return value of CPhraseReplacement::Initialize()
* Return value of CPhraseReplacement conversion routines
**********************************************************************/
HRESULT CResultContainer::GetAltInfo( ULONG ulStartElement,
ULONG cElements,
ULONG ulAlternateIndex,
ULONG *pulStartInParent,
ULONG *pcEltsInParent,
ULONG *pcEltsInAlt,
bool fReturnPostReplIndices)
{
if ( ulAlternateIndex >= ALT_REQUEST_COUNT )
{
// Out of bounds
return E_INVALIDARG;
}
// First make sure we actually have this alternate
ULONG cPhrasesReturned;
HRESULT hr = GetAlternates(
ulStartElement, cElements, ulAlternateIndex + 1, &cPhrasesReturned );
if ( FAILED( hr ) )
{
return hr;
}
if ( cPhrasesReturned < (ulAlternateIndex + 1) )
{
// There aren't enough alternates to accommodate
// this one
return E_FAIL;
}
// Sanity check: The pointer to the ISpPhraseAlt for this
// alternate is non-NULL
_ASSERTE( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] );
if ( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] == NULL )
{
return E_UNEXPECTED;
}
// Call to ISpPhraseAlt::GetAltInfo()
ULONG ulStartInParent;
ULONG cEltsInParent;
ULONG cEltsInAlt;
hr = (m_apLastRequestedPhraseAlts[ ulAlternateIndex ])->GetAltInfo(
NULL, &ulStartInParent, &cEltsInParent, &cEltsInAlt );
#ifdef _DEBUG
// Debug code for seeing the text of the entire alternate
// (which covers the entire parent phrase) and for seeing
// the text for just the part of the alternate that
// covers the elements in the parent that we are interested in
WCHAR * pwszWhole = 0;
WCHAR * pwszAlt = 0;
BYTE b;
m_apLastRequestedPhraseAlts[ ulAlternateIndex ]->GetText(
SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, true, &pwszWhole, &b );
m_apLastRequestedPhraseAlts[ ulAlternateIndex ]->GetText(
ulStartInParent, cEltsInAlt, true, &pwszAlt, &b );
::CoTaskMemFree( pwszWhole );
::CoTaskMemFree( pwszAlt );
#endif
// If there is replaced (ITNed) text anywhere in this set of
// elements in the parent, we should be getting alternate
// text for the entire replacement.
// That is, if a replacement covers the word "thirty" in the
// phrase "I have thirty nine dollars", we should also be
// showing alternate text for the elements "nine dollars",
// since it appears to all be one element ("$39.00") to the
// user
if ( SUCCEEDED( hr ) )
{
const ULONG cEltsInParentBeforeExpansion = cEltsInParent;
// Expand the endpoints to include entire replacements
hr = m_pPhraseReplacement->ExpandToIncludeWholeReplacements(
&ulStartInParent, &cEltsInParent );
_ASSERTE( SUCCEEDED( hr ) );
// Adjust the number of elements in the alternate accordingly
// (all of the extra elements we have just included are the
// same in the alternate as they are in the parent, so it
// will be the same number of extra elements in the alternate
cEltsInAlt += cEltsInParent - cEltsInParentBeforeExpansion;
}
if ( !fReturnPostReplIndices )
{
// We're done; no need to convert, since the caller wants
// output in phrase element indices from the result object's
// point of view
if ( pulStartInParent )
{
*pulStartInParent = ulStartInParent;
}
if ( pcEltsInParent )
{
*pcEltsInParent = cEltsInParent;
}
if ( pcEltsInAlt )
{
*pcEltsInAlt = cEltsInAlt;
}
return hr;
}
// Convert these element indices to post-replacement indices
if ( SUCCEEDED( hr ) )
{
// First convert the start element
ULONG ulPostReplStartInParent;
hr = m_pPhraseReplacement->PreToPostReplacementIndex(
ulStartInParent, &ulPostReplStartInParent );
if (FAILED(hr))
{
return hr;
}
if ( pulStartInParent )
{
*pulStartInParent = ulPostReplStartInParent;
}
// Convert the end element to determine how many post-replacement
// elements this alternate replaces
if ( pcEltsInParent )
{
ULONG ulEndInParent = ulStartInParent + cEltsInParent;
ULONG ulEndPostReplElement;
hr = m_pPhraseReplacement->PreToPostReplacementIndex(
ulEndInParent, &ulEndPostReplElement );
if (FAILED(hr))
{
return hr;
}
*pcEltsInParent = ulEndPostReplElement - ulPostReplStartInParent;
}
}
// Find the number of elements in the alternate, if that was requested
if ( SUCCEEDED(hr) && pcEltsInAlt )
{
// Since this ISpPhraseAlt is a completely different ISpPhrase
// from the one associated with m_cpRecoResult, we have to
// construct a new CPhraseReplacement object to deal specifically
// with this phrase.
CPhraseReplacement newPhraseRepl;
hr = newPhraseRepl.Initialize(*(m_apLastRequestedPhraseAlts[ ulAlternateIndex ]));
// First convert the start element
ULONG ulPostReplStartInAlt;
if ( SUCCEEDED( hr ) )
{
hr = newPhraseRepl.PreToPostReplacementIndex(
ulStartInParent, &ulPostReplStartInAlt );
}
// Convert the end element to determine how many post-replacement
// elements this alternate replaces
ULONG ulEndInAlt = ulStartInParent + cEltsInAlt;
ULONG ulEndPostReplElement;
if ( SUCCEEDED(hr) )
{
hr = newPhraseRepl.PreToPostReplacementIndex(
ulEndInAlt, &ulEndPostReplElement );
}
if ( SUCCEEDED(hr) )
{
*pcEltsInAlt = ulEndPostReplElement - ulPostReplStartInAlt;
}
}
return hr;
} /* CResultContainer::GetAltInfo */
/**********************************************************************
* CResultContainer::GetAltText *
*------------------------------*
* Description:
* Given the alternates index, gets the text for the
* alternate and display attributes.
* Return:
* S_OK
* Return value of CResultContainer::GetAlternates()
* Return value of CResultContainer::GetAltInfo()
* Return value of ISpPhraseAlt::GetText()
**********************************************************************/
HRESULT CResultContainer::GetAltText( ULONG ulStartElement,
ULONG cElements,
ULONG ulAlternateIndex,
__deref_out WCHAR **ppszCoMemText,
__out_opt BYTE *pbDisplayAttributes )
{
if ( ulAlternateIndex >= ALT_REQUEST_COUNT )
{
// Out of bounds
return E_INVALIDARG;
}
// First make sure we actually have this alternate
ULONG cPhrasesReturned;
HRESULT hr = GetAlternates(
ulStartElement, cElements, ulAlternateIndex + 1, &cPhrasesReturned );
if ( FAILED( hr ) )
{
return hr;
}
if ( cPhrasesReturned < (ulAlternateIndex + 1) )
{
// There aren't enough alternates to accommodate
// this one
return E_FAIL;
}
// Sanity check: The pointer to the ISpPhraseAlt for this
// alternate is non-NULL
_ASSERTE( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] );
if ( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] == NULL )
{
return E_UNEXPECTED;
}
// Find out what elements in the parent this alternate replaces
// Note that we do NOT want text replacements taken into effect,
// since we are calling ISpPhrase::GetText() directly with
// these phrase element indices, so we need those indices
// from the result object's point of view
ULONG ulStartInParent;
ULONG cEltsInParent;
ULONG cEltsInAlt;
hr = GetAltInfo( ulStartElement, cElements, ulAlternateIndex,
&ulStartInParent, &cEltsInParent, &cEltsInAlt, false );
if ( FAILED(hr) )
{
return hr;
}
// Call to ISpPhraseAlt::GetText()
return m_apLastRequestedPhraseAlts[ ulAlternateIndex ]->GetText(
ulStartInParent, cEltsInAlt, true, ppszCoMemText, pbDisplayAttributes );
} /* CResultContainer::GetAltText */
/**********************************************************************
* CResultContainer::ChooseAlternate *
*-----------------------------------*
* Description:
* Called when the caller wishes to commit one of the alternates.
* If pbDisplayAttributes is non-NULL, hands back the display
* attributes of the alternate text going in.
* Adjusts the phrase element mapping in CPhraseReplacement
* to the new result object.
* Return:
* S_OK
* Return value of CResultContainer::GetAlternates()
* Return value of CResultContainer::GetAltText()
* Return value of CResultContainer::NotifyOwnersOfCommit()
* Return value of ISpPhraseAlt::Commit()
* Return value of CPhraseReplacement::Initialize()
**********************************************************************/
HRESULT CResultContainer::ChooseAlternate( ULONG ulStartElement,
ULONG cElements,
ULONG ulAlternateIndex,
BYTE *pbDisplayAttributes )
{
if ( ulAlternateIndex >= ALT_REQUEST_COUNT )
{
// Out of bounds
return E_INVALIDARG;
}
// First make sure we actually have this alternate
ULONG cPhrasesReturned;
HRESULT hr = GetAlternates(
ulStartElement, cElements, ulAlternateIndex + 1, &cPhrasesReturned );
if ( FAILED( hr ) )
{
return hr;
}
if ( cPhrasesReturned < (ulAlternateIndex + 1) )
{
// There aren't enough alternates to accommodate
// this one
return E_FAIL;
}
// Sanity check: The pointer to the ISpPhraseAlt for this
// alternate is non-NULL
_ASSERTE( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] );
if ( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] == NULL )
{
return E_UNEXPECTED;
}
// If there is interest in the display attributes for the alternate,
// get them
if ( pbDisplayAttributes )
{
WCHAR *pszCoMemText;
hr = GetAltText( ulStartElement, cElements, ulAlternateIndex,
&pszCoMemText, pbDisplayAttributes );
if ( FAILED( hr ) )
{
return hr;
}
::CoTaskMemFree( pszCoMemText );
}
// Notify owners of the alternate committal so that they can adjust
// their element offset maps
hr = NotifyOwnersOfCommit( ulStartElement, cElements, ulAlternateIndex );
if ( FAILED( hr ) )
{
return hr;
}
// Call to ISpPhraseAlt::Commit()
hr = m_apLastRequestedPhraseAlts[ulAlternateIndex]->Commit();
if ( FAILED( hr ) )
{
return hr;
}
// The alternate is now "dead"
// Re-initialize the phrase replacement info, as the element indices
// and text replacment (ITN) situations are likely to have changed
return m_pPhraseReplacement->Initialize( *m_cpRecoResult );
} /* CResultContainer::ChooseAlternate */
/**********************************************************************
* CResultContainer::NotifyOwnersOfCommit *
*----------------------------------------*
* Description:
* Called when an alternate is about to be committed BEFORE
* the commit happens.
* Notifies all DictationRuns that hold onto this result
* that their phrase element offset maps will need to change
* Return:
* S_OK
* E_OUTOFMEMORY if the head of the owner list wasn't
* allocated at construction time
* Return value of CResultContainer::GetAlternates()
* Return value of CPhraseReplacement::Initialize()
* Return value of CResultContainer::GetAltInfo()
**********************************************************************/
HRESULT CResultContainer::NotifyOwnersOfCommit( ULONG ulStartElement,
ULONG cElements,
ULONG ulAlternateIndex )
//ISpPhraseAlt *pChosenAlternate )
{
if ( ulAlternateIndex >= ALT_REQUEST_COUNT )
{
// Out of bounds
return E_INVALIDARG;
}
if ( !m_pOwnerListHead )
{
return E_OUTOFMEMORY;
}
// First make sure we actually have this alternate
ULONG cPhrasesReturned;
HRESULT hr = GetAlternates(
ulStartElement, cElements, ulAlternateIndex + 1, &cPhrasesReturned );
if ( FAILED( hr ) )
{
return hr;
}
if ( cPhrasesReturned < (ulAlternateIndex + 1) )
{
// There aren't enough alternates to accommodate
// this one
return E_FAIL;
}
// Sanity check: The pointer to the ISpPhraseAlt for this
// alternate is non-NULL
_ASSERTE( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] );
if ( m_apLastRequestedPhraseAlts[ ulAlternateIndex ] == NULL )
{
return E_UNEXPECTED;
}
// We need to determine how many post-replacement elements the new
// phrase will have, but the alternate has not yet been committed to
// m_cpRecoResult, so we need to construct a different CPhraseReplacement
// to get that information
CPhraseReplacement newPhraseRepl;
hr = newPhraseRepl.Initialize( *(m_apLastRequestedPhraseAlts[ ulAlternateIndex ]) );
if ( FAILED( hr ) )
{
return hr;
}
ULONG cPostReplElementsAfterCommit = newPhraseRepl.GetNumReplacementElements();
// Find out which (post-replacement) elements in the parent
// this alternate will replace
ULONG ulStartInParent;
ULONG cEltsInParent;
hr = GetAltInfo( ulStartElement, cElements, ulAlternateIndex,
&ulStartInParent, &cEltsInParent );
if ( FAILED(hr) )
{
return hr;
}
// Loop through the owner list, and let each one adjust its maps
for ( OWNERNODE *pNode = m_pOwnerListHead; pNode; pNode = pNode->pNext )
{
hr = pNode->pOwner->OnAlternateCommit( ulStartInParent,
cEltsInParent, cPostReplElementsAfterCommit );
if ( FAILED( hr ) )
{
return hr;
}
}
return S_OK;
} /* CResultContainer::NotifyOwnersOfCommit */