1295 lines
32 KiB
C
1295 lines
32 KiB
C
#include <assert.h>
|
|
#include <windows.h>
|
|
#include <imm.h>
|
|
#include "FullIME.h"
|
|
|
|
static DWORD CompColor[ 4 ] = { RGB( 255, 0, 0 ),
|
|
RGB( 255, 0 , 255 ),
|
|
RGB( 0, 0, 255 ),
|
|
RGB( 0, 255, 0 ) };
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIStartComposition()
|
|
//
|
|
// This handles WM_IME_STARTCOMPOSITION message.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUIStartComposition(
|
|
HWND hwnd )
|
|
{
|
|
|
|
//
|
|
// Change caption title to DBCS composition mode.
|
|
//
|
|
|
|
SetWindowText( hwnd, szSteCompTitle );
|
|
|
|
//
|
|
// Reset global variables.
|
|
//
|
|
|
|
gImeUIData.uCompLen = 0; // length of composition string.
|
|
|
|
gImeUIData.ImeState |= IME_IN_COMPOSITION;
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIComposition()
|
|
//
|
|
// This handles WM_IME_COMPOSITION message. It here just handles
|
|
// composition string and result string. For normal case, it should
|
|
// examine all posibile flags indicated by CompFlag, then do some
|
|
// actitions to reflect what kinds of composition info. IME conversion
|
|
// engine informs.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUIComposition(
|
|
HWND hwnd,
|
|
WPARAM wParam,
|
|
LPARAM CompFlag )
|
|
{
|
|
|
|
if ( CompFlag & GCS_RESULTSTR )
|
|
{
|
|
GetResultStr( hwnd );
|
|
}
|
|
else
|
|
{
|
|
if ( CompFlag & GCS_COMPSTR )
|
|
{
|
|
GetCompositionStr( hwnd, CompFlag );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void GetCompositionStr()
|
|
//
|
|
// This handles WM_IME_COMPOSITION message with GCS_COMPSTR flag on.
|
|
//
|
|
//**********************************************************************
|
|
void GetCompositionStr(
|
|
HWND hwnd,
|
|
LPARAM CompFlag )
|
|
{
|
|
LONG bufLen; // Stogare for len. of composition str
|
|
LPSTR lpCompStr; // Pointer to composition str.
|
|
HIMC hIMC; // Input context handle.
|
|
HLOCAL hMem; // Memory handle.
|
|
LPSTR lpCompStrAttr; // Pointer to composition str array.
|
|
HLOCAL hMemAttr = NULL; // Memory handle for comp. str. array.
|
|
LONG bufLenAttr;
|
|
|
|
//
|
|
// If fail to get input context handle then do nothing.
|
|
// Applications should call ImmGetContext API to get
|
|
// input context handle.
|
|
//
|
|
|
|
if ( !( hIMC = ImmGetContext( hwnd ) ) )
|
|
return;
|
|
|
|
//
|
|
// Determines how much memory space to store the composition string.
|
|
// Applications should call ImmGetCompositionString with
|
|
// GCS_COMPSTR flag on, buffer length zero, to get the bullfer
|
|
// length.
|
|
//
|
|
|
|
bufLen = ImmGetCompositionString( hIMC, GCS_COMPSTR, (void*)NULL, 0l );
|
|
if ( ( IMM_ERROR_NODATA == bufLen) ||
|
|
( IMM_ERROR_GENERAL == bufLen) )
|
|
{
|
|
goto exit2;
|
|
}
|
|
|
|
//
|
|
// Allocates memory with bufLen+1 bytes to store the composition
|
|
// string. Here we allocale on more byte to put null character.
|
|
//
|
|
|
|
if ( !( hMem = LocalAlloc( LPTR, (int)bufLen + 1 ) ) )
|
|
goto exit2;
|
|
|
|
if ( !( lpCompStr = (LPSTR) LocalLock( hMem ) ) )
|
|
goto exit1;
|
|
|
|
//
|
|
// Reads in the composition string.
|
|
//
|
|
|
|
ImmGetCompositionString( hIMC, GCS_COMPSTR, lpCompStr, bufLen );
|
|
|
|
//
|
|
// Null terminated.
|
|
//
|
|
|
|
lpCompStr[ bufLen ] = 0;
|
|
|
|
//
|
|
// If GCS_COMPATTR flag is on, then we need to take care of it.
|
|
//
|
|
|
|
if ( CompFlag & GCS_COMPATTR )
|
|
{
|
|
bufLenAttr = ImmGetCompositionString( hIMC, GCS_COMPATTR, (void*)NULL, 0l );
|
|
if ( ( IMM_ERROR_NODATA == bufLenAttr) ||
|
|
( IMM_ERROR_GENERAL == bufLenAttr) )
|
|
{
|
|
goto nothing;
|
|
}
|
|
|
|
//
|
|
// Allocate memory to store attributes of composition strings.
|
|
//
|
|
|
|
if ( !( hMemAttr = LocalAlloc( LPTR, (int)bufLenAttr + 1 ) ) )
|
|
goto nothing;
|
|
|
|
if ( !( lpCompStrAttr = (LPSTR) LocalLock( hMemAttr ) ) )
|
|
{
|
|
LocalFree( hMemAttr );
|
|
goto nothing;
|
|
}
|
|
|
|
//
|
|
// Reads in the attribute array.
|
|
//
|
|
|
|
ImmGetCompositionString( hIMC, GCS_COMPATTR, lpCompStrAttr,
|
|
bufLenAttr );
|
|
|
|
lpCompStrAttr[ bufLenAttr ] = 0;
|
|
|
|
} else
|
|
{
|
|
|
|
nothing:
|
|
lpCompStrAttr = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Display new composition chars.
|
|
//
|
|
|
|
DisplayCompString( hwnd, lpCompStr, lpCompStrAttr );
|
|
|
|
//
|
|
// Keep the length of the composition string for using later.
|
|
//
|
|
|
|
gImeUIData.uCompLen = (UINT)bufLen;
|
|
|
|
LocalUnlock( hMem );
|
|
|
|
if ( lpCompStrAttr )
|
|
{
|
|
if (NULL != hMemAttr)
|
|
{
|
|
LocalUnlock( hMemAttr );
|
|
LocalFree( hMemAttr );
|
|
}
|
|
}
|
|
|
|
exit1:
|
|
|
|
LocalFree( hMem );
|
|
|
|
exit2:
|
|
|
|
ImmReleaseContext( hwnd, hIMC );
|
|
|
|
}
|
|
|
|
|
|
//***********************************************************************
|
|
//
|
|
// void GetResultStr()
|
|
//
|
|
// This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
|
|
//
|
|
//***********************************************************************
|
|
void GetResultStr(
|
|
HWND hwnd )
|
|
{
|
|
LONG bufLen; // Storage for length of result str.
|
|
LPSTR lpResultStr; // Pointer to result string.
|
|
HIMC hIMC; // Input context handle.
|
|
HLOCAL hMem; // Memory handle.
|
|
|
|
//
|
|
// If fail to get input context handle then do nothing.
|
|
//
|
|
|
|
if ( !( hIMC = ImmGetContext( hwnd ) ) )
|
|
return;
|
|
|
|
//
|
|
// Determines how much memory space to store the result string.
|
|
// Applications should call ImmGetCompositionString with
|
|
// GCS_RESULTSTR flag on, buffer length zero, to get the bullfer
|
|
// length.
|
|
//
|
|
|
|
if ( ( bufLen = ImmGetCompositionString( hIMC, GCS_RESULTSTR,
|
|
(void *)NULL, (DWORD) 0 ) ) <= 0 )
|
|
goto exit2;
|
|
|
|
//
|
|
// Allocates memory with bufLen+1 bytes to store the result
|
|
// string. Here we allocale on more byte to put null character.
|
|
//
|
|
|
|
if ( !( hMem = LocalAlloc( LPTR, (int)bufLen + 1 ) ) )
|
|
goto exit2;
|
|
|
|
if ( !( lpResultStr = (LPSTR) LocalLock( hMem ) ) )
|
|
goto exit1;
|
|
|
|
//
|
|
// Reads in the result string.
|
|
//
|
|
|
|
ImmGetCompositionString( hIMC, GCS_RESULTSTR, lpResultStr, bufLen );
|
|
|
|
//
|
|
// Displays the result string.
|
|
//
|
|
|
|
DisplayResultString( hwnd, lpResultStr );
|
|
|
|
LocalUnlock( hMem );
|
|
|
|
exit1:
|
|
|
|
LocalFree( hMem );
|
|
|
|
exit2:
|
|
|
|
ImmReleaseContext( hwnd, hIMC );
|
|
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIEndComposition
|
|
//
|
|
// This handles WM_IME_ENDCOMPOSITION message.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUIEndComposition(
|
|
HWND hwnd )
|
|
{
|
|
|
|
RECT rect = {0};
|
|
|
|
//
|
|
// Change caption title to normal
|
|
//
|
|
|
|
SetWindowText( hwnd, szSteTitle );
|
|
|
|
//
|
|
// Update client area.
|
|
//
|
|
|
|
GetClientRect( hwnd, &rect );
|
|
|
|
InvalidateRect( hwnd, &rect, FALSE );
|
|
|
|
//
|
|
// Reset the length of composition string to zero.
|
|
//
|
|
|
|
gImeUIData.uCompLen = 0;
|
|
|
|
gImeUIData.ImeState &= ~IME_IN_COMPOSITION;
|
|
|
|
}
|
|
|
|
//**********************************************************************
|
|
//
|
|
// BOOL ImeUINotify()
|
|
//
|
|
// This handles WM_IME_NOTIFY message.
|
|
//
|
|
//**********************************************************************
|
|
BOOL ImeUINotify(
|
|
HWND hwnd,
|
|
WPARAM wParam,
|
|
LPARAM lParam )
|
|
{
|
|
switch (wParam )
|
|
{
|
|
case IMN_OPENCANDIDATE:
|
|
|
|
ImeUIOpenCandidate( hwnd, lParam );
|
|
break;
|
|
|
|
case IMN_CLOSECANDIDATE:
|
|
|
|
ImeUICloseCandidate( hwnd, lParam );
|
|
break;
|
|
|
|
case IMN_CHANGECANDIDATE:
|
|
|
|
ImeUIChangeCandidate( hwnd, lParam );
|
|
break;
|
|
|
|
case IMN_SETOPENSTATUS:
|
|
|
|
ImeUISetOpenStatus( hwnd );
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIOpenCandidate()
|
|
//
|
|
// This handles WM_IME_NOTIFY message with wParam = IMN_OPENCANDIDATE.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUIOpenCandidate(
|
|
HWND hwnd,
|
|
LPARAM CandList )
|
|
{
|
|
HIMC hIMC; // Input context handle.
|
|
LPCANDIDATELIST lpCandList; // Storage for LP to candidate list.
|
|
CANDIDATELIST tempCandList; // Storage for LP to candidate list.
|
|
DWORD bufLen; // Storage for candidate strings.
|
|
LPSTR lpStr; // Storage for LP to a string.
|
|
DWORD dwIndex; // Storage for index of ListCand array
|
|
DWORD i; // Loop count.
|
|
int width = 0; // Storage for width of listCand
|
|
int CurNumCandList = 0; // Storage for number of cand. lists.
|
|
DWORD dwPreferNumPerPage; // Storage for PreferNumPerPage
|
|
POINT point; // Storage for caret position.
|
|
|
|
//
|
|
// If fail to get input context handle, do nothing.
|
|
//
|
|
|
|
if ( ! (hIMC = ImmGetContext( hwnd ) ) )
|
|
return;
|
|
|
|
//
|
|
// Change caption title to DBCS candidate mode.
|
|
//
|
|
|
|
SetWindowText( hwnd, szSteCandTitle );
|
|
|
|
//
|
|
// Find out how many candidate windows have already been opened.
|
|
//
|
|
|
|
for( i = 0; i < MAX_LISTCAND; i++ )
|
|
{
|
|
if ( gImeUIData.hListCand[ i ] )
|
|
{
|
|
CurNumCandList++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check which candidate lists should be displayed by loopping
|
|
// through all possible candidate lists.
|
|
//
|
|
|
|
for( dwIndex = 0; dwIndex < MAX_LISTCAND ; dwIndex ++ )
|
|
{
|
|
|
|
if ( CandList & ( 1 << dwIndex ) )
|
|
{
|
|
//
|
|
// The dwIndex-th candidate list contains candidate strings.
|
|
// So here we want to display them.
|
|
//
|
|
|
|
//
|
|
// Determines how musch memory space should be allocated to
|
|
// read in the corresponding candidate list .
|
|
//
|
|
|
|
if ( ! ( bufLen = ImmGetCandidateList( hIMC, dwIndex, &tempCandList, 0 ) ) )
|
|
goto exit2;
|
|
|
|
|
|
//
|
|
// Allocate memory space.
|
|
//
|
|
|
|
if( !( gImeUIData.hListCandMem[ dwIndex ] =
|
|
GlobalAlloc( LPTR, (int)bufLen ) ) )
|
|
goto exit2;
|
|
|
|
if( !( lpStr =
|
|
(LPSTR)GlobalLock( gImeUIData.hListCandMem[ dwIndex ] ) ) )
|
|
{
|
|
GlobalFree( gImeUIData.hListCandMem[ dwIndex ] );
|
|
gImeUIData.hListCandMem[ dwIndex ] = NULL;
|
|
goto exit2;
|
|
}
|
|
|
|
lpCandList = (LPCANDIDATELIST) lpStr;
|
|
|
|
//
|
|
// Reads in the corresponding candidate list.
|
|
//
|
|
|
|
ImmGetCandidateList( hIMC, dwIndex, lpCandList, bufLen );
|
|
|
|
//
|
|
// Get current caret position.
|
|
//
|
|
|
|
GetCaretPos( (POINT *)&point );
|
|
ClientToScreen( hwnd, &point );
|
|
|
|
//
|
|
// Determines how many candidate strings per page.
|
|
//
|
|
|
|
dwPreferNumPerPage = ( !lpCandList->dwPageSize ) ?
|
|
DEFAULT_CAND_NUM_PER_PAGE :
|
|
lpCandList->dwPageSize;
|
|
//
|
|
// Determining maximum character length the list box
|
|
// will display by loopping through all candidate strings.
|
|
//
|
|
|
|
for( i = 0; i < lpCandList->dwCount; i++ )
|
|
{
|
|
//
|
|
// Get the pointer to i-th candidate string.
|
|
//
|
|
|
|
lpStr = (LPSTR)lpCandList +
|
|
lpCandList->dwOffset[ i ];
|
|
|
|
width = ( width < lstrlen( lpStr ) ) ? lstrlen( lpStr ) : width;
|
|
|
|
}
|
|
|
|
//
|
|
// Create a candidate window for the candidate list.
|
|
//
|
|
|
|
|
|
gImeUIData.hListCand[ dwIndex ] =
|
|
CreateWindow( szCandClass,
|
|
NULL,
|
|
WS_BORDER | WS_POPUP | WS_DISABLED,
|
|
CurNumCandList * X_INDENT + point.x,
|
|
CurNumCandList * Y_INDENT +
|
|
point.y + cyMetrics,
|
|
( width ) * cxMetrics + 10,
|
|
(int)(dwPreferNumPerPage) * cyMetrics + 5,
|
|
hwnd,
|
|
NULL,
|
|
(HINSTANCE)GetWindowLongPtr( hwnd, GWLP_HINSTANCE ),
|
|
NULL );
|
|
//
|
|
// If fail to create the candidate window then do nothing.
|
|
//
|
|
|
|
if ( gImeUIData.hListCand[ dwIndex ] < 0 )
|
|
{
|
|
GlobalUnlock( gImeUIData.hListCandMem[ dwIndex ] );
|
|
GlobalFree( gImeUIData.hListCandMem[ dwIndex ] );
|
|
goto exit2;
|
|
}
|
|
|
|
|
|
//
|
|
// Show candidate window.
|
|
//
|
|
|
|
ShowWindow( gImeUIData.hListCand[ dwIndex ], SW_SHOWNOACTIVATE );
|
|
|
|
//
|
|
// Display candidate strings.
|
|
//
|
|
|
|
DisplayCandStrings( gImeUIData.hListCand[ dwIndex ], lpCandList );
|
|
|
|
GlobalUnlock( gImeUIData.hListCandMem[ dwIndex ] );
|
|
|
|
CurNumCandList++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset IME state.
|
|
//
|
|
|
|
gImeUIData.ImeState |= IME_IN_CHOSECAND;
|
|
|
|
exit2:
|
|
|
|
ImmReleaseContext( hwnd, hIMC );
|
|
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUICloseCandidate()
|
|
//
|
|
// This handles WM_IME_NOTIFY message with
|
|
// wParam = IMN_CLOSECANDIDATE.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUICloseCandidate(
|
|
HWND hwnd,
|
|
LPARAM CandList )
|
|
{
|
|
int index;
|
|
|
|
//
|
|
// Change window's caption title to normal.
|
|
//
|
|
|
|
SetWindowText( hwnd, szSteCompTitle );
|
|
|
|
//
|
|
// If the i-th bit on of CandList that means the i-th
|
|
// candidate list should be closed.
|
|
//
|
|
|
|
for( index = 0; index < MAX_LISTCAND; index ++ )
|
|
{
|
|
if (( CandList & ( 1 << index ) ) && gImeUIData.hListCand[ index ])
|
|
{
|
|
|
|
//
|
|
// Destroy the candidate window.
|
|
//
|
|
|
|
DestroyWindow( gImeUIData.hListCand[ index ] );
|
|
|
|
gImeUIData.hListCand[ index ] = NULL;
|
|
|
|
//
|
|
// Free memory.
|
|
//
|
|
|
|
GlobalFree( gImeUIData.hListCandMem[ index ] );
|
|
|
|
gImeUIData.hListCandMem[ index ] = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
gImeUIData.ImeState &= ~IME_IN_CHOSECAND;
|
|
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIChangeCandidate()
|
|
//
|
|
// This handles WM_IME_NOTIFY message with wParam = IMN_CHANGECANDIDATE.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUIChangeCandidate(
|
|
HWND hwnd,
|
|
LPARAM CandList )
|
|
{
|
|
HIMC hIMC;
|
|
LPCANDIDATELIST lpCandList = NULL;
|
|
DWORD dwIndex;
|
|
DWORD bufLen;
|
|
LPSTR lpStr;
|
|
DWORD i;
|
|
RECT rect = {0};
|
|
int width = 0;
|
|
DWORD dwPreferNumPerPage;
|
|
|
|
|
|
//
|
|
// If fail to get input context, do nothing.
|
|
//
|
|
|
|
if ( !( hIMC = ImmGetContext( hwnd ) ) )
|
|
return;
|
|
|
|
//
|
|
// Determine which candidate list should be updated.
|
|
//
|
|
|
|
for ( dwIndex = 0; dwIndex < MAX_LISTCAND; dwIndex++ )
|
|
if ( CandList & ( 1 << dwIndex ) )
|
|
break;
|
|
|
|
//
|
|
// If dwIndex == MAX_LISTCAND, then something wrong, do nothing.
|
|
//
|
|
|
|
if ( dwIndex == MAX_LISTCAND )
|
|
return;
|
|
|
|
//
|
|
// Determines how much memory space should be allocated to read in the
|
|
// corresponding candidate list.
|
|
//
|
|
|
|
if ( !( bufLen = ImmGetCandidateList( hIMC, dwIndex, lpCandList, 0 ) ) )
|
|
goto exit2;
|
|
|
|
//
|
|
// Relocate memory space.
|
|
//
|
|
|
|
if ( !( gImeUIData.hListCandMem[ dwIndex ] = GlobalReAlloc(
|
|
gImeUIData.hListCandMem[ dwIndex ], (int)bufLen, LPTR ) ) )
|
|
{
|
|
goto exit2;
|
|
}
|
|
|
|
if ( !( lpStr =
|
|
(LPSTR)GlobalLock( gImeUIData.hListCandMem[ dwIndex ] ) ) )
|
|
{
|
|
GlobalFree( gImeUIData.hListCandMem[ dwIndex ] );
|
|
gImeUIData.hListCandMem[ dwIndex ] = NULL;
|
|
goto exit2;
|
|
}
|
|
|
|
lpCandList = (LPCANDIDATELIST) lpStr;
|
|
|
|
//
|
|
// Reads in the corresponding candidate list.
|
|
//
|
|
|
|
ImmGetCandidateList( hIMC, dwIndex, lpCandList, bufLen );
|
|
|
|
//
|
|
// Determines how many candidate strings per page.
|
|
//
|
|
|
|
dwPreferNumPerPage = ( !lpCandList->dwPageSize ) ?
|
|
DEFAULT_CAND_NUM_PER_PAGE :
|
|
lpCandList->dwPageSize;
|
|
//
|
|
// Determining maximum character length the list box
|
|
// will display by loopping through all candidate strings.
|
|
//
|
|
|
|
for( i = 0; i < lpCandList->dwCount; i++ )
|
|
{
|
|
//
|
|
// Get the pointer to i-th candidate string.
|
|
//
|
|
|
|
lpStr = (LPSTR)lpCandList +
|
|
lpCandList->dwOffset[ i ];
|
|
|
|
width = ( width < lstrlen( lpStr ) ) ? lstrlen( lpStr ) :
|
|
width;
|
|
}
|
|
|
|
GetWindowRect( gImeUIData.hListCand[ dwIndex ] , &rect);
|
|
|
|
SetWindowPos( gImeUIData.hListCand[ dwIndex ],
|
|
hwnd,
|
|
rect.left,
|
|
rect.top,
|
|
( width ) * cxMetrics + 10,
|
|
(int)(dwPreferNumPerPage) * cyMetrics + 5,
|
|
SWP_NOZORDER | SWP_NOACTIVATE );
|
|
|
|
|
|
DisplayCandStrings( gImeUIData.hListCand[ dwIndex ], lpCandList );
|
|
|
|
GlobalUnlock( gImeUIData.hListCandMem[ dwIndex ] );
|
|
|
|
exit2:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//***********************************************************************
|
|
//
|
|
// void ImeUISetOpenStatus()
|
|
//
|
|
// This handles WM_IME_REPORT message with wParam = IR_NOTIFY &
|
|
// lParam = IMC_SETOPENSTATUS.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUISetOpenStatus(
|
|
HWND hwnd )
|
|
{
|
|
int i; // Lopp counter
|
|
HIMC hIMC; // Storage for input context handle.
|
|
|
|
//
|
|
// If fail to get input context handle then do nothing
|
|
//
|
|
|
|
if ( !( hIMC = ImmGetContext( hwnd ) ) )
|
|
return;
|
|
|
|
if ( ImmGetOpenStatus( hIMC ) )
|
|
{
|
|
|
|
//
|
|
// If the IME conversion engine is open, here we change
|
|
// window's caption title to DBCS composition mode.
|
|
//
|
|
|
|
SetWindowText( hwnd, (LPSTR)szSteCompTitle );
|
|
}
|
|
else
|
|
{
|
|
RECT rect = {0};
|
|
|
|
//
|
|
// If the IME conversion engine is closed, here we
|
|
// erase all already displayed composition chars if any,
|
|
// change the window's caption title to normal.
|
|
//
|
|
|
|
GetClientRect( hwnd, &rect );
|
|
InvalidateRect( hwnd, &rect, FALSE );
|
|
|
|
SetWindowText( hwnd, (LPSTR)szSteTitle );
|
|
|
|
//
|
|
// Here we close and destroy all of candidate windows
|
|
// if IME conversion engine is closed.
|
|
//
|
|
|
|
for( i = 0; i <= MAX_LISTCAND; i++ )
|
|
{
|
|
if ( gImeUIData.hListCand[ i ] )
|
|
{
|
|
DestroyWindow( gImeUIData.hListCand[ i ] );
|
|
gImeUIData.hListCand[ i ] = NULL;
|
|
GlobalFree( gImeUIData.hListCandMem[ i ] );
|
|
gImeUIData.hListCandMem[ i ] = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset IMEUI's global data.
|
|
//
|
|
|
|
gImeUIData.uCompLen = 0;
|
|
gImeUIData.ImeState = 0;
|
|
|
|
ResetCaret( hwnd );
|
|
|
|
}
|
|
|
|
ImmReleaseContext( hwnd, hIMC );
|
|
}
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// void DisplayCompString()
|
|
//
|
|
// This displays composition string.
|
|
//
|
|
// This function supports only fixed pitch font.
|
|
//
|
|
//*********************************************************************
|
|
void DisplayCompString( HWND hwnd, LPSTR lpStr, LPSTR lpStrAttr )
|
|
{
|
|
HDC hdc;
|
|
int StrLen = lstrlen( lpStr );
|
|
RECT rect;
|
|
DWORD dwColor;
|
|
int i;
|
|
BOOL fDBCSTrailByte = FALSE;
|
|
|
|
|
|
hdc = GetDC( hwnd );
|
|
|
|
HideCaret( hwnd );
|
|
|
|
//
|
|
// Determine OPAQUE rect.
|
|
//
|
|
|
|
rect.left = xPos * cxMetrics;
|
|
rect.top = yPos * cyMetrics;
|
|
rect.bottom = rect.top + cyMetrics;
|
|
rect.right = ( (int)gImeUIData.uCompLen > StrLen ) ?
|
|
( xPos + gImeUIData.uCompLen ) * cxMetrics:
|
|
( xPos + StrLen ) * cxMetrics;
|
|
|
|
//
|
|
// This example we use red to display composition chars
|
|
// with attribute 000, pink for attribute 001,
|
|
// blue for attribute 010, green for attribute 011.
|
|
//
|
|
|
|
//
|
|
// Each composition character has different attribute.
|
|
// We here use different kinds of color to represent attributes.
|
|
// Red, pink, blue and green are for attribute 000, 001, 010 and 011,
|
|
// respectively.
|
|
//
|
|
|
|
|
|
dwColor = GetTextColor( hdc );
|
|
|
|
if ( ( StrLen % 2 ) )
|
|
{
|
|
if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
|
|
{
|
|
textbuf[ yPos ][ xPos + StrLen ] = ' ';
|
|
fDBCSTrailByte = TRUE;
|
|
}
|
|
} else
|
|
{
|
|
if ( !IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 2 ] ) )
|
|
{
|
|
if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
|
|
{
|
|
textbuf[ yPos ][ xPos + StrLen ] = ' ';
|
|
fDBCSTrailByte = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !lpStrAttr )
|
|
{
|
|
//
|
|
// If there are not attribute array, here we use default color, RED,
|
|
// to display all of composition characters.
|
|
//
|
|
|
|
SetTextColor( hdc, CompColor[ 0 ] ); // default color
|
|
|
|
ExtTextOut( hdc, xPos * cxMetrics, yPos * cyMetrics,
|
|
ETO_OPAQUE, &rect, lpStr, StrLen, 0 );
|
|
|
|
} else
|
|
{
|
|
|
|
int ColorIndex;
|
|
|
|
ExtTextOut( hdc, xPos * cxMetrics, yPos * cyMetrics, ETO_OPAQUE,
|
|
&rect, NULL, 0, 0 );
|
|
|
|
for( i = 0; *lpStr;)
|
|
{
|
|
int cnt = IsDBCSLeadByte(*lpStr) ? 2 : 1;
|
|
|
|
ColorIndex = ( ((int)*lpStrAttr) < 0 ) ? 0 : (int)*lpStrAttr;
|
|
ColorIndex = ( ColorIndex > 3 ) ? 3 : ColorIndex;
|
|
|
|
SetTextColor( hdc, CompColor[ ColorIndex ] );
|
|
|
|
TextOut( hdc, ( i + xPos ) * cxMetrics, yPos * cyMetrics,
|
|
lpStr, cnt );
|
|
lpStr += cnt;
|
|
lpStrAttr += cnt;
|
|
i += cnt;
|
|
}
|
|
}
|
|
|
|
|
|
SetTextColor( hdc, dwColor );
|
|
|
|
if ( fDBCSTrailByte )
|
|
{
|
|
TextOut( hdc, ( xPos + StrLen ) * cxMetrics, yPos * cyMetrics,
|
|
" ", 1 );
|
|
}
|
|
|
|
SetCaretPos( ( xPos + StrLen ) * cxMetrics, yPos * cyMetrics );
|
|
|
|
ShowCaret( hwnd );
|
|
|
|
ReleaseDC( hwnd, hdc );
|
|
|
|
}
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// void DisplayResultString()
|
|
//
|
|
// This displays result string.
|
|
//
|
|
// This function supports only fixed pitch font.
|
|
//
|
|
//*********************************************************************
|
|
void DisplayResultString(
|
|
HWND hwnd,
|
|
LPSTR lpStr )
|
|
{
|
|
|
|
int StrLen; // Storage for string length.
|
|
int i; // Loop counter.
|
|
HDC hdc; // Display context handle.
|
|
|
|
StrLen = lstrlen( lpStr );
|
|
|
|
//
|
|
// If there is no room for compsoition string, discard it
|
|
//
|
|
|
|
if ( xPos == ( LASTCOL - StrLen - 1 ) )
|
|
return;
|
|
|
|
//
|
|
// if insert mode or during composition session,
|
|
// move rest of line to the right by StrLen bytes.
|
|
//
|
|
|
|
if ( fInsertMode )
|
|
{
|
|
|
|
for( i = LASTCOL; i > xPos; i-- )
|
|
textbuf[ yPos ][ i ] = textbuf[ yPos ][ i - StrLen ];
|
|
|
|
//
|
|
// If the row ends on a lead byte, blank it out,
|
|
// To do this we must first traverse the string starting
|
|
// from a know character boundry until we reach the last column.
|
|
// If the last column is a character boundry then the last
|
|
// character is either a string byte or a lead byte.
|
|
//
|
|
|
|
for( i = xPos + StrLen; i < LASTCOL; )
|
|
{
|
|
if ( IsDBCSLeadByte( textbuf[ yPos ][ i ] ) )
|
|
i++;
|
|
i++;
|
|
}
|
|
|
|
if ( i == LASTCOL && IsDBCSLeadByte( textbuf[ yPos ][ i ] ) )
|
|
textbuf[ yPos ][ LASTCOL ] = ' ';
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// overtype mode
|
|
//
|
|
|
|
if ( ( StrLen % 2 ) )
|
|
{
|
|
if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
|
|
textbuf[ yPos ][ xPos + StrLen ] = ' ';
|
|
}
|
|
else
|
|
{
|
|
if ( !IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 2 ] ) )
|
|
{
|
|
//
|
|
// Overtyping the current byte, plus the following byte
|
|
// which could be a lead byte.
|
|
//
|
|
|
|
if ( IsDBCSLeadByte( textbuf[ yPos ][ xPos + StrLen - 1 ] ) )
|
|
textbuf[ yPos ][ xPos + StrLen ] = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store input character at current caret position.
|
|
//
|
|
|
|
for( i = 0; i <= LASTCOL && *lpStr; i++ )
|
|
textbuf[ yPos ][ xPos + i ] = *lpStr++;
|
|
|
|
//
|
|
// Display input character
|
|
//
|
|
|
|
|
|
hdc = GetDC( hwnd );
|
|
|
|
HideCaret( hwnd );
|
|
|
|
//
|
|
// Displays result string with normal color.
|
|
//
|
|
|
|
TextOut( hdc, xPos *cxMetrics, yPos * cyMetrics,
|
|
(LPCSTR)&( textbuf[ yPos][xPos] ), MAXCOL - xPos );
|
|
|
|
|
|
ShowCaret( hwnd );
|
|
|
|
ReleaseDC( hwnd, hdc );
|
|
|
|
//
|
|
// Reset Caret position
|
|
//
|
|
|
|
xPos += StrLen;
|
|
|
|
if ( xPos > LASTCOL )
|
|
xPos = LASTCOL;
|
|
|
|
ResetCaret( hwnd );
|
|
|
|
gImeUIData.uCompLen = 0;
|
|
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void RestoreImeUI()
|
|
//
|
|
// This repaints all displayed composition string if need.
|
|
// Main window procedure will call this upon receiving
|
|
// WM_PAINT message.
|
|
//
|
|
//**********************************************************************
|
|
void RestoreImeUI(
|
|
HWND hwnd )
|
|
{
|
|
HIMC hIMC; // Storage for input context handle.
|
|
LONG bufLen; //
|
|
|
|
|
|
//
|
|
// If fail to get input context handle then do nothing.
|
|
//
|
|
|
|
if ( !( hIMC = ImmGetContext( hwnd ) ) )
|
|
return;
|
|
|
|
//
|
|
// If IME conversion engine is open and there are any composition
|
|
// string in the context then we redisplay them.
|
|
//
|
|
|
|
if ( ImmGetOpenStatus( hIMC ) && gImeUIData.ImeState &&
|
|
( bufLen = ImmGetCompositionString( hIMC, GCS_COMPSTR,
|
|
(void *)NULL, 0l ) ) > 0 )
|
|
{
|
|
LPSTR lpCompStr; // Pointer to composition string
|
|
HLOCAL hMem; // Storage for memory handle.
|
|
LPSTR lpCompStrAttr = NULL; // Pointer to composition string's attribute
|
|
LONG bufLenAttr; //
|
|
HLOCAL hMemAttr = NULL; // Memory handle for composition string's
|
|
// attributes.
|
|
|
|
//
|
|
// If fail to allocate and lock memory space for reading in
|
|
// the composition string then do nothing.
|
|
//
|
|
|
|
if ( !( hMem = LocalAlloc( LPTR, (int)bufLen + 1 ) ) )
|
|
goto exit2;
|
|
|
|
if( !( lpCompStr = (LPSTR) LocalLock( hMem ) ) )
|
|
{
|
|
LocalFree( hMem );
|
|
goto exit2;
|
|
}
|
|
|
|
//
|
|
// Get composition string and redisplay them.
|
|
//
|
|
|
|
if ( ImmGetCompositionString( hIMC, GCS_COMPSTR, lpCompStr,
|
|
bufLen ) > 0 )
|
|
{
|
|
|
|
//
|
|
// Make sure whether we need to handle composition string's
|
|
// attributes.
|
|
//
|
|
|
|
if ( ( bufLenAttr = ( ImmGetCompositionString( hIMC,
|
|
GCS_COMPATTR, (void FAR*)NULL, 0l ) ) ) > 0 )
|
|
{
|
|
//
|
|
// If fail to allocate and lock memory space for reading in
|
|
// the composition string's attribute then we assume
|
|
// no attribute array.
|
|
//
|
|
|
|
if ( !( hMemAttr = LocalAlloc(LPTR, (int)bufLenAttr + 1 )))
|
|
goto nothing;
|
|
|
|
if ( !( lpCompStrAttr = (LPSTR) LocalLock( hMemAttr ) ) )
|
|
{
|
|
LocalFree( hMemAttr );
|
|
goto nothing;
|
|
}
|
|
|
|
ImmGetCompositionString( hIMC, GCS_COMPATTR, lpCompStrAttr,
|
|
bufLenAttr );
|
|
|
|
lpCompStrAttr[ bufLenAttr ] = 0;
|
|
}
|
|
else
|
|
{
|
|
nothing:
|
|
lpCompStrAttr = NULL;
|
|
}
|
|
|
|
|
|
lpCompStr[ bufLen ] = 0;
|
|
|
|
DisplayCompString( hwnd, lpCompStr, lpCompStrAttr );
|
|
|
|
}
|
|
|
|
LocalUnlock( hMem );
|
|
LocalFree( hMem );
|
|
|
|
if ( lpCompStrAttr )
|
|
{
|
|
if (NULL != hMemAttr)
|
|
{
|
|
LocalUnlock( hMemAttr );
|
|
LocalFree( hMemAttr );
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
exit2:
|
|
|
|
ImmReleaseContext( hwnd, hIMC );
|
|
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIMove()
|
|
//
|
|
// Handler routine of WM_MOVE message.
|
|
//
|
|
//*********************************************************************
|
|
void ImeUIMoveCandWin(
|
|
HWND hwnd )
|
|
{
|
|
|
|
if ( gImeUIData.ImeState & IME_IN_CHOSECAND )
|
|
{
|
|
POINT point; // Storage for caret position.
|
|
int i; // loop counter.
|
|
int NumCandWin; // Storage for num of cand win.
|
|
RECT rect; // Storage for client rect.
|
|
|
|
//
|
|
// If current IME state is in chosing candidate, here we
|
|
// move all candidate windows, if any, to the appropriate
|
|
// position based on the parent window's position.
|
|
//
|
|
|
|
NumCandWin = 0;
|
|
|
|
GetCaretPos( &point );
|
|
ClientToScreen( hwnd, &point );
|
|
|
|
for ( i = 0; i < MAX_LISTCAND ; i++ )
|
|
{
|
|
if ( gImeUIData.hListCand[ i ] )
|
|
{
|
|
GetClientRect( gImeUIData.hListCand[ i ], &rect );
|
|
|
|
MoveWindow( gImeUIData.hListCand[ i ],
|
|
point.x + X_INDENT * NumCandWin,
|
|
point.y + Y_INDENT * NumCandWin + cyMetrics,
|
|
( rect.right - rect.left + 1 ),
|
|
( rect.bottom - rect.top + 1 ), TRUE );
|
|
|
|
NumCandWin++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
//
|
|
// void ImeUIClearData()
|
|
//
|
|
// Handler routine of WM_IME_SELECT message.
|
|
//
|
|
//**********************************************************************
|
|
void ImeUIClearData(
|
|
HWND hwnd )
|
|
{
|
|
|
|
RECT rect;
|
|
int i;
|
|
|
|
SetWindowText( hwnd, szSteTitle );
|
|
|
|
//
|
|
// If user switches to other IME, here we destroy all candidate
|
|
// windows which has been opened and erase all composition
|
|
// chars if any.
|
|
//
|
|
|
|
for( i = 0; i < MAX_LISTCAND; i++ )
|
|
{
|
|
if ( gImeUIData.hListCand[ i ] )
|
|
{
|
|
//
|
|
// The i-th candidate list has already been displayed,
|
|
// destroy it and free memory which stores candidate
|
|
// strings.
|
|
//
|
|
|
|
DestroyWindow( gImeUIData.hListCand[ i] );
|
|
GlobalFree( gImeUIData.hListCandMem[ i ] );
|
|
|
|
gImeUIData.hListCand[ i ] =
|
|
gImeUIData.hListCandMem[ i ] = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update client area.
|
|
//
|
|
|
|
GetClientRect( hwnd, &rect );
|
|
|
|
InvalidateRect( hwnd, &rect, FALSE );
|
|
|
|
|
|
//
|
|
// Reset IMEUI's global data.
|
|
//
|
|
|
|
gImeUIData.uCompLen = gImeUIData.ImeState = 0;
|
|
|
|
//
|
|
// Reset caret to the original position.
|
|
//
|
|
|
|
HideCaret( hwnd );
|
|
ResetCaret( hwnd );
|
|
ShowCaret( hwnd );
|
|
|
|
}
|
|
|