725 lines
26 KiB
C++
725 lines
26 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
|
|
|
|
/******************************************************************************
|
|
* candidatelist.cpp
|
|
* Implementation details for the CCandidateList object which is a
|
|
* class that manages the recognized alternatives for dictated
|
|
* text.
|
|
******************************************************************************/
|
|
#include "stdafx.h"
|
|
#include "candidatelist.h"
|
|
#include "DictationPad.h"
|
|
|
|
// Multi-Language Header File
|
|
#include <mlang.h>
|
|
|
|
#define MAX_ALTS_DISPLAYED 10
|
|
|
|
/*****************************************************************************
|
|
* CCandidateList::CCandidateList *
|
|
*--------------------------------*
|
|
* Description:
|
|
* Constructor for the CCandidateList class.
|
|
* Modifies the RICHEDIT_CLASS of the main
|
|
* application window to use our callback function
|
|
* and registers that window class.
|
|
*******************************************************************************/
|
|
CCandidateList::CCandidateList( HWND hClient, CRecoEventMgr &rRecoMgr ) :
|
|
m_pwcParentClass( NULL ),
|
|
m_hMainClientWindow( hClient ),
|
|
m_hParent( NULL ),
|
|
m_hAltsList( NULL ),
|
|
// For now, set the langid to default.
|
|
// LangID can be set to something else
|
|
// later.
|
|
m_langid( ::GetUserDefaultLangID() ),
|
|
m_fMakeUIVisible( true ),
|
|
m_fPlaybackInProgress( false ),
|
|
m_pCurrentDictRun( NULL ),
|
|
m_cpTextSel( NULL ),
|
|
m_pRecoMgr( &rRecoMgr ),
|
|
m_hFont( NULL ),
|
|
m_ulNumAltsDisplayed( 0 )
|
|
{
|
|
m_hInst = (HINSTANCE)(LONG_PTR) ::GetWindowLongPtr( m_hMainClientWindow, GWLP_HINSTANCE );
|
|
|
|
// Register a window class that is like the rich edit control class in every way
|
|
// except that it has space for an extra pointer and calls our wndproc
|
|
WNDCLASS wcModifiedRichEdit;
|
|
::GetClassInfo( m_hInst, RICHEDIT_CLASS, &wcModifiedRichEdit );
|
|
|
|
// Bump the space by the size of a pointer
|
|
m_cbOffset = wcModifiedRichEdit.cbWndExtra;
|
|
int cbExtraSpace = sizeof(CCandidateList *);
|
|
wcModifiedRichEdit.cbWndExtra += cbExtraSpace;
|
|
|
|
// Change the wndproc, storing the original wndproc
|
|
m_wpOrigWndProc = wcModifiedRichEdit.lpfnWndProc;
|
|
wcModifiedRichEdit.lpfnWndProc = CandidateUIProc;
|
|
|
|
wcModifiedRichEdit.lpszClassName = MODIFIED_RICHEDIT_NAME;
|
|
ATOM atomRet = ::RegisterClass( &wcModifiedRichEdit );
|
|
|
|
if ( atomRet )
|
|
{
|
|
m_pwcParentClass = new WNDCLASS;
|
|
*m_pwcParentClass = wcModifiedRichEdit;
|
|
}
|
|
|
|
GetFontSettings();
|
|
} /* CCandidateList::CCandidateList */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::~CCandidateList *
|
|
*---------------------------------*
|
|
* Description:
|
|
* Destructor for the CCandidateListclass
|
|
*******************************************************************************/
|
|
CCandidateList::~CCandidateList()
|
|
{
|
|
if ( m_pwcParentClass )
|
|
{
|
|
delete m_pwcParentClass;
|
|
}
|
|
|
|
if (m_hFont)
|
|
{
|
|
DeleteObject(m_hFont);
|
|
}
|
|
|
|
::DestroyWindow( m_hButton );
|
|
DoneWithAltsList();
|
|
} /* CCandidateList::~CCandidateList */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::GetFontSettings *
|
|
*---------------------------------*
|
|
* Description:
|
|
* Gets the font settings for the candidate list UI
|
|
*******************************************************************************/
|
|
void CCandidateList::GetFontSettings()
|
|
{
|
|
int iHeight = 0; // Will cause CreateFont() to use default in case we
|
|
// don't get the height below
|
|
HDC hdc = GetDC(m_hMainClientWindow);
|
|
HFONT hfontNew = 0;
|
|
|
|
// Get the height of the text
|
|
if (hdc)
|
|
{
|
|
TEXTMETRIC tm;
|
|
|
|
if (GetTextMetrics(hdc, &tm))
|
|
{
|
|
iHeight = tm.tmHeight;
|
|
}
|
|
|
|
ReleaseDC(m_hMainClientWindow, hdc);
|
|
}
|
|
|
|
// Pick an appropriate font. On Windows 2000, let the system fontlink.
|
|
if (NT5orGreater())
|
|
{
|
|
hfontNew = CreateFont(iHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
|
DEFAULT_CHARSET,
|
|
OUT_DEFAULT_PRECIS,
|
|
CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY,
|
|
DEFAULT_PITCH,
|
|
TEXT("Microsoft Sans Serif"));
|
|
}
|
|
else
|
|
{
|
|
LCID lcid = MAKELCID( m_langid, SORT_DEFAULT );
|
|
UINT uiCodePage = SpCodePageFromLcid( lcid );
|
|
|
|
CComPtr<IMultiLanguage> cpMultiLanguage;
|
|
MIMECPINFO MimeCpInfo;
|
|
|
|
if ( SUCCEEDED(cpMultiLanguage.CoCreateInstance(CLSID_CMultiLanguage))
|
|
&& SUCCEEDED(cpMultiLanguage->GetCodePageInfo(uiCodePage, &MimeCpInfo)))
|
|
{
|
|
hfontNew = CreateFont(iHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
|
MimeCpInfo.bGDICharset,
|
|
OUT_DEFAULT_PRECIS,
|
|
CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY,
|
|
DEFAULT_PITCH,
|
|
CW2T(MimeCpInfo.wszProportionalFont));
|
|
|
|
}
|
|
}
|
|
|
|
if (hfontNew)
|
|
{
|
|
if (m_hFont)
|
|
{
|
|
DeleteObject(m_hFont);
|
|
}
|
|
|
|
m_hFont = hfontNew;
|
|
}
|
|
} /* CCandidateList::GetFontSettings */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::SetParent *
|
|
*---------------------------*
|
|
* Description:
|
|
* Called after the parent window is created.
|
|
* Sets the parent window member and creates a button for the
|
|
* alternates UI
|
|
*******************************************************************************/
|
|
void CCandidateList::SetParent( HWND hParent )
|
|
{
|
|
m_hParent = hParent;
|
|
m_hButton = ::CreateWindow( _T("BUTTON"),
|
|
_T(""),
|
|
WS_CHILD | BS_DEFPUSHBUTTON,
|
|
0,
|
|
0,
|
|
BUTTON_WIDTH,
|
|
BUTTON_HEIGHT,
|
|
m_hParent,
|
|
NULL,
|
|
m_hInst,
|
|
NULL );
|
|
} /* CCandidateList::SetParent */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::SetLangID *
|
|
*---------------------------*
|
|
* Description:
|
|
* Sets the m_langid and calls GetFontSettings()
|
|
*******************************************************************************/
|
|
void CCandidateList::SetLangID( LANGID langid )
|
|
{
|
|
m_langid = langid;
|
|
GetFontSettings();
|
|
} /* CCandidateList::SetLangID */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::Update *
|
|
*------------------------*
|
|
* Description:
|
|
* Called whenever an EN_SELCHANGE notification is received.
|
|
* Shows the alternates button if the entire selection is
|
|
* dictated text from the same phrase.
|
|
* Return:
|
|
* If the entire selection is within a single dictated text range, returns
|
|
* the handle to the button and shows the button.
|
|
* Otherwise returns NULL and hides the button.
|
|
********************************************************************************/
|
|
HWND CCandidateList::Update( CTextRunList *pTextRunList )
|
|
{
|
|
// Clicking off the alternates list should dismiss it
|
|
if ( m_hAltsList )
|
|
{
|
|
DoneWithAltsList();
|
|
}
|
|
|
|
m_pCurrentDictRun = NULL;
|
|
|
|
::ShowWindow( m_hButton, SW_HIDE );
|
|
::InvalidateRect( m_hParent, NULL, true );
|
|
|
|
if ( !m_fMakeUIVisible )
|
|
{
|
|
// The button should not be displayed
|
|
return NULL;
|
|
}
|
|
|
|
if ( !m_cpTextSel || !pTextRunList || !m_hParent )
|
|
{
|
|
// error
|
|
return NULL;
|
|
}
|
|
|
|
long lStart;
|
|
long lEnd;
|
|
HRESULT hr = m_cpTextSel->GetStart( &lStart );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
hr = m_cpTextSel->GetEnd( &lEnd );
|
|
}
|
|
|
|
// Show the alternates UI only if there is no reco computation
|
|
if ( SUCCEEDED( hr ) && m_pRecoMgr && !(m_pRecoMgr->IsProcessingPhrase()) )
|
|
{
|
|
PTEXTRUNNODE pNode = pTextRunList->Find( lStart );
|
|
if ( pNode && pNode->pTextRun->IsDict() &&
|
|
( lEnd <= pNode->pTextRun->GetEnd() ) )
|
|
{
|
|
// The selection is completely contained within this dictated run.
|
|
// The button should appear at the lower right-hand
|
|
// corner of the selected text
|
|
POINT pt;
|
|
hr = m_cpTextSel->GetPoint( tomEnd | TA_BASELINE | TA_LEFT,
|
|
&(pt.x), &(pt.y) );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
// Move the button to the new location
|
|
::ScreenToClient( m_hParent, &pt );
|
|
::MoveWindow( m_hButton, pt.x, pt.y,
|
|
BUTTON_WIDTH, BUTTON_HEIGHT, true );
|
|
::ShowWindow( m_hButton, SW_SHOW );
|
|
|
|
// We know that this node contains a dictation run (see above)
|
|
m_pCurrentDictRun =
|
|
static_cast<CDictationRun *>(pNode->pTextRun);
|
|
|
|
return m_hButton;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise, hide the window and return NULL
|
|
return NULL;
|
|
} /* CCandidateList::Update */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::ShowAlternates *
|
|
*--------------------------------*
|
|
* Description:
|
|
* Called whenever the alternates button is clicked.
|
|
* Hides the alternates button and displays the
|
|
* alternates dialog box.
|
|
* When the alternates dialog box is done, shows the button again.
|
|
********************************************************************************/
|
|
void CCandidateList::ShowAlternates()
|
|
{
|
|
_ASSERTE( m_pCurrentDictRun );
|
|
_ASSERTE( m_cpTextSel );
|
|
if ( !m_pCurrentDictRun || !m_cpTextSel )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get the alternates. The text for the alternates
|
|
// will have been CoTaskMemAlloced
|
|
WCHAR *apszAltsText[ALT_REQUEST_COUNT];
|
|
bool apfFitsInRun[ ALT_REQUEST_COUNT ];
|
|
long lAltStart;
|
|
long lAltEnd;
|
|
ULONG cAltsReturned = 0;
|
|
HRESULT hr = m_pCurrentDictRun->GetAlternatesText(
|
|
m_cpTextSel, ALT_REQUEST_COUNT, &lAltStart, &lAltEnd,
|
|
apszAltsText, apfFitsInRun, &cAltsReturned );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check to make sure that at least one alternate is displayable
|
|
bool fDisplayableAlts = false;
|
|
for ( ULONG ulAlt = 0; !fDisplayableAlts && (ulAlt < cAltsReturned); ulAlt++ )
|
|
{
|
|
fDisplayableAlts = apfFitsInRun[ ulAlt ];
|
|
}
|
|
if ( !fDisplayableAlts )
|
|
{
|
|
// No alternates to display: Won't be doing anything here
|
|
return;
|
|
}
|
|
|
|
// Hide and disable the alternates button
|
|
_ASSERTE( m_hParent );
|
|
BOOL fVisible = ::ShowWindow( m_hButton, SW_HIDE );
|
|
_ASSERTE( fVisible );
|
|
::EnableWindow( m_hButton, false );
|
|
::InvalidateRect( m_hParent, NULL, true );
|
|
|
|
// Create a window for the alternates list.
|
|
// The alternates list should appear at the lower
|
|
// right-hand corner of the text selection
|
|
POINT pt;
|
|
m_cpTextSel->GetPoint( tomEnd | TA_BASELINE | TA_LEFT,
|
|
&(pt.x), &(pt.y) );
|
|
::ScreenToClient( m_hParent, &pt );
|
|
m_hAltsList = ::CreateWindow( _T("LISTBOX"),
|
|
_T(""),
|
|
WS_CHILD | WS_DLGFRAME | LBS_OWNERDRAWFIXED | LBS_NOTIFY | WS_VSCROLL,
|
|
pt.x, pt.y, // Dimensions will be determined by
|
|
// number and width of alternates
|
|
0, 0,
|
|
m_hParent,
|
|
(HMENU) IDC_LIST_ALTS,
|
|
m_hInst,
|
|
NULL );
|
|
|
|
// Get the font with which to draw the alternates (this is an
|
|
// owner-drawn listbox)
|
|
HDC hdc = ::GetDC( m_hAltsList );
|
|
HGDIOBJ hfontOld = m_hFont ? SelectObject(hdc, m_hFont) : 0;
|
|
|
|
// Populate the alternates list.
|
|
ULONG ulAltIndex;
|
|
ULONG ulNumAltsDisplayed = 0;
|
|
WCHAR **ppszCoMemText;
|
|
SIZE size;
|
|
int cxMaxWidth = 0;
|
|
for ( ulAltIndex = 0, ppszCoMemText = apszAltsText;
|
|
ulAltIndex < cAltsReturned;
|
|
ulAltIndex++, ppszCoMemText++ )
|
|
{
|
|
if ( !apfFitsInRun[ ulAltIndex ] )
|
|
{
|
|
// This alt will not be displayed, since the alt covers elements
|
|
// not in this run
|
|
continue;
|
|
}
|
|
|
|
// Keep track of which alternate is going into location
|
|
// cAltsListed in the alternates list
|
|
m_aulAltIndices[ ulNumAltsDisplayed ] = ulAltIndex;
|
|
|
|
// Keep track of the widest alt so far
|
|
_ASSERTE( *ppszCoMemText );
|
|
::GetTextExtentPointW(
|
|
hdc, *ppszCoMemText, (int)wcslen( *ppszCoMemText ), &size );
|
|
cxMaxWidth = max( cxMaxWidth, size.cx );
|
|
|
|
// Owner-drawn list box, so the string is stored as item data (as a WCHAR *)
|
|
// The memory allocated by _wcsdup will be freed in method CCandidateList::DoneWithAltsList
|
|
WCHAR *pwszListItem = _wcsdup( *ppszCoMemText );
|
|
::SendMessage( m_hAltsList, LB_INSERTSTRING, ulNumAltsDisplayed, (LPARAM) pwszListItem );
|
|
|
|
ulNumAltsDisplayed++;
|
|
}
|
|
|
|
// Keep track of how many alternates there are
|
|
m_ulNumAltsDisplayed = ulNumAltsDisplayed;
|
|
|
|
// Get the old font back
|
|
hfontOld ? SelectObject(hdc, hfontOld) : NULL;
|
|
::ReleaseDC( m_hAltsList, hdc );
|
|
|
|
// Bump up the maximum width by the list box border width and the
|
|
// vertical scroll bar width if necessary
|
|
cxMaxWidth += 2 * GetSystemMetrics( SM_CXDLGFRAME );
|
|
if (ulNumAltsDisplayed > MAX_ALTS_DISPLAYED)
|
|
{
|
|
cxMaxWidth += GetSystemMetrics( SM_CXVSCROLL );
|
|
}
|
|
|
|
// The alternates text was CoTaskMemAlloced, so we must free
|
|
// it now
|
|
ULONG ul;
|
|
for ( ul = 0, ppszCoMemText = apszAltsText;
|
|
ul < cAltsReturned;
|
|
ul++, ppszCoMemText++ )
|
|
{
|
|
if ( *ppszCoMemText )
|
|
{
|
|
::CoTaskMemFree( *ppszCoMemText );
|
|
}
|
|
}
|
|
|
|
// Resize the window to the correct width
|
|
// The alternates list should always go inside the parent window,
|
|
// so if the list is too wide, move it to the left.
|
|
RECT rectButton;
|
|
RECT rectParent;
|
|
POINT ptTopLeft;
|
|
::GetWindowRect( m_hButton, &rectButton );
|
|
::GetWindowRect( m_hParent, &rectParent );
|
|
int cyItemHeight = (int) ::SendMessage( m_hAltsList, LB_GETITEMHEIGHT, 0, 0 );
|
|
int cyHeight = __min(((int) ulNumAltsDisplayed + 1) * cyItemHeight,
|
|
(MAX_ALTS_DISPLAYED + 1) * cyItemHeight);
|
|
ptTopLeft.x = __min( rectButton.left, rectParent.right - cxMaxWidth );
|
|
ptTopLeft.y = rectButton.top;
|
|
::ScreenToClient( m_hParent, &ptTopLeft );
|
|
::MoveWindow( m_hAltsList,
|
|
ptTopLeft.x,
|
|
ptTopLeft.y,
|
|
cxMaxWidth,
|
|
cyHeight,
|
|
true );
|
|
|
|
|
|
// Display the alternates list
|
|
::ShowWindow( m_hAltsList, SW_SHOW );
|
|
|
|
// Highlight the text for the first alternate displayed
|
|
MakeTextSelReflectAlt(0);
|
|
|
|
::SetFocus( m_hAltsList );
|
|
} /* CCandidateList::ShowAlternates */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::ShowButton *
|
|
*----------------------------*
|
|
* Description:
|
|
* Shows/hides the alternates button.
|
|
********************************************************************************/
|
|
void CCandidateList::ShowButton( bool bShow )
|
|
{
|
|
::ShowWindow( m_hButton, bShow ? SW_SHOW : SW_HIDE );
|
|
::EnableWindow( m_hButton, bShow );
|
|
::InvalidateRect( m_hParent, NULL, true );
|
|
m_fMakeUIVisible = bShow;
|
|
} /* CCandidateList::ShowButton */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::MakeTextSelReflectAlt *
|
|
*---------------------------------------*
|
|
* Description:
|
|
* Called when an item in the alternates list is selected.
|
|
* Adjusts the text selection in order to jive with whichever
|
|
* elements that alternate replaces
|
|
********************************************************************************/
|
|
void CCandidateList::MakeTextSelReflectAlt( ULONG ulAltIndexInList )
|
|
{
|
|
_ASSERTE( m_pCurrentDictRun );
|
|
_ASSERTE( m_cpTextSel );
|
|
_ASSERTE( ulAltIndexInList < m_ulNumAltsDisplayed );
|
|
_ASSERTE( ulAltIndexInList < ALT_REQUEST_COUNT );
|
|
if ( !m_pCurrentDictRun || !m_cpTextSel || ulAltIndexInList >= m_ulNumAltsDisplayed ||
|
|
ulAltIndexInList >= ALT_REQUEST_COUNT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
long lSelStart;
|
|
long lSelEnd;
|
|
|
|
HRESULT hr = m_pCurrentDictRun->GetAltEndpoints(
|
|
m_aulAltIndices[ ulAltIndexInList ], &lSelStart, &lSelEnd );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// The WM_STOPUPDATE message tells DictationPad that there is no
|
|
// new text, so it does not need to process this selection change
|
|
::SendMessage( m_hMainClientWindow, WM_STOPUPDATE, 0, 0 );
|
|
m_cpTextSel->SetStart( lSelStart );
|
|
m_cpTextSel->SetEnd( lSelEnd );
|
|
::SendMessage( m_hMainClientWindow, WM_STARTUPDATE, 0, 0 );
|
|
}
|
|
} /* CCandidateList::MakeTextSelReflectAlt */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::AlternateChosen *
|
|
*---------------------------------*
|
|
* Description:
|
|
* Called when the user selects an alternate from the alternates UI.
|
|
* Notifies the appropriate CDictationRun that the alt has been
|
|
* chosen and changes the text.
|
|
* Dismisses the alternates list, since choosing an alternate means
|
|
* the user is done with the alternates list.
|
|
********************************************************************************/
|
|
void CCandidateList::AlternateChosen( ULONG ulChosenAltInList )
|
|
{
|
|
_ASSERTE( m_hAltsList );
|
|
_ASSERTE( m_pCurrentDictRun );
|
|
_ASSERTE( m_cpTextSel );
|
|
_ASSERTE( ulChosenAltInList < m_ulNumAltsDisplayed );
|
|
_ASSERTE( ulChosenAltInList < ALT_REQUEST_COUNT );
|
|
if ( !m_hAltsList || !m_pCurrentDictRun || !m_cpTextSel ||
|
|
ulChosenAltInList >= m_ulNumAltsDisplayed || ulChosenAltInList >= ALT_REQUEST_COUNT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
::SendMessage( m_hMainClientWindow, WM_STOPUPDATE, 0, 0 );
|
|
m_pCurrentDictRun->ChooseAlternate( m_aulAltIndices[ ulChosenAltInList ] );
|
|
::SendMessage( m_hMainClientWindow, WM_STARTUPDATE, 0, 0 );
|
|
|
|
// The main window should update the alternates button
|
|
::SendMessage( m_hMainClientWindow, WM_UPDATEALTSBUTTON, 0, 0 );
|
|
|
|
DoneWithAltsList();
|
|
} /* CCandidateList::AlternateChosen */
|
|
|
|
/******************************************************************************
|
|
* CCandidateList::DoneWithAltsList *
|
|
*----------------------------------*
|
|
* Description:
|
|
* Called when the alternates list no longer need be displayed.
|
|
* Either the user has chosen an alternate or the user has clicked
|
|
* off the list to dismiss it.
|
|
********************************************************************************/
|
|
void CCandidateList::DoneWithAltsList()
|
|
{
|
|
if ( !m_hAltsList )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int cItems = (int) ::SendMessage( m_hAltsList, LB_GETCOUNT, 0, 0 );
|
|
for ( int i = 0; i < cItems; i++ )
|
|
{
|
|
// Free the memory used for the strings in the list box
|
|
WCHAR *pwszListItem = (WCHAR *) ::SendMessage(
|
|
m_hAltsList, LB_GETITEMDATA, i, 0 );
|
|
if ( pwszListItem )
|
|
{
|
|
free( pwszListItem );
|
|
::SendMessage( m_hAltsList, LB_SETITEMDATA, i, NULL );
|
|
}
|
|
}
|
|
|
|
m_ulNumAltsDisplayed = 0;
|
|
|
|
::DestroyWindow( m_hAltsList );
|
|
m_hAltsList = 0;
|
|
m_pCurrentDictRun = NULL;
|
|
|
|
// Bring the button back
|
|
::EnableWindow( m_hButton, true );
|
|
} /* CCandidateList::DoneWithAltsList */
|
|
|
|
/******************************************************************************
|
|
* CandidateUIProc() *
|
|
*-------------------*
|
|
* Description:
|
|
* Subclassing procedure for the richedit control so that it can process
|
|
* messages from the candidate list UI.
|
|
*******************************************************************************/
|
|
LRESULT APIENTRY CandidateUIProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
static int cbOffset = 0; // Where the data to be used by this
|
|
// wndproc starts
|
|
// The window long points to the associated instance of the candidate list UI
|
|
CCandidateList *pCandidateList =
|
|
( CCandidateList * )(LONG_PTR) ::GetWindowLongPtr( hWnd, cbOffset );
|
|
|
|
switch( message )
|
|
{
|
|
case WM_NCCREATE:
|
|
{
|
|
// lParam points to a CREATESTRUCT with the CCandidateList * object
|
|
// as its lpCreateParams
|
|
pCandidateList = (CCandidateList *)
|
|
((LPCREATESTRUCT) lParam)->lpCreateParams;
|
|
|
|
// Get the class info and find the offset that will give us
|
|
// the very end of the extra space
|
|
WNDCLASS wc;
|
|
TCHAR pszClassName[ MAX_CLASS_NAME ];
|
|
::GetClassName( hWnd, pszClassName, MAX_CLASS_NAME );
|
|
::GetClassInfo( (HINSTANCE)(LONG_PTR) ::GetWindowLongPtr( hWnd, GWLP_HINSTANCE ),
|
|
pszClassName, &wc );
|
|
_ASSERTE( wc.cbWndExtra >= sizeof( CCandidateList *) );
|
|
if ( wc.cbWndExtra < sizeof( CCandidateList * ) )
|
|
{
|
|
// No space for the CCandidateList * in the window long
|
|
return -1;
|
|
}
|
|
cbOffset = wc.cbWndExtra - sizeof( CCandidateList *);
|
|
|
|
// Set the window long
|
|
::SetWindowLongPtr( hWnd, cbOffset, (LONG_PTR) pCandidateList );
|
|
|
|
// Tell the candidate list about the parent window
|
|
pCandidateList->SetParent( hWnd );
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_KEYDOWN:
|
|
case WM_CHAR:
|
|
// Ignore keystrokes as a recognition is being processed
|
|
if ( pCandidateList->m_pRecoMgr->IsProcessingPhrase()
|
|
|| ( pCandidateList->IsPlaybackInProgress() && ( VK_ESCAPE != wParam )) )
|
|
{
|
|
// Dropping these messages
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_STARTCOMPOSITION:
|
|
if ( pCandidateList->m_pRecoMgr->IsProcessingPhrase()
|
|
|| ( pCandidateList->IsPlaybackInProgress() && ( VK_ESCAPE != wParam )) )
|
|
{
|
|
HIMC himc = ::ImmGetContext( hWnd );
|
|
::ImmNotifyIME( himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0 );
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch ( HIWORD( wParam ) )
|
|
{
|
|
case BN_CLICKED:
|
|
// Clicking on the alternates button
|
|
pCandidateList->ShowAlternates();
|
|
break;
|
|
|
|
case LBN_SELCHANGE:
|
|
// Selecting a different alternate in the alternates list
|
|
pCandidateList->MakeTextSelReflectAlt(
|
|
(int) ::SendMessage( pCandidateList->m_hAltsList, LB_GETCURSEL, 0, 0 ) );
|
|
break;
|
|
|
|
case LBN_DBLCLK:
|
|
// Choosing an alternate
|
|
pCandidateList->AlternateChosen(
|
|
(int) ::SendMessage( pCandidateList->m_hAltsList, LB_GETCURSEL, 0, 0 ) );
|
|
break;
|
|
|
|
case LBN_SETFOCUS:
|
|
// When the alternates list first appears, we give it the input
|
|
// focus. The first alternate should start out selected
|
|
::SendMessage( pCandidateList->m_hAltsList, LB_SETCURSEL, 0, 0 );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DRAWITEM:
|
|
// Since we have an owner-draw list box we need to process this
|
|
// message
|
|
if (wParam == IDC_LIST_ALTS)
|
|
{
|
|
LPDRAWITEMSTRUCT pdis = (LPDRAWITEMSTRUCT)lParam;
|
|
|
|
HGDIOBJ hfontOld = pCandidateList->m_hFont ?
|
|
SelectObject( pdis->hDC, pCandidateList->m_hFont ) : NULL;
|
|
UINT oldTextAlign = GetTextAlign(pdis->hDC);
|
|
|
|
UINT options = ETO_OPAQUE | ETO_CLIPPED;
|
|
|
|
// Strings are stored as item data
|
|
HWND hwndList = pCandidateList->m_hAltsList;
|
|
WCHAR *pwszItemText = (WCHAR *) ::SendMessage( hwndList,
|
|
LB_GETITEMDATA, pdis->itemID, 0 );
|
|
|
|
UINT cStringLen = (UINT) wcslen( pwszItemText );
|
|
|
|
SetTextAlign(pdis->hDC, TA_UPDATECP);
|
|
MoveToEx(pdis->hDC, pdis->rcItem.left, pdis->rcItem.top, NULL);
|
|
ExtTextOutW(pdis->hDC,
|
|
pdis->rcItem.left, pdis->rcItem.top,
|
|
options,
|
|
&pdis->rcItem,
|
|
pwszItemText,
|
|
cStringLen,
|
|
NULL);
|
|
|
|
|
|
SetTextAlign(pdis->hDC, oldTextAlign);
|
|
|
|
if (hfontOld)
|
|
{
|
|
SelectObject(pdis->hDC, hfontOld);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
// Call the original WndProc
|
|
return ::CallWindowProc( pCandidateList->m_wpOrigWndProc,
|
|
hWnd, message, wParam, lParam );
|
|
} /* CandidateUIProc */
|
|
|