IMEUI.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, (LPSTR)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 )
{
DWORD dwBufLen; // 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; // Memory handle for comp. str. array.
DWORD dwBufLenAttr;

//
// 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.
//

if ( ( dwBufLen = ImmGetCompositionString( hIMC, GCS_COMPSTR,
(void FAR*)NULL, 0l ) ) < 0 )
goto exit2;

//
// Allocates memory with dwBufLen+1 bytes to store the composition
// string. Here we allocale on more byte to put null character.
//

if ( !( hMem = LocalAlloc( LPTR, (int)dwBufLen + 1 ) ) )
goto exit2;

if ( !( lpCompStr = (LPSTR) LocalLock( hMem ) ) )
goto exit1;

//
// Reads in the composition string.
//

ImmGetCompositionString( hIMC, GCS_COMPSTR, lpCompStr, dwBufLen );

//
// Null terminated.
//

lpCompStr[ dwBufLen ] = 0;

//
// If GCS_COMPATTR flag is on, then we need to take care of it.
//

if ( CompFlag & GCS_COMPATTR )
{

if ( ( dwBufLenAttr = ImmGetCompositionString( hIMC, GCS_COMPATTR,
( void FAR *)NULL, 0l ) ) < 0 )
goto nothing;

//
// Allocate memory to store attributes of composition strings.
//

if ( !( hMemAttr = LocalAlloc( LPTR, (int)dwBufLenAttr + 1 ) ) )
goto nothing;

if ( !( lpCompStrAttr = (LPSTR) LocalLock( hMemAttr ) ) )
{
LocalFree( hMemAttr );
goto nothing;
}

//
// Reads in the attribute array.
//

ImmGetCompositionString( hIMC, GCS_COMPATTR, lpCompStrAttr,
dwBufLenAttr );

lpCompStrAttr[ dwBufLenAttr ] = 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)dwBufLen;

LocalUnlock( hMem );

if ( lpCompStrAttr )
{
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 )
{
DWORD dwBufLen; // 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 ( ( dwBufLen = ImmGetCompositionString( hIMC, GCS_RESULTSTR,
(void FAR *)NULL, (DWORD) 0 ) ) <= 0 )
goto exit2;

//
// Allocates memory with dwBufLen+1 bytes to store the result
// string. Here we allocale on more byte to put null character.
//

if ( !( hMem = LocalAlloc( LPTR, (int)dwBufLen + 1 ) ) )
goto exit2;

if ( !( lpResultStr = (LPSTR) LocalLock( hMem ) ) )
goto exit1;

//
// Reads in the result string.
//

ImmGetCompositionString( hIMC, GCS_RESULTSTR, lpResultStr, dwBufLen );

//
// 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;

//
// Change caption title to normal
//

SetWindowText( hwnd, (LPSTR)szSteTitle );

//
// Update client area.
//

GetClientRect( hwnd, (LPRECT)&rect );

InvalidateRect( hwnd, (LPRECT)&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.
DWORD dwBufLen; // 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, (LPSTR)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 ( ! ( dwBufLen = ImmGetCandidateList( hIMC, dwIndex,
lpCandList, 0 ) ) )
goto exit2;


//
// Allocate memory space.
//

if( !( gImeUIData.hListCandMem[ dwIndex ] =
GlobalAlloc( LPTR, (int)dwBufLen ) ) )
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, dwBufLen );

//
// Get current caret position.
//

GetCaretPos( (POINT FAR*)&point );
ClientToScreen( hwnd, (LPPOINT)&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(
(LPCTSTR)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,
(HMENU)NULL,
(HANDLE)GetWindowLong( hwnd, GWL_HINSTANCE ),
(LPVOID)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, (LPSTR)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 dwBufLen;
LPSTR lpStr;
DWORD i;
RECT rect;
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 ( !( dwBufLen = ImmGetCandidateList( hIMC, dwIndex, lpCandList, 0 ) ) )
goto exit2;

//
// Relocate memory space.
//

if ( !( gImeUIData.hListCandMem[ dwIndex ] = GlobalReAlloc(
gImeUIData.hListCandMem[ dwIndex ], (int)dwBufLen, 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, dwBufLen );

//
// 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 ] , (LPRECT) &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;

//
// 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, (LPRECT)&rect );
InvalidateRect( hwnd, (LPRECT)&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,
&( 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.
DWORD dwBufLen; //


//
// 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 &&
( dwBufLen = ImmGetCompositionString( hIMC, GCS_COMPSTR,
(void FAR*)NULL, 0l ) ) > 0 )
{
LPSTR lpCompStr; // Pointer to composition string
HLOCAL hMem; // Storage for memory handle.
LPSTR lpCompStrAttr; // Pointer to composition string's attribute
DWORD dwBufLenAttr; //
HLOCAL hMemAttr; // 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)dwBufLen + 1 ) ) )
goto exit2;

if( !( lpCompStr = (LPSTR) LocalLock( hMem ) ) )
{
LocalFree( hMem );
goto exit2;
}


//
// Get composition string and redisplay them.
//

if ( ImmGetCompositionString( hIMC, GCS_COMPSTR, lpCompStr,
dwBufLen ) > 0 )
{

//
// MAke sure whether we need to handle composition string's
// attributes.
//

if ( ( dwBufLenAttr = ( 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)dwBufLenAttr + 1 )))
goto nothing;

if ( !( lpCompStrAttr = (LPSTR) LocalLock( hMemAttr ) ) )
{
LocalFree( hMemAttr );
goto nothing;
}

ImmGetCompositionString( hIMC, GCS_COMPATTR, lpCompStrAttr,
dwBufLenAttr );

lpCompStrAttr[ dwBufLenAttr ] = 0;
} else
{
nothing:
lpCompStrAttr = NULL;
}


lpCompStr[ dwBufLen ] = 0;

DisplayCompString( hwnd, lpCompStr, lpCompStrAttr );

}

LocalUnlock( hMem );
LocalFree( hMem );

if ( lpCompStrAttr )
{
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( (LPPOINT)&point );
ClientToScreen( hwnd, (LPPOINT)&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, (LPSTR)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, (LPRECT)&rect );

InvalidateRect( hwnd, (LPRECT)&rect, FALSE );


//
// Reset IMEUI's global data.
//

gImeUIData.uCompLen = gImeUIData.ImeState = 0;

//
// Reset caret to the original position.
//

HideCaret( hwnd );
ResetCaret( hwnd );
ShowCaret( hwnd );

}