/************************************************************************ * * Title: * * Main.C - IME Half-aware Simple Text Editor (DBCS version) * * Purpose: * * Sample program for DBCS programming and IME half-aware application. * * Synopsis: * * This program is designed as a bare-bone example to demonstrate the * basic elements of DBCS-enabling, and how to design an IME half-aware * application. * * The data structure is a fixed-size 2-dimensional byte array. The * font is the fixed-pitch system font. Rudimentary text editing * functions, such as the basic cursor movements, insertion, deletion, * have been implemented. * * DBCS-enabling notes: * * This version of STE is DBCS-enabled with respect to character input, * caret shape and movement, and mouse clicking. It should work on * any version of Far East Windows since 4.0. * * As far as source code maintenance goes, there are generally two * approaches. * * The first is to add DBCS enabling code under '#ifdef DBCS'. The * advantage of this approach is that it keeps the DBCS enabling code * distinct from the SBCS, so it's easier to add them in, and also to * remove them later. (For example, when you want to replace DBCS * enabling with Unicode enabling.) The drawback is that because the * DBCS and the SBCS logic are not integrated, they can easily get out * of sync (as the SBCS code evolves.) * * The second approach, which is adopted by this sample app, is to * integrate DBCS enabling with SBCS. It takes longer to do, but * the resulting source is easier to maintain. Since IsDBCSLeadByte * is at the heart of any DBCS-enabling logic, an additional speed up * for generating an SBCS-only version is to define that function as * FALSE, and let the compiler optimize the DBCS logic away. * * IME Half-Aware notes: * * This version of STE is an IME half-aware application with the * capiabilities to open/close IME conversion engine and control * where the IME composition window will be located in. It should * work on any version of Far East Windows since 4.0. * * This kind of application typically wants to control the behavior * of IME like opening/closing IME, setting where to show composition * window, setting where to show candidate lists and so on. It does * not display any user interface for IME. * * History: * * 17-Aug-1992 created * 28-Sep-1992 added DBCS-enabling * 30-Sep-1992 bug fixes * 25-Mar-1994 added IME half-aware logics * ************************************************************************/ #include #include #include #include "halfime.h" // // Function prototype // void ResetCaret( HWND ); void SetIMECompFormPos( HWND ); LRESULT CALLBACK SteWndProc( HWND, UINT, WPARAM, LPARAM ); /************************************************************************ * * Global data * ************************************************************************/ TCHAR szSteClass[10] = {0}; // window class name TCHAR szSteTitle[40] = {0}; // window frame title UINT cxMetrics, // aver. character width cxOverTypeCaret, // caret width for overtype mode cyMetrics; // character height int xPos, yPos; // caret position HFONT hfntFixed; // fixed-pitch font HFONT hfntOld; // default font holder BOOL fInsertMode; // insert/overtype mode flag int CaretWidth; // insert/overtype mode caret width BYTE textbuf[MAXROW][MAXCOL]; // text buffer int DBCSFillChar; // 'Pattern/DBCS' fill character WPARAM CompWindowMode; // Composition window mode. COMPOSITIONFORM CompForm = {0}; // Storage for composition window // structure. DWORD fdwProperty; // the property of current active IME /************************************************************************ * * SteRegister - standard class registration routine * ************************************************************************/ int SteRegister( HANDLE hInstance ) { WNDCLASS wc = {0}; wc.hCursor = LoadCursor( NULL, IDC_IBEAM ); wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(ID_ICON)); wc.lpszMenuName = MAKEINTRESOURCE(ID_MENU); wc.hInstance = hInstance; wc.lpszClassName = szSteClass; wc.lpfnWndProc = (WNDPROC)SteWndProc; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.style = CS_BYTEALIGNCLIENT | CS_CLASSDC; if ( !RegisterClass( &wc ) ) { return FALSE; } else { return TRUE; } } /************************************************************************ * * WinMain * ************************************************************************/ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow ) { MSG msg = {0}; HWND hWnd = NULL; BOOL bSuccess = TRUE; BOOL bRet; LoadString( hInstance, IDS_CLASS, szSteClass, 10 ); LoadString( hInstance, IDS_TITLE, szSteTitle, 40 ); if ( !SteRegister( hInstance ) ) { bSuccess = FALSE; goto exit_func; } if ( !(hWnd = CreateWindow( szSteClass, szSteTitle, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL)) ) { bSuccess = FALSE; goto exit_func; } // // Create window with just enough client space for the text buffer // SetWindowPos( hWnd, 0, 0, 0, MAXCOL*cxMetrics + GetSystemMetrics(SM_CXBORDER)*2, MAXROW*cyMetrics + GetSystemMetrics(SM_CYBORDER)*2 + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYMENU), SWP_NOZORDER ); ShowWindow( hWnd, nCmdShow ); // // Any IME half-aware application should call TranslateMessage API to // translate messages. This is because Windows95 IME architecture // will call ImmToAsciiEx API, if necessary, to make IME conversion // engine work, and generate some IME messages during TranslateMessage // session. Otherwise, IME will not work. // while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if (bRet == -1) { // possibly handle the error bSuccess = FALSE; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } exit_func: if (bSuccess) { return (int) msg.wParam; } else { return 0; } } /********************************************************************** * * SteIMEOpenClose() * * This routines calls IMM API to open or close IME. * ***********************************************************************/ void SteImeOpenClose( HWND hWnd, BOOL fFlag ) { HIMC hIMC = NULL; // Input context handle. // // If fFlag is true then open IME; otherwise close it. // if ( !( hIMC = ImmGetContext( hWnd ) ) ) { return; } // // When an application wants to open/close IME for some // reasons, it should call TMMSetOpenStatus() to do that. // ImmSetOpenStatus( hIMC, fFlag ); ImmReleaseContext( hWnd, hIMC ); } /********************************************************************** * * SteIMECompWindowPos() * * This routines inform IME where to display composition window. * Here we control IME composition window with the following cases: * 1: DEFAULT: Display composition window in IME default position. * 2: NEARCARET: Display composition window in near caret position. * 3: PreFer: Display composition window in application prefer position. * ***********************************************************************/ void SteIMECompWindowPos( HWND hWnd, WPARAM cmd ) { RECT rect = {0}; // Storage for client rect. HIMC hImc = NULL; // // Set current composition mode. // CompWindowMode = cmd; switch( cmd ) { case IDC_DEFAULT_POS: // // Put composition window in default position // being deterimined by IME UI. Applications // have to set composition form style with // CFS_DEFAULT to imform IME UI. // CompForm.dwStyle = CFS_DEFAULT; break; case IDC_NEARCARET_POS: // // Put composition window in near caret // position. If IME UI receives this control // message, it will put the composition window // near to the position deesignated by ptCurrentPos. // So for this operation, applications have to set // composition form style with CFS_POINT and store // caret position in ptCurrentPos. // CompForm.dwStyle = CFS_POINT; GetCaretPos( (PPOINT) &CompForm.ptCurrentPos ); break; case IDC_PREFER_POS: // Put composition window in application // prefer position. Applications have to set // composition form sytle with CFS_POINT | // CFS_FORCE_POSITION and store the prefer // position in ptCurrentPos. // // The difference between CFS_POINT and CFS_POINT // with CFS_FORCE_POSITION is if setting // CFS_FORCE_POSITION style, IME will actually // display its composition window in the position // specified by ptCurrent without any adjustment; // otherwise IME will shPosow its composition window // by adjusting the position specified by // ptCurrentPos. // CompForm.dwStyle = CFS_POINT | CFS_FORCE_POSITION; GetClientRect( hWnd, (LPRECT) &rect); CompForm.ptCurrentPos.x = 0; CompForm.ptCurrentPos.y = rect.bottom; break; } hImc = ImmGetContext( hWnd ); ImmSetCompositionWindow( hImc, (COMPOSITIONFORM FAR*)&CompForm ); ImmReleaseContext( hWnd, hImc ); } /*********************************************************************** * * ResetIMECompWin() * * This routine informs IME the fact that caret position has been changed. * ************************************************************************/ void ResetIMECompWin( HWND hWnd ) { if ( CompWindowMode == IDC_NEARCARET_POS || CompWindowMode == IDC_PREFER_POS ) { HIMC hIMC = ImmGetContext(hWnd); if ( hIMC ) { ImmSetCompositionWindow(hIMC,&CompForm); } ImmReleaseContext( hWnd , hIMC); } } /*********************************************************************** * * SetIMECompFormPos( HWND ) * ************************************************************************/ void SetIMECompFormPos( HWND hWnd ) { // // If current composition form mode is near caret operation, // application should inform IME UI the caret position has been // changed. IME UI will make decision whether it has to adjust // composition window position. // // if ( CompWindowMode == IDC_NEARCARET_POS ) { HIMC hIMC = ImmGetContext(hWnd); POINT point = {0}; GetCaretPos( &point ); CompForm.dwStyle = CFS_POINT; CompForm.ptCurrentPos.x = (long) point.x; CompForm.ptCurrentPos.y = (long) point.y; if ( hIMC ) { ImmSetCompositionWindow(hIMC,&CompForm); } ImmReleaseContext( hWnd , hIMC); } } /************************************************************************ * * SteCreate - WM_CREATE message handler * ************************************************************************/ void SteCreate( HWND hWnd ) { HDC hdc = NULL; TEXTMETRIC tm = {0}; LOGFONT lFont = {0}; HFONT hFont = NULL; // // Note that this window has a class DC // hdc = GetDC( hWnd ); // // Select fixed pitch system font and get its text metrics // hfntFixed = GetStockObject( SYSTEM_FIXED_FONT ); hfntOld = SelectObject( hdc, hfntFixed ); GetTextMetrics( hdc, &tm ); ReleaseDC( hWnd, hdc ); cxMetrics = tm.tmAveCharWidth; cyMetrics = tm.tmHeight; // // Determine the version of DBCS Windows from system font charset ID. // Then hardcode a DBCS character value for the 'Pattern/DBCS' command. // The value is the Han character for 'door' or 'gate', which is // left-right symmetrical. // switch( tm.tmCharSet ) { case SHIFTJIS_CHARSET: DBCSFillChar = 0x96e5; break; case HANGEUL_CHARSET: DBCSFillChar = 0xdaa6; break; case CHINESEBIG5_CHARSET: DBCSFillChar = 0xaaf9; break; default: DBCSFillChar = 0x7071; // 'pq' break; } // // Initialize caret width. Fat in INSERT mode, skinny in OVERTYPE mode. // fInsertMode = FALSE; CaretWidth = cxOverTypeCaret = GetSystemMetrics( SM_CXBORDER ); // // Sets the logical font to be used to display characters in the // composition window. Especially for at caret or near caret operation, // application should set composition font. // if ( ( hFont = (HFONT)SendMessage( hWnd, WM_GETFONT, 0, 0L ) ) != NULL ) { if ( GetObject( hFont, sizeof(LOGFONT), (LPVOID)&lFont ) ) { HIMC hImc = NULL; if ( ( hImc = ImmGetContext( hWnd ) ) ) { ImmSetCompositionFont( hImc, &lFont ); ImmReleaseContext( hWnd, hImc ); } } } PostMessage( hWnd, WM_COMMAND, IDC_CLEAR, 0L ); fdwProperty = ImmGetProperty( GetKeyboardLayout(0), IGP_PROPERTY ); } /************************************************************************ * * ResetCaret - Reset caret shape to match input mode (overtype/insert) * ************************************************************************/ void ResetCaret( HWND hWnd ) { HideCaret( hWnd ); DestroyCaret(); CreateCaret( hWnd, NULL, (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ? CaretWidth*2 : CaretWidth, cyMetrics ); SetCaretPos( xPos * cxMetrics, yPos * cyMetrics ); ShowCaret( hWnd ); SetIMECompFormPos( hWnd ); } /************************************************************************ * * SteCommand - WM_COMMAND handler * ************************************************************************/ void SteCommand( HWND hWnd, WPARAM cmd ) { switch( cmd ) { case IDC_CLEAR: // // Blank out text buffer. Return caret to home position // for ( yPos = FIRSTROW; yPos <= LASTROW; yPos++ ) { for ( xPos = FIRSTCOL; xPos <= LASTCOL; xPos++ ) { textbuf[yPos][xPos] = ' '; } } break; case IDC_ANSIFILL: /* fall-through */ case IDC_DBCSFILL: // // Fill text buffer with ANSI or DBCS pattern // for ( yPos = FIRSTROW; yPos <= LASTROW; yPos++ ) { for ( xPos = FIRSTCOL; xPos <= LASTCOL; xPos++ ) { if ( cmd == IDC_ANSIFILL ) { textbuf[yPos][xPos] = 'a'; } else { textbuf[yPos][xPos] = HIBYTE(DBCSFillChar); textbuf[yPos][++xPos] = LOBYTE(DBCSFillChar); } } } break; // // The following messages are to control IME. // case IDC_OPENIME: SteImeOpenClose( hWnd, TRUE ); goto RETURN; break; case IDC_CLOSEIME: SteImeOpenClose( hWnd, FALSE ); goto RETURN; break; case IDC_DEFAULT_POS: /* fall-through */ case IDC_NEARCARET_POS: /* fall-through */ case IDC_PREFER_POS: SteIMECompWindowPos( hWnd, cmd ); goto RETURN; break; } yPos = FIRSTROW; xPos = FIRSTCOL; InvalidateRect( hWnd, (LPRECT)NULL, TRUE ); ResetCaret(hWnd); RETURN: return; } /************************************************************************ * * IsDBCSTrailByte - returns TRUE if the given byte is a DBCS trail byte * * The algorithm searchs backward in the string, to some * known character boundary, counting consecutive bytes * in the lead byte range. An odd number indicates the * current byte is part of a two byte character code. * * INPUT: PCHAR - pointer to a preceding known character boundary. * PCHAR - pointer to the character to test. * * OUTPUT:BOOL - indicating truth of p==trailbyte. * ************************************************************************/ BOOL IsDBCSTrailByte( char *base, char *p ) { int lbc = 0; // lead byte count assert(base <= p); while ( p > base ) { if ( !IsDBCSLeadByte(*(--p)) ) { break; } lbc++; } return (lbc & 1); } /************************************************************************ * * VirtualKeyHandler - WM_KEYDOWN handler * * * INPUT: HWND - handle to the window for repainting output. * WPARAM - virtual key code. * ************************************************************************/ void VirtualKeyHandler( HWND hWnd, WPARAM wParam ) { int i; HDC hdc; static int delta = 1; switch( wParam ) { case VK_HOME: // beginning of line xPos = FIRSTCOL; break; case VK_END: // end of line xPos = LASTCOL; goto check_for_trailbyte; case VK_RIGHT: if ( IsDBCSLeadByte( textbuf[yPos][xPos] ) ) { if (xPos==LASTCOL - 1) { break; // last character don't move } xPos += 2; // skip 2 for DB Character } else { xPos = min( xPos+1, LASTCOL ); } break; case VK_LEFT: xPos = max( xPos-1, FIRSTCOL ); check_for_trailbyte: if ( IsDBCSTrailByte( (CHAR *)textbuf[yPos], (CHAR *)&(textbuf[yPos][xPos]) ) ) { xPos--; } break; case VK_UP: yPos = max( yPos-1, FIRSTROW ); goto Virtical_Check_Trail; case VK_DOWN: yPos = min( yPos+1, LASTROW ); Virtical_Check_Trail: if ( IsDBCSTrailByte( (CHAR *)textbuf[yPos], (CHAR *)&(textbuf[yPos][xPos]) ) ) { if (xPos FIRSTCOL ) { xPos--; // // DB Character so backup one more to allign on boundary // if ( IsDBCSTrailByte( (CHAR *)textbuf[yPos], (CHAR *)&(textbuf[yPos][xPos]) ) ) { xPos--; } // // Fall Through to VK_DELETE to adjust row // } else //FIRST COLUMN don't backup -- this would change for wrapping { break; } case VK_DELETE: if ( !IsDBCSLeadByte( textbuf[yPos][xPos] ) ) { // // Move rest of line left by one, then blank out last character // for ( i = xPos; i < LASTCOL; i++ ) textbuf[yPos][i] = textbuf[yPos][i+1]; textbuf[yPos][LASTCOL] = ' '; } else { // // Move line left by two bytes, blank out last two bytes // for ( i = xPos; i < LASTCOL-1; i++ ) { textbuf[yPos][i] = textbuf[yPos][i+2]; } textbuf[yPos][LASTCOL-1] = ' '; textbuf[yPos][LASTCOL] = ' '; } // // Repaint the entire line // hdc = GetDC( hWnd ); HideCaret( hWnd ); TextOutA( hdc, 0, yPos*cyMetrics, (LPCSTR)textbuf[yPos], MAXCOL ); ReleaseDC( hWnd, hdc ); break; case VK_TAB: // tab -- tabs are column allignment not character { int xTabMax = xPos + TABSTOP; int xPosPrev; do { xPosPrev = xPos; SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L ); } while ((xPos % TABSTOP) && (xPos < xTabMax) && (xPos != xPosPrev)); } break; case VK_RETURN: // linefeed yPos = min( yPos+1, LASTROW ); xPos = FIRSTCOL; break; } ResetCaret( hWnd ); } /************************************************************************ * * StoreChar - Stores one SBCS character into text buffer and advances * cursor * ************************************************************************/ void StoreChar( HWND hWnd, BYTE ch ) { int i; HDC hdc = NULL; // // If insert mode, move rest of line to the right by one // if ( fInsertMode ) { for ( i = LASTCOL; i > xPos; i-- ) { textbuf[yPos][i] = textbuf[yPos][i-1]; } // // If the row ends on a lead byte, blank it out // To do this we must first traverse the string // starting from a known character boundry until // we reach the last column. If the last column // is a character boundry then the last character // is either a single byte or a lead byte // for ( i = xPos+1; i < LASTCOL; ) { if ( IsDBCSLeadByte( textbuf[yPos][i] ) ) { i++; } i++; } if (i==LASTCOL) { if ( IsDBCSLeadByte( textbuf[yPos][LASTCOL] ) ) { textbuf[yPos][LASTCOL] = ' '; } } } else { // overtype mode if ( IsDBCSLeadByte( textbuf[yPos][xPos] ) ) { // // Blank out trail byte // textbuf[yPos][xPos+1] = ' '; // // or shift line left on character and blank last column // // for ( i = xPos+1; i < LASTCOL; i++ ) // textbuf[yPos][i] = textbuf[yPos][i+1]; // textbuf[yPos][LASTCOL] = ' '; } } // // Store input character at current caret position // textbuf[yPos][xPos] = ch; // // Display input character. // hdc = GetDC( hWnd ); HideCaret( hWnd ); TextOutA( hdc, xPos*cxMetrics, yPos*cyMetrics, (LPCSTR)&(textbuf[yPos][xPos]), MAXCOL-xPos ); ShowCaret( hWnd ); ReleaseDC( hWnd, hdc ); SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L ); } /************************************************************************ * * StoreDBCSChar - Stores one DBCS character into text buffer and * advances cursor * ************************************************************************/ void StoreDBCSChar( HWND hWnd, WORD ch ) { int i; HDC hdc; // // If there is no room for a DBCS character, discard it // if ( xPos == LASTCOL ) { return; } // // If insert mode, move rest of line to the right by two // if ( fInsertMode ) { for ( i = LASTCOL; i > xPos+1; i-- ) { textbuf[yPos][i] = textbuf[yPos][i-2]; } // // If the row ends on a lead byte, blank it out // To do this we must first traverse the string // starting from a known charcter boundry until // we reach the last column. If the last column // is not a trail byte then it is a single byte // or a lead byte // for ( i = xPos+2; i < LASTCOL; ) { if ( IsDBCSLeadByte( textbuf[yPos][i] ) ) { i++; } i++; } if (i==LASTCOL) { if (IsDBCSLeadByte( textbuf[yPos][LASTCOL] ) ) { textbuf[yPos][LASTCOL] = ' '; } } } else { // overtype mode if ( !IsDBCSLeadByte( textbuf[yPos][xPos] ) ) { // // Overtyping the current byte, plus the following byte, // which could be a lead byte. // if ( IsDBCSLeadByte( textbuf[yPos][xPos+1] ) ) { textbuf[yPos][xPos+2] = ' '; } } } // // Store input character at current caret position // textbuf[yPos][xPos] = LOBYTE(ch); // lead byte textbuf[yPos][xPos+1] = HIBYTE(ch); // trail byte // // Display input character. // hdc = GetDC( hWnd ); HideCaret( hWnd ); TextOutA( hdc, xPos*cxMetrics, yPos*cyMetrics, (LPCSTR)&(textbuf[yPos][xPos]), MAXCOL-xPos ); ShowCaret( hWnd ); ReleaseDC( hWnd, hdc ); SendMessage( hWnd, WM_KEYDOWN, VK_RIGHT, 1L ); } /************************************************************************ * * CharHandler - WM_CHAR handler * ************************************************************************/ void CharHandler( HWND hWnd, WPARAM wParam ) { unsigned char ch = (unsigned char)wParam; // // Because DBCS characters are usually generated by IMEs (as two // PostMessages), if a lead byte comes in, the trail byte should // arrive very soon after. We wait here for the trail byte and // store them into the text buffer together. if ( IsDBCSLeadByte( ch ) ) { // // Wait an arbitrary amount of time for the trail byte to // arrive. If it doesn't, then discard the lead byte. // // This could happen if the IME screwed up. Or, more likely, // the user generated the lead byte through ALT-numpad. // MSG msg = {0}; int i = 10; while (!PeekMessage((LPMSG)&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE)) { if ( --i == 0 ) { return; } Yield(); } StoreDBCSChar( hWnd, (WORD)(((unsigned)(msg.wParam)<<8) | (unsigned)ch )); } else { switch( ch ) { case '\r': /* fall-through */ case '\t': /* fall-through */ case '\b': // // Throw away. Already handled at WM_KEYDOWN time. // break; default: StoreChar( hWnd, ch ); break; } } } /************************************************************************ * * MouseHandler - WM_BUTTONDOWN handler * ************************************************************************/ void MouseHandler( HWND hWnd, LPARAM lParam ) { HideCaret( hWnd ); // // Calculate caret position based on fixed pitched font // yPos = MAKEPOINTS(lParam).y / cyMetrics; xPos = MAKEPOINTS(lParam).x / cxMetrics; // // Adjust caret position if click landed on a trail byte // if ( IsDBCSTrailByte( (CHAR *)textbuf[yPos], (CHAR *)&(textbuf[yPos][xPos]) ) ) { // // If click landed on the last quarter of the DBCS character, // assume the user was aiming at the next character. // if ( (MAKEPOINTS(lParam).x - xPos * cxMetrics) > (cxMetrics / 2) ) { xPos++; } else { xPos--; } } DestroyCaret(); CreateCaret(hWnd, NULL, (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ? CaretWidth*2 : CaretWidth, cyMetrics ); SetCaretPos( xPos * cxMetrics, yPos * cyMetrics ); ShowCaret( hWnd ); SetIMECompFormPos( hWnd ); } /************************************************************************ * * InputChangeHandler - WM_INPUTLANGCHANGE handler * ************************************************************************/ void InputChangeHandler( HWND hWnd ) { HIMC hIMC; fdwProperty = ImmGetProperty( GetKeyboardLayout(0), IGP_PROPERTY ); // if this application set the candidate position, it need to set // it to default for the near caret IME if ( hIMC = ImmGetContext( hWnd ) ) { UINT i; for (i = 0; i < 4; i++) { CANDIDATEFORM CandForm = {0}; if ( fdwProperty & IME_PROP_AT_CARET ) { CandForm.dwIndex = i; CandForm.dwStyle = CFS_CANDIDATEPOS; #if 0 // This application do not want to set candidate window to // any position. Anyway, if an application need to set the // candiadet position, it should remove the if 0 code // the position you want to set CandForm.ptCurrentPos.x = ptAppWantPosition[i].x; CandForm.ptCurrentPos.y = ptAppWantPosition[i].y; ImmSetCandidateWindow( hIMC, &CandForm ); #endif } else { if ( !ImmGetCandidateWindow( hIMC, i, &CandForm ) ) { continue; } if ( CandForm.dwStyle == CFS_DEFAULT ) { continue; } CandForm.dwStyle = CFS_DEFAULT; ImmSetCandidateWindow( hIMC, &CandForm ); } } ImmReleaseContext( hWnd, hIMC ); } return; } /************************************************************************ * * SteWndProc - STE class window procedure * ************************************************************************/ LRESULT CALLBACK SteWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { int i = 0; HDC hdc = NULL; PAINTSTRUCT ps = {0}; BOOL bRetDWP = FALSE; switch( msg ) { case WM_CREATE: SteCreate( hWnd ); break; case WM_MOVE: ResetIMECompWin( hWnd ); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_CLOSE: DestroyWindow( hWnd ); break; case WM_SETFOCUS: CreateCaret(hWnd, NULL, (fInsertMode && IsDBCSLeadByte( textbuf[yPos][xPos] )) ? CaretWidth*2 : CaretWidth, cyMetrics ); SetCaretPos( xPos * cxMetrics, yPos * cyMetrics ); ShowCaret( hWnd ); break; case WM_KILLFOCUS: HideCaret( hWnd ); DestroyCaret(); break; case WM_KEYDOWN: VirtualKeyHandler( hWnd, wParam ); break; case WM_CHAR: CharHandler( hWnd, wParam ); break; case WM_LBUTTONDOWN: MouseHandler( hWnd, lParam ); break; case WM_COMMAND: SteCommand( hWnd, wParam ); break; case WM_PAINT: InvalidateRect(hWnd,NULL,FALSE); hdc = BeginPaint( hWnd, &ps ); // // Refresh display from text buffer // for ( i = FIRSTROW; i <= LASTROW; i++ ) { TextOutA( hdc, 0, i*cyMetrics, (LPCSTR)textbuf[i], MAXCOL ); } EndPaint( hWnd, &ps ); break; case WM_INPUTLANGCHANGE: InputChangeHandler( hWnd ); bRetDWP = TRUE; break; default: bRetDWP = TRUE; break; } if (bRetDWP) { return DefWindowProc( hWnd, msg, wParam, lParam ); } else { return 0; } }