DXUTgui.cpp
上传用户:junlon
上传日期:2022-01-05
资源大小:39075k
文件大小:284k
源码类别:

DirextX编程

开发平台:

Visual C++

  1.                     // Invoke the callback when the user presses Enter.
  2.                     m_pDialog->SendEvent( EVENT_EDITBOX_STRING, true, this );
  3.                     break;
  4.                 // Junk characters we don't want in the string
  5.                 case 26:  // Ctrl Z
  6.                 case 2:   // Ctrl B
  7.                 case 14:  // Ctrl N
  8.                 case 19:  // Ctrl S
  9.                 case 4:   // Ctrl D
  10.                 case 6:   // Ctrl F
  11.                 case 7:   // Ctrl G
  12.                 case 10:  // Ctrl J
  13.                 case 11:  // Ctrl K
  14.                 case 12:  // Ctrl L
  15.                 case 17:  // Ctrl Q
  16.                 case 23:  // Ctrl W
  17.                 case 5:   // Ctrl E
  18.                 case 18:  // Ctrl R
  19.                 case 20:  // Ctrl T
  20.                 case 25:  // Ctrl Y
  21.                 case 21:  // Ctrl U
  22.                 case 9:   // Ctrl I
  23.                 case 15:  // Ctrl O
  24.                 case 16:  // Ctrl P
  25.                 case 27:  // Ctrl [
  26.                 case 29:  // Ctrl ]
  27.                 case 28:  // Ctrl  
  28.                     break;
  29.                 default:
  30.                 {
  31.                     // If there's a selection and the user
  32.                     // starts to type, the selection should
  33.                     // be deleted.
  34.                     if( m_nCaret != m_nSelStart )
  35.                         DeleteSelectionText();
  36.                     // If we are in overwrite mode and there is already
  37.                     // a char at the caret's position, simply replace it.
  38.                     // Otherwise, we insert the char as normal.
  39.                     if( !m_bInsertMode && m_nCaret < m_Buffer.GetTextSize() )
  40.                     {
  41.                         m_Buffer[m_nCaret] = (WCHAR)wParam;
  42.                         PlaceCaret( m_nCaret + 1 );
  43.                         m_nSelStart = m_nCaret;
  44.                     } else
  45.                     {
  46.                         // Insert the char
  47.                         if( m_Buffer.InsertChar( m_nCaret, (WCHAR)wParam ) )
  48.                         {
  49.                             PlaceCaret( m_nCaret + 1 );
  50.                             m_nSelStart = m_nCaret;
  51.                         }
  52.                     }
  53.                     ResetCaretBlink();
  54.                     m_pDialog->SendEvent( EVENT_EDITBOX_CHANGE, true, this );
  55.                 }
  56.             }
  57.             return true;
  58.         }
  59.     }
  60.     return false;
  61. }
  62. //--------------------------------------------------------------------------------------
  63. void CDXUTEditBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime )
  64. {
  65.     if( m_bVisible == false )
  66.         return;
  67.     HRESULT hr;
  68.     int nSelStartX = 0, nCaretX = 0;  // Left and right X cordinates of the selection region
  69.     CDXUTElement* pElement = GetElement( 0 );
  70.     if( pElement )
  71.     {
  72.         m_Buffer.SetFontNode( m_pDialog->GetFont( pElement->iFont ) );
  73.         PlaceCaret( m_nCaret );  // Call PlaceCaret now that we have the font info (node),
  74.                                  // so that scrolling can be handled.
  75.     }
  76.     // Render the control graphics
  77.     for( int e = 0; e < 9; ++e )
  78.     {
  79.         pElement = m_Elements.GetAt( e );
  80.         pElement->TextureColor.Blend( DXUT_STATE_NORMAL, fElapsedTime );
  81.         m_pDialog->DrawSprite( pElement, &m_rcRender[e] );
  82.     }
  83.     //
  84.     // Compute the X coordinates of the first visible character.
  85.     //
  86.     int nXFirst;
  87.     m_Buffer.CPtoX( m_nFirstVisible, FALSE, &nXFirst );
  88.     //
  89.     // Compute the X coordinates of the selection rectangle
  90.     //
  91.     hr = m_Buffer.CPtoX( m_nCaret, FALSE, &nCaretX );
  92.     if( m_nCaret != m_nSelStart )
  93.         hr = m_Buffer.CPtoX( m_nSelStart, FALSE, &nSelStartX );
  94.     else
  95.         nSelStartX = nCaretX;
  96.     //
  97.     // Render the selection rectangle
  98.     //
  99.     RECT rcSelection;  // Make this available for rendering selected text
  100.     if( m_nCaret != m_nSelStart )
  101.     {
  102.         int nSelLeftX = nCaretX, nSelRightX = nSelStartX;
  103.         // Swap if left is bigger than right
  104.         if( nSelLeftX > nSelRightX )
  105.             { int nTemp = nSelLeftX; nSelLeftX = nSelRightX; nSelRightX = nTemp; }
  106.         SetRect( &rcSelection, nSelLeftX, m_rcText.top, nSelRightX, m_rcText.bottom );
  107.         OffsetRect( &rcSelection, m_rcText.left - nXFirst, 0 );
  108.         IntersectRect( &rcSelection, &m_rcText, &rcSelection );
  109.         pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
  110.         m_pDialog->DrawRect( &rcSelection, m_SelBkColor );
  111.         pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
  112.     }
  113.     //
  114.     // Render the text
  115.     //
  116.     // Element 0 for text
  117.     m_Elements.GetAt( 0 )->FontColor.Current = m_TextColor;
  118.     m_pDialog->DrawText( m_Buffer.GetBuffer() + m_nFirstVisible, m_Elements.GetAt( 0 ), &m_rcText );
  119.     // Render the selected text
  120.     if( m_nCaret != m_nSelStart )
  121.     {
  122.         int nFirstToRender = __max( m_nFirstVisible, __min( m_nSelStart, m_nCaret ) );
  123.         int nNumChatToRender = __max( m_nSelStart, m_nCaret ) - nFirstToRender;
  124.         m_Elements.GetAt( 0 )->FontColor.Current = m_SelTextColor;
  125.         m_pDialog->DrawText( m_Buffer.GetBuffer() + nFirstToRender,
  126.                              m_Elements.GetAt( 0 ), &rcSelection, false, nNumChatToRender );
  127.     }
  128.     //
  129.     // Blink the caret
  130.     //
  131.     if( DXUTGetGlobalTimer()->GetAbsoluteTime() - m_dfLastBlink >= m_dfBlink )
  132.     {
  133.         m_bCaretOn = !m_bCaretOn;
  134.         m_dfLastBlink = DXUTGetGlobalTimer()->GetAbsoluteTime();
  135.     }
  136.     //
  137.     // Render the caret if this control has the focus
  138.     //
  139.     if( m_bHasFocus && m_bCaretOn && !s_bHideCaret )
  140.     {
  141.         // Start the rectangle with insert mode caret
  142.         RECT rcCaret = { m_rcText.left - nXFirst + nCaretX - 1, m_rcText.top,
  143.                          m_rcText.left - nXFirst + nCaretX + 1, m_rcText.bottom };
  144.         // If we are in overwrite mode, adjust the caret rectangle
  145.         // to fill the entire character.
  146.         if( !m_bInsertMode )
  147.         {
  148.             // Obtain the right edge X coord of the current character
  149.             int nRightEdgeX;
  150.             m_Buffer.CPtoX( m_nCaret, TRUE, &nRightEdgeX );
  151.             rcCaret.right = m_rcText.left - nXFirst + nRightEdgeX;
  152.         }
  153.         m_pDialog->DrawRect( &rcCaret, m_CaretColor );
  154.     }
  155. }
  156. #define IN_FLOAT_CHARSET( c ) 
  157.     ( (c) == L'-' || (c) == L'.' || ( (c) >= L'0' && (c) <= L'9' ) )
  158. void CDXUTEditBox::ParseFloatArray( float *pNumbers, int nCount )
  159. {
  160.     int nWritten = 0;  // Number of floats written
  161.     const WCHAR *pToken, *pEnd;
  162.     WCHAR wszToken[60];
  163.     pToken = m_Buffer.GetBuffer();
  164.     while( nWritten < nCount && *pToken != L'' )
  165.     {
  166.         // Skip leading spaces
  167.         while( *pToken == L' ' )
  168.             ++pToken;
  169.         if( *pToken == L'' )
  170.             break;
  171.         // Locate the end of number
  172.         pEnd = pToken;
  173.         while( IN_FLOAT_CHARSET( *pEnd ) )
  174.             ++pEnd;
  175.         // Copy the token to our buffer
  176.         int nTokenLen = __min( sizeof(wszToken) / sizeof(wszToken[0]) - 1, int(pEnd - pToken) );
  177.         StringCchCopy( wszToken, nTokenLen, pToken );
  178.         *pNumbers = (float)wcstod( wszToken, NULL );
  179.         ++nWritten;
  180.         ++pNumbers;
  181.         pToken = pEnd;
  182.     }
  183. }
  184. void CDXUTEditBox::SetTextFloatArray( const float *pNumbers, int nCount )
  185. {
  186.     WCHAR wszBuffer[512] = {0};
  187.     WCHAR wszTmp[64];
  188.     
  189.     if( pNumbers == NULL )
  190.         return;
  191.         
  192.     for( int i = 0; i < nCount; ++i )
  193.     {
  194.         StringCchPrintf( wszTmp, 64, L"%.4f ", pNumbers[i] );
  195.         StringCchCat( wszBuffer, 512, wszTmp );
  196.     }
  197.     // Don't want the last space
  198.     if( nCount > 0 && wcslen(wszBuffer) > 0 )
  199.         wszBuffer[wcslen(wszBuffer)-1] = 0;
  200.     SetText( wszBuffer );
  201. }
  202. //--------------------------------------------------------------------------------------
  203. // CDXUTIMEEditBox class
  204. //--------------------------------------------------------------------------------------
  205. // IME constants
  206. #define CHT_IMEFILENAME1    "TINTLGNT.IME" // New Phonetic
  207. #define CHT_IMEFILENAME2    "CINTLGNT.IME" // New Chang Jie
  208. #define CHT_IMEFILENAME3    "MSTCIPHA.IME" // Phonetic 5.1
  209. #define CHS_IMEFILENAME1    "PINTLGNT.IME" // MSPY1.5/2/3
  210. #define CHS_IMEFILENAME2    "MSSCIPYA.IME" // MSPY3 for OfficeXP
  211. #define LANG_CHT            MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
  212. #define LANG_CHS            MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
  213. #define _CHT_HKL            ( (HKL)(INT_PTR)0xE0080404 ) // New Phonetic
  214. #define _CHT_HKL2           ( (HKL)(INT_PTR)0xE0090404 ) // New Chang Jie
  215. #define _CHS_HKL            ( (HKL)(INT_PTR)0xE00E0804 ) // MSPY
  216. #define MAKEIMEVERSION( major, minor )      ( (DWORD)( ( (BYTE)( major ) << 24 ) | ( (BYTE)( minor ) << 16 ) ) )
  217. #define IMEID_CHT_VER42 ( LANG_CHT | MAKEIMEVERSION( 4, 2 ) )   // New(Phonetic/ChanJie)IME98  : 4.2.x.x // Win98
  218. #define IMEID_CHT_VER43 ( LANG_CHT | MAKEIMEVERSION( 4, 3 ) )   // New(Phonetic/ChanJie)IME98a : 4.3.x.x // Win2k
  219. #define IMEID_CHT_VER44 ( LANG_CHT | MAKEIMEVERSION( 4, 4 ) )   // New ChanJie IME98b          : 4.4.x.x // WinXP
  220. #define IMEID_CHT_VER50 ( LANG_CHT | MAKEIMEVERSION( 5, 0 ) )   // New(Phonetic/ChanJie)IME5.0 : 5.0.x.x // WinME
  221. #define IMEID_CHT_VER51 ( LANG_CHT | MAKEIMEVERSION( 5, 1 ) )   // New(Phonetic/ChanJie)IME5.1 : 5.1.x.x // IME2002(w/OfficeXP)
  222. #define IMEID_CHT_VER52 ( LANG_CHT | MAKEIMEVERSION( 5, 2 ) )   // New(Phonetic/ChanJie)IME5.2 : 5.2.x.x // IME2002a(w/Whistler)
  223. #define IMEID_CHT_VER60 ( LANG_CHT | MAKEIMEVERSION( 6, 0 ) )   // New(Phonetic/ChanJie)IME6.0 : 6.0.x.x // IME XP(w/WinXP SP1)
  224. #define IMEID_CHS_VER41 ( LANG_CHS | MAKEIMEVERSION( 4, 1 ) )   // MSPY1.5  // SCIME97 or MSPY1.5 (w/Win98, Office97)
  225. #define IMEID_CHS_VER42 ( LANG_CHS | MAKEIMEVERSION( 4, 2 ) )   // MSPY2    // Win2k/WinME
  226. #define IMEID_CHS_VER53 ( LANG_CHS | MAKEIMEVERSION( 5, 3 ) )   // MSPY3    // WinXP
  227. // Function pointers
  228. INPUTCONTEXT* (WINAPI * CDXUTIMEEditBox::_ImmLockIMC)( HIMC ) = CDXUTIMEEditBox::Dummy_ImmLockIMC;
  229. BOOL (WINAPI * CDXUTIMEEditBox::_ImmUnlockIMC)( HIMC ) = CDXUTIMEEditBox::Dummy_ImmUnlockIMC;
  230. LPVOID (WINAPI * CDXUTIMEEditBox::_ImmLockIMCC)( HIMCC ) = CDXUTIMEEditBox::Dummy_ImmLockIMCC;
  231. BOOL (WINAPI * CDXUTIMEEditBox::_ImmUnlockIMCC)( HIMCC ) = CDXUTIMEEditBox::Dummy_ImmUnlockIMCC;
  232. BOOL (WINAPI * CDXUTIMEEditBox::_ImmDisableTextFrameService)( DWORD ) = CDXUTIMEEditBox::Dummy_ImmDisableTextFrameService;
  233. LONG (WINAPI * CDXUTIMEEditBox::_ImmGetCompositionStringW)( HIMC, DWORD, LPVOID, DWORD ) = CDXUTIMEEditBox::Dummy_ImmGetCompositionStringW;
  234. DWORD (WINAPI * CDXUTIMEEditBox::_ImmGetCandidateListW)( HIMC, DWORD, LPCANDIDATELIST, DWORD ) = CDXUTIMEEditBox::Dummy_ImmGetCandidateListW;
  235. HIMC (WINAPI * CDXUTIMEEditBox::_ImmGetContext)( HWND ) = CDXUTIMEEditBox::Dummy_ImmGetContext;
  236. BOOL (WINAPI * CDXUTIMEEditBox::_ImmReleaseContext)( HWND, HIMC ) = CDXUTIMEEditBox::Dummy_ImmReleaseContext;
  237. HIMC (WINAPI * CDXUTIMEEditBox::_ImmAssociateContext)( HWND, HIMC ) = CDXUTIMEEditBox::Dummy_ImmAssociateContext;
  238. BOOL (WINAPI * CDXUTIMEEditBox::_ImmGetOpenStatus)( HIMC ) = CDXUTIMEEditBox::Dummy_ImmGetOpenStatus;
  239. BOOL (WINAPI * CDXUTIMEEditBox::_ImmSetOpenStatus)( HIMC, BOOL ) = CDXUTIMEEditBox::Dummy_ImmSetOpenStatus;
  240. BOOL (WINAPI * CDXUTIMEEditBox::_ImmGetConversionStatus)( HIMC, LPDWORD, LPDWORD ) = CDXUTIMEEditBox::Dummy_ImmGetConversionStatus;
  241. HWND (WINAPI * CDXUTIMEEditBox::_ImmGetDefaultIMEWnd)( HWND ) = CDXUTIMEEditBox::Dummy_ImmGetDefaultIMEWnd;
  242. UINT (WINAPI * CDXUTIMEEditBox::_ImmGetIMEFileNameA)( HKL, LPSTR, UINT ) = CDXUTIMEEditBox::Dummy_ImmGetIMEFileNameA;
  243. UINT (WINAPI * CDXUTIMEEditBox::_ImmGetVirtualKey)( HWND ) = CDXUTIMEEditBox::Dummy_ImmGetVirtualKey;
  244. BOOL (WINAPI * CDXUTIMEEditBox::_ImmNotifyIME)( HIMC, DWORD, DWORD, DWORD ) = CDXUTIMEEditBox::Dummy_ImmNotifyIME;
  245. BOOL (WINAPI * CDXUTIMEEditBox::_ImmSetConversionStatus)( HIMC, DWORD, DWORD ) = CDXUTIMEEditBox::Dummy_ImmSetConversionStatus;
  246. BOOL (WINAPI * CDXUTIMEEditBox::_ImmSimulateHotKey)( HWND, DWORD ) = CDXUTIMEEditBox::Dummy_ImmSimulateHotKey;
  247. BOOL (WINAPI * CDXUTIMEEditBox::_ImmIsIME)( HKL ) = CDXUTIMEEditBox::Dummy_ImmIsIME;
  248. UINT (WINAPI * CDXUTIMEEditBox::_GetReadingString)( HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT ) = CDXUTIMEEditBox::Dummy_GetReadingString; // Traditional Chinese IME
  249. BOOL (WINAPI * CDXUTIMEEditBox::_ShowReadingWindow)( HIMC, BOOL ) = CDXUTIMEEditBox::Dummy_ShowReadingWindow; // Traditional Chinese IME
  250. BOOL (APIENTRY * CDXUTIMEEditBox::_VerQueryValueA)( const LPVOID, LPSTR, LPVOID *, PUINT ) = CDXUTIMEEditBox::Dummy_VerQueryValueA;
  251. BOOL (APIENTRY * CDXUTIMEEditBox::_GetFileVersionInfoA)( LPSTR, DWORD, DWORD, LPVOID )= CDXUTIMEEditBox::Dummy_GetFileVersionInfoA;
  252. DWORD (APIENTRY * CDXUTIMEEditBox::_GetFileVersionInfoSizeA)( LPSTR, LPDWORD ) = CDXUTIMEEditBox::Dummy_GetFileVersionInfoSizeA;
  253. HINSTANCE CDXUTIMEEditBox::s_hDllImm32;      // IMM32 DLL handle
  254. HINSTANCE CDXUTIMEEditBox::s_hDllVer;        // Version DLL handle
  255. HKL       CDXUTIMEEditBox::s_hklCurrent;     // Current keyboard layout of the process
  256. bool      CDXUTIMEEditBox::s_bVerticalCand;  // Indicates that the candidates are listed vertically
  257. WCHAR     CDXUTIMEEditBox::s_aszIndicator[5][3] = // String to draw to indicate current input locale
  258.             {
  259.                 L"En",
  260.                 L"x7B80",
  261.                 L"x7E41",
  262.                 L"xAC00",
  263.                 L"x3042",
  264.             };
  265. LPWSTR    CDXUTIMEEditBox::s_wszCurrIndicator = CDXUTIMEEditBox::s_aszIndicator[0];  // Points to an indicator string that corresponds to current input locale
  266. bool      CDXUTIMEEditBox::s_bInsertOnType;     // Insert the character as soon as a key is pressed (Korean behavior)
  267. HINSTANCE CDXUTIMEEditBox::s_hDllIme;           // Instance handle of the current IME module
  268. HIMC      CDXUTIMEEditBox::s_hImcDef;           // Default input context
  269. CDXUTIMEEditBox::IMESTATE  CDXUTIMEEditBox::s_ImeState = IMEUI_STATE_OFF;
  270. bool      CDXUTIMEEditBox::s_bEnableImeSystem;  // Whether the IME system is active
  271. POINT     CDXUTIMEEditBox::s_ptCompString;      // Composition string position. Updated every frame.
  272. int       CDXUTIMEEditBox::s_nCompCaret;
  273. int       CDXUTIMEEditBox::s_nFirstTargetConv;  // Index of the first target converted char in comp string.  If none, -1.
  274. CUniBuffer CDXUTIMEEditBox::s_CompString = CUniBuffer( 0 );
  275. BYTE      CDXUTIMEEditBox::s_abCompStringAttr[MAX_COMPSTRING_SIZE];
  276. DWORD     CDXUTIMEEditBox::s_adwCompStringClause[MAX_COMPSTRING_SIZE];
  277. WCHAR     CDXUTIMEEditBox::s_wszReadingString[32];
  278. CDXUTIMEEditBox::CCandList CDXUTIMEEditBox::s_CandList;       // Data relevant to the candidate list
  279. bool      CDXUTIMEEditBox::s_bShowReadingWindow; // Indicates whether reading window is visible
  280. bool      CDXUTIMEEditBox::s_bHorizontalReading; // Indicates whether the reading window is vertical or horizontal
  281. bool      CDXUTIMEEditBox::s_bChineseIME;
  282. CGrowableArray< CDXUTIMEEditBox::CInputLocale > CDXUTIMEEditBox::s_Locale; // Array of loaded keyboard layout on system
  283. #if defined(DEBUG) || defined(_DEBUG)
  284. bool      CDXUTIMEEditBox::m_bIMEStaticMsgProcCalled = false;
  285. #endif
  286. //--------------------------------------------------------------------------------------
  287. CDXUTIMEEditBox::CDXUTIMEEditBox( CDXUTDialog *pDialog )
  288. {
  289.     CDXUTIMEEditBox::Initialize(); // ensure static vars are properly init'ed first
  290.     _ImmDisableTextFrameService( (DWORD)-1 );  // Disable TSF for the current process
  291.     m_Type = DXUT_CONTROL_IMEEDITBOX;
  292.     m_pDialog = pDialog;
  293.     s_bEnableImeSystem = true;
  294.     m_nIndicatorWidth = 0;
  295.     m_ReadingColor = D3DCOLOR_ARGB( 188, 255, 255, 255 );
  296.     m_ReadingWinColor = D3DCOLOR_ARGB( 128, 0, 0, 0 );
  297.     m_ReadingSelColor = D3DCOLOR_ARGB( 255, 255, 0, 0 );
  298.     m_ReadingSelBkColor = D3DCOLOR_ARGB( 128, 80, 80, 80 );
  299.     m_CandidateColor = D3DCOLOR_ARGB( 255, 200, 200, 200 );
  300.     m_CandidateWinColor = D3DCOLOR_ARGB( 128, 0, 0, 0 );
  301.     m_CandidateSelColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
  302.     m_CandidateSelBkColor = D3DCOLOR_ARGB( 128, 158, 158, 158 );
  303.     m_CompColor = D3DCOLOR_ARGB( 255, 200, 200, 255 );
  304.     m_CompWinColor = D3DCOLOR_ARGB( 198, 0, 0, 0 );
  305.     m_CompCaretColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
  306.     m_CompTargetColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
  307.     m_CompTargetBkColor = D3DCOLOR_ARGB( 255, 150, 150, 150 );
  308.     m_CompTargetNonColor = D3DCOLOR_ARGB( 255, 255, 255, 0 );
  309.     m_CompTargetNonBkColor = D3DCOLOR_ARGB( 255, 150, 150, 150 );
  310.     m_IndicatorImeColor = D3DCOLOR_ARGB( 255, 255, 255, 255 );
  311.     m_IndicatorEngColor = D3DCOLOR_ARGB( 255, 0, 0, 0 );
  312.     m_IndicatorBkColor = D3DCOLOR_ARGB( 255, 128, 128, 128 );
  313. }
  314. //--------------------------------------------------------------------------------------
  315. CDXUTIMEEditBox::~CDXUTIMEEditBox()
  316. {
  317. }
  318. //--------------------------------------------------------------------------------------
  319. void CDXUTIMEEditBox::SendKey( BYTE nVirtKey )
  320. {
  321.     keybd_event( nVirtKey, 0, 0,               0 );
  322.     keybd_event( nVirtKey, 0, KEYEVENTF_KEYUP, 0 );
  323. }
  324. //--------------------------------------------------------------------------------------
  325. // Called by CDXUTDialogResourceManager::OnCreateDevice.  This gives the class a
  326. // chance to initialize its default input context associated with the app window.
  327. HRESULT CDXUTIMEEditBox::StaticOnCreateDevice()
  328. {
  329.     // Save the default input context
  330.     s_hImcDef = _ImmGetContext( DXUTGetHWND() );
  331.     _ImmReleaseContext( DXUTGetHWND(), s_hImcDef );
  332.     return S_OK;
  333. }
  334. //--------------------------------------------------------------------------------------
  335. void CDXUTIMEEditBox::UpdateRects()
  336. {
  337.     // Temporary adjust m_width so that CDXUTEditBox can compute
  338.     // the correct rects for its rendering since we need to make space
  339.     // for the indicator button
  340.     int nWidth = m_width;
  341.     m_width -= m_nIndicatorWidth + m_nBorder * 2; // Make room for the indicator button
  342.     CDXUTEditBox::UpdateRects();
  343.     m_width = nWidth;  // Restore
  344.     // Compute the indicator button rectangle
  345.     SetRect( &m_rcIndicator, m_rcBoundingBox.right, m_rcBoundingBox.top, m_x + m_width, m_rcBoundingBox.bottom );
  346. //    InflateRect( &m_rcIndicator, -m_nBorder, -m_nBorder );
  347.     m_rcBoundingBox.right = m_rcBoundingBox.left + m_width;
  348. }
  349. //--------------------------------------------------------------------------------------
  350. //  GetImeId( UINT uIndex )
  351. //      returns 
  352. //  returned value:
  353. //  0: In the following cases
  354. //      - Non Chinese IME input locale
  355. //      - Older Chinese IME
  356. //      - Other error cases
  357. //
  358. //  Othewise:
  359. //      When uIndex is 0 (default)
  360. //          bit 31-24:  Major version
  361. //          bit 23-16:  Minor version
  362. //          bit 15-0:   Language ID
  363. //      When uIndex is 1
  364. //          pVerFixedInfo->dwFileVersionLS
  365. //
  366. //  Use IMEID_VER and IMEID_LANG macro to extract version and language information.
  367. //  
  368. // We define the locale-invariant ID ourselves since it doesn't exist prior to WinXP
  369. // For more information, see the CompareString() reference.
  370. #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
  371. DWORD CDXUTIMEEditBox::GetImeId( UINT uIndex )
  372. {
  373.     static HKL hklPrev = 0;
  374.     static DWORD dwID[2] = { 0, 0 };  // Cache the result
  375.     
  376.     DWORD   dwVerSize;
  377.     DWORD   dwVerHandle;
  378.     LPVOID  lpVerBuffer;
  379.     LPVOID  lpVerData;
  380.     UINT    cbVerData;
  381.     char    szTmp[1024];
  382.     if( uIndex >= sizeof( dwID ) / sizeof( dwID[0] ) )
  383.         return 0;
  384.     if( hklPrev == s_hklCurrent )
  385.         return dwID[uIndex];
  386.     hklPrev = s_hklCurrent;  // Save for the next invocation
  387.     // Check if we are using an older Chinese IME
  388.     if( !( ( s_hklCurrent == _CHT_HKL ) || ( s_hklCurrent == _CHT_HKL2 ) || ( s_hklCurrent == _CHS_HKL ) ) )
  389.     {
  390.         dwID[0] = dwID[1] = 0;
  391.         return dwID[uIndex];
  392.     }
  393.     // Obtain the IME file name
  394.     if ( !_ImmGetIMEFileNameA( s_hklCurrent, szTmp, ( sizeof(szTmp) / sizeof(szTmp[0]) ) - 1 ) )
  395.     {
  396.         dwID[0] = dwID[1] = 0;
  397.         return dwID[uIndex];
  398.     }
  399.     // Check for IME that doesn't implement reading string API
  400.     if ( !_GetReadingString )
  401.     {
  402.         if( ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME1, -1 ) != CSTR_EQUAL ) &&
  403.             ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME2, -1 ) != CSTR_EQUAL ) &&
  404.             ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME3, -1 ) != CSTR_EQUAL ) &&
  405.             ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME1, -1 ) != CSTR_EQUAL ) &&
  406.             ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME2, -1 ) != CSTR_EQUAL ) )
  407.         {
  408.             dwID[0] = dwID[1] = 0;
  409.             return dwID[uIndex];
  410.         }
  411.     }
  412.     dwVerSize = _GetFileVersionInfoSizeA( szTmp, &dwVerHandle );
  413.     if( dwVerSize )
  414.     {
  415.         lpVerBuffer = HeapAlloc( GetProcessHeap(), 0, dwVerSize );
  416.         if( lpVerBuffer )
  417.         {
  418.             if( _GetFileVersionInfoA( szTmp, dwVerHandle, dwVerSize, lpVerBuffer ) )
  419.             {
  420.                 if( _VerQueryValueA( lpVerBuffer, "\", &lpVerData, &cbVerData ) )
  421.                 {
  422.                     DWORD dwVer = ( (VS_FIXEDFILEINFO*)lpVerData )->dwFileVersionMS;
  423.                     dwVer = ( dwVer & 0x00ff0000 ) << 8 | ( dwVer & 0x000000ff ) << 16;
  424.                     if( _GetReadingString
  425.                         ||
  426.                         ( GetLanguage() == LANG_CHT &&
  427.                           ( dwVer == MAKEIMEVERSION(4, 2) || 
  428.                             dwVer == MAKEIMEVERSION(4, 3) || 
  429.                             dwVer == MAKEIMEVERSION(4, 4) || 
  430.                             dwVer == MAKEIMEVERSION(5, 0) ||
  431.                             dwVer == MAKEIMEVERSION(5, 1) ||
  432.                             dwVer == MAKEIMEVERSION(5, 2) ||
  433.                             dwVer == MAKEIMEVERSION(6, 0) ) )
  434.                         ||
  435.                         ( GetLanguage() == LANG_CHS &&
  436.                           ( dwVer == MAKEIMEVERSION(4, 1) ||
  437.                             dwVer == MAKEIMEVERSION(4, 2) ||
  438.                             dwVer == MAKEIMEVERSION(5, 3) ) )
  439.                       )
  440.                     {
  441.                         dwID[0] = dwVer | GetLanguage();
  442.                         dwID[1] = ( (VS_FIXEDFILEINFO*)lpVerData )->dwFileVersionLS;
  443.                     }
  444.                 }
  445.             }
  446.             HeapFree( GetProcessHeap(), 0, lpVerBuffer );
  447.         }
  448.     }
  449.     return dwID[uIndex];
  450. }
  451. //--------------------------------------------------------------------------------------
  452. void CDXUTIMEEditBox::CheckInputLocale()
  453. {
  454.     static HKL hklPrev = 0;
  455.     s_hklCurrent = GetKeyboardLayout( 0 );
  456.     if ( hklPrev == s_hklCurrent )
  457.         return;
  458.     hklPrev = s_hklCurrent;
  459.     switch ( GetPrimaryLanguage() )
  460.     {
  461.         // Simplified Chinese
  462.         case LANG_CHINESE:
  463.             s_bVerticalCand = true;
  464.             switch ( GetSubLanguage() )
  465.             {
  466.                 case SUBLANG_CHINESE_SIMPLIFIED:
  467.                     s_wszCurrIndicator = s_aszIndicator[INDICATOR_CHS];
  468.                     s_bVerticalCand = GetImeId() == 0;
  469.                     break;
  470.                 case SUBLANG_CHINESE_TRADITIONAL:
  471.                     s_wszCurrIndicator = s_aszIndicator[INDICATOR_CHT];
  472.                     break;
  473.                 default:    // unsupported sub-language
  474.                     s_wszCurrIndicator = s_aszIndicator[INDICATOR_NON_IME];
  475.                     break;
  476.             }
  477.             break;
  478.         // Korean
  479.         case LANG_KOREAN:
  480.             s_wszCurrIndicator = s_aszIndicator[INDICATOR_KOREAN];
  481.             s_bVerticalCand = false;
  482.             break;
  483.         // Japanese
  484.         case LANG_JAPANESE:
  485.             s_wszCurrIndicator = s_aszIndicator[INDICATOR_JAPANESE];
  486.             s_bVerticalCand = true;
  487.             break;
  488.         default:
  489.             // A non-IME language.  Obtain the language abbreviation
  490.             // and store it for rendering the indicator later.
  491.             s_wszCurrIndicator = s_aszIndicator[INDICATOR_NON_IME];
  492.     }
  493.     // If non-IME, use the language abbreviation.
  494.     if( s_wszCurrIndicator == s_aszIndicator[INDICATOR_NON_IME] )
  495.     {
  496.         WCHAR wszLang[5];
  497.         GetLocaleInfoW( MAKELCID( LOWORD( s_hklCurrent ), SORT_DEFAULT ), LOCALE_SABBREVLANGNAME, wszLang, 5 );
  498.         s_wszCurrIndicator[0] = wszLang[0];
  499.         s_wszCurrIndicator[1] = towlower( wszLang[1] );
  500.     }
  501. }
  502. //--------------------------------------------------------------------------------------
  503. void CDXUTIMEEditBox::CheckToggleState()
  504. {
  505.     CheckInputLocale();
  506.     bool bIme = _ImmIsIME( s_hklCurrent ) != 0;
  507.     s_bChineseIME = ( GetPrimaryLanguage() == LANG_CHINESE ) && bIme;
  508.     HIMC hImc;
  509.     if( NULL != ( hImc = _ImmGetContext( DXUTGetHWND() ) ) )
  510.     {
  511.         if( s_bChineseIME )
  512.         {
  513.             DWORD dwConvMode, dwSentMode;
  514.             _ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode );
  515.             s_ImeState = ( dwConvMode & IME_CMODE_NATIVE ) ? IMEUI_STATE_ON : IMEUI_STATE_ENGLISH;
  516.         }
  517.         else
  518.         {
  519.             s_ImeState = ( bIme && _ImmGetOpenStatus( hImc ) != 0 ) ? IMEUI_STATE_ON : IMEUI_STATE_OFF;
  520.         }
  521.         _ImmReleaseContext( DXUTGetHWND(), hImc );
  522.     }
  523.     else
  524.         s_ImeState = IMEUI_STATE_OFF;
  525. }
  526. //--------------------------------------------------------------------------------------
  527. // Enable/disable the entire IME system.  When disabled, the default IME handling
  528. // kicks in.
  529. void CDXUTIMEEditBox::EnableImeSystem( bool bEnable )
  530. {
  531.     s_bEnableImeSystem = bEnable;
  532. }
  533. //--------------------------------------------------------------------------------------
  534. // Sets up IME-specific APIs for the IME edit controls.  This is called every time
  535. // the input locale changes.
  536. void CDXUTIMEEditBox::SetupImeApi()
  537. {
  538.     char szImeFile[MAX_PATH + 1];
  539.     _GetReadingString = NULL;
  540.     _ShowReadingWindow = NULL;
  541.     if( _ImmGetIMEFileNameA( s_hklCurrent, szImeFile, sizeof(szImeFile)/sizeof(szImeFile[0]) - 1 ) == 0 )
  542.         return;
  543.     if( s_hDllIme ) FreeLibrary( s_hDllIme );
  544.     s_hDllIme = LoadLibraryA( szImeFile );
  545.     if ( !s_hDllIme )
  546.         return;
  547.     _GetReadingString = (UINT (WINAPI*)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT))
  548.         ( GetProcAddress( s_hDllIme, "GetReadingString" ) );
  549.     _ShowReadingWindow =(BOOL (WINAPI*)(HIMC, BOOL))
  550.         ( GetProcAddress( s_hDllIme, "ShowReadingWindow" ) );
  551. }
  552. //--------------------------------------------------------------------------------------
  553. // Resets the composition string.
  554. void CDXUTIMEEditBox::ResetCompositionString()
  555. {
  556.     s_nCompCaret = 0;
  557.     s_CompString.SetText( L"" );
  558.     ZeroMemory( s_abCompStringAttr, sizeof(s_abCompStringAttr) );
  559. }
  560. //--------------------------------------------------------------------------------------
  561. // Truncate composition string by sending keystrokes to the window.
  562. void CDXUTIMEEditBox::TruncateCompString( bool bUseBackSpace, int iNewStrLen )
  563. {
  564.     if( !s_bInsertOnType )
  565.         return;
  566.     int cc = (int) wcslen( s_CompString.GetBuffer() );
  567.     assert( iNewStrLen == 0 || iNewStrLen >= cc );
  568.     // Send right arrow keystrokes to move the caret
  569.     //   to the end of the composition string.
  570.     for (int i = 0; i < cc - s_nCompCaret; ++i )
  571.         SendMessage( DXUTGetHWND(), WM_KEYDOWN, VK_RIGHT, 0 );
  572.     SendMessage( DXUTGetHWND(), WM_KEYUP, VK_RIGHT, 0 );
  573.     if( bUseBackSpace || m_bInsertMode )
  574.         iNewStrLen = 0;
  575.     // The caller sets bUseBackSpace to false if there's possibility of sending
  576.     // new composition string to the app right after this function call.
  577.     // 
  578.     // If the app is in overwriting mode and new comp string is 
  579.     // shorter than current one, delete previous comp string 
  580.     // till it's same long as the new one. Then move caret to the beginning of comp string.
  581.     // New comp string will overwrite old one.
  582.     if( iNewStrLen < cc )
  583.     {
  584.         for( int i = 0; i < cc - iNewStrLen; ++i )
  585.         {
  586.             SendMessage( DXUTGetHWND(), WM_KEYDOWN, VK_BACK, 0 );  // Backspace character
  587.             SendMessageW( DXUTGetHWND(), WM_CHAR, VK_BACK, 0 );
  588.         }
  589.         SendMessage( DXUTGetHWND(), WM_KEYUP, VK_BACK, 0 );
  590.     }
  591.     else
  592.         iNewStrLen = cc;
  593.     // Move the caret to the beginning by sending left keystrokes
  594.     for (int i = 0; i < iNewStrLen; ++i )
  595.         SendMessage( DXUTGetHWND(), WM_KEYDOWN, VK_LEFT, 0 );
  596.     SendMessage( DXUTGetHWND(), WM_KEYUP, VK_LEFT, 0 );
  597. }
  598. //--------------------------------------------------------------------------------------
  599. // Sends the current composition string to the application by sending keystroke
  600. // messages.
  601. void CDXUTIMEEditBox::SendCompString()
  602. {
  603.     for( int i = 0; i < lstrlen( s_CompString.GetBuffer() ); ++i )
  604.         MsgProc( WM_CHAR, (WPARAM)s_CompString[i], 0 );
  605. }
  606. //--------------------------------------------------------------------------------------
  607. // Outputs current composition string then cleans up the composition task.
  608. void CDXUTIMEEditBox::FinalizeString( bool bSend )
  609. {
  610.     HIMC hImc;
  611.     if( NULL == ( hImc = _ImmGetContext( DXUTGetHWND() ) ) )
  612.         return;
  613.     static bool bProcessing = false;
  614.     if( bProcessing )    // avoid infinite recursion
  615.     {
  616.         DXUTTRACE( L"CDXUTIMEEditBox::FinalizeString: Reentrant detected!n" );
  617.         _ImmReleaseContext( DXUTGetHWND(), hImc );
  618.         return;
  619.     }
  620.     bProcessing = true;
  621.     if( !s_bInsertOnType && bSend )
  622.     {
  623.         // Send composition string to app.
  624.         LONG lLength = lstrlen( s_CompString.GetBuffer() );
  625.         // In case of CHT IME, don't send the trailing double byte space, if it exists.
  626.         if( GetLanguage() == LANG_CHT
  627.             && s_CompString[lLength - 1] == 0x3000 )
  628.         {
  629.             s_CompString[lLength - 1] = 0;
  630.         }
  631.         SendCompString();
  632.     }
  633.     ResetCompositionString();
  634.     // Clear composition string in IME
  635.     _ImmNotifyIME( hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0 );
  636.     // the following line is necessary as Korean IME doesn't close cand list
  637.     // when comp string is cancelled.
  638.     _ImmNotifyIME( hImc, NI_CLOSECANDIDATE, 0, 0 ); 
  639.     _ImmReleaseContext( DXUTGetHWND(), hImc );
  640.     bProcessing = false;
  641. }
  642. //--------------------------------------------------------------------------------------
  643. // Determine whether the reading window should be vertical or horizontal.
  644. void CDXUTIMEEditBox::GetReadingWindowOrientation( DWORD dwId )
  645. {
  646.     s_bHorizontalReading = ( s_hklCurrent == _CHS_HKL ) || ( s_hklCurrent == _CHT_HKL2 ) || ( dwId == 0 );
  647.     if( !s_bHorizontalReading && ( dwId & 0x0000FFFF ) == LANG_CHT )
  648.     {
  649.         WCHAR wszRegPath[MAX_PATH];
  650.         HKEY hKey;
  651.         DWORD dwVer = dwId & 0xFFFF0000;
  652.         StringCchCopy( wszRegPath, MAX_PATH, L"software\microsoft\windows\currentversion\" );
  653.         StringCchCat( wszRegPath, MAX_PATH, ( dwVer >= MAKEIMEVERSION( 5, 1 ) ) ? L"MSTCIPH" : L"TINTLGNT" );
  654.         LONG lRc = RegOpenKeyExW( HKEY_CURRENT_USER, wszRegPath, 0, KEY_READ, &hKey );
  655.         if (lRc == ERROR_SUCCESS)
  656.         {
  657.             DWORD dwSize = sizeof(DWORD), dwMapping, dwType;
  658.             lRc = RegQueryValueExW( hKey, L"Keyboard Mapping", NULL, &dwType, (PBYTE)&dwMapping, &dwSize );
  659.             if (lRc == ERROR_SUCCESS)
  660.             {
  661.                 if ( ( dwVer <= MAKEIMEVERSION( 5, 0 ) && 
  662.                        ( (BYTE)dwMapping == 0x22 || (BYTE)dwMapping == 0x23 ) )
  663.                      ||
  664.                      ( ( dwVer == MAKEIMEVERSION( 5, 1 ) || dwVer == MAKEIMEVERSION( 5, 2 ) ) &&
  665.                        (BYTE)dwMapping >= 0x22 && (BYTE)dwMapping <= 0x24 )
  666.                    )
  667.                 {
  668.                     s_bHorizontalReading = true;
  669.                 }
  670.             }
  671.             RegCloseKey( hKey );
  672.         }
  673.     }
  674. }
  675. //--------------------------------------------------------------------------------------
  676. // Obtain the reading string upon WM_IME_NOTIFY/INM_PRIVATE notification.
  677. void CDXUTIMEEditBox::GetPrivateReadingString()
  678. {
  679.     DWORD dwId = GetImeId();
  680.     if( !dwId )
  681.     {
  682.         s_bShowReadingWindow = false;
  683.         return;
  684.     }
  685.     HIMC hImc;
  686.     hImc = _ImmGetContext( DXUTGetHWND() );
  687.     if( !hImc )
  688.     {
  689.         s_bShowReadingWindow = false;
  690.         return;
  691.     }
  692.     DWORD dwReadingStrLen = 0;
  693.     DWORD dwErr = 0;
  694.     WCHAR *pwszReadingStringBuffer = NULL;  // Buffer for when the IME supports GetReadingString()
  695.     WCHAR *wstr = 0;
  696.     bool bUnicodeIme = false;  // Whether the IME context component is Unicode.
  697.     INPUTCONTEXT *lpIC = NULL;
  698.     if( _GetReadingString )
  699.     {
  700.         UINT uMaxUiLen;
  701.         BOOL bVertical;
  702.         // Obtain the reading string size
  703.         dwReadingStrLen = _GetReadingString( hImc, 0, NULL, (PINT)&dwErr, &bVertical, &uMaxUiLen );
  704.         if( dwReadingStrLen )
  705.         {
  706.             wstr = pwszReadingStringBuffer = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * dwReadingStrLen );
  707.             if( !pwszReadingStringBuffer )
  708.             {
  709.                 // Out of memory. Exit.
  710.                 _ImmReleaseContext( DXUTGetHWND(), hImc );
  711.                 return;
  712.             }
  713.             // Obtain the reading string
  714.             dwReadingStrLen = _GetReadingString( hImc, dwReadingStrLen, wstr, (PINT)&dwErr, &bVertical, &uMaxUiLen );
  715.         }
  716.         s_bHorizontalReading = !bVertical;
  717.         bUnicodeIme = true;
  718.     }
  719.     else
  720.     {
  721.         // IMEs that doesn't implement Reading String API
  722.         lpIC = _ImmLockIMC( hImc );
  723.         
  724.         LPBYTE p = 0;
  725.         switch( dwId )
  726.         {
  727.             case IMEID_CHT_VER42: // New(Phonetic/ChanJie)IME98  : 4.2.x.x // Win98
  728.             case IMEID_CHT_VER43: // New(Phonetic/ChanJie)IME98a : 4.3.x.x // WinMe, Win2k
  729.             case IMEID_CHT_VER44: // New ChanJie IME98b          : 4.4.x.x // WinXP
  730.                 p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 24 );
  731.                 if( !p ) break;
  732.                 dwReadingStrLen = *(DWORD *)( p + 7 * 4 + 32 * 4 );
  733.                 dwErr = *(DWORD *)( p + 8 * 4 + 32 * 4 );
  734.                 wstr = (WCHAR *)( p + 56 );
  735.                 bUnicodeIme = true;
  736.                 break;
  737.             case IMEID_CHT_VER50: // 5.0.x.x // WinME
  738.                 p = *(LPBYTE *)( (LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 3 * 4 );
  739.                 if( !p ) break;
  740.                 p = *(LPBYTE *)( (LPBYTE)p + 1*4 + 5*4 + 4*2 );
  741.                 if( !p ) break;
  742.                 dwReadingStrLen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16);
  743.                 dwErr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 + 1*4);
  744.                 wstr = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
  745.                 bUnicodeIme = false;
  746.                 break;
  747.             case IMEID_CHT_VER51: // 5.1.x.x // IME2002(w/OfficeXP)
  748.             case IMEID_CHT_VER52: // 5.2.x.x // (w/whistler)
  749.             case IMEID_CHS_VER53: // 5.3.x.x // SCIME2k or MSPY3 (w/OfficeXP and Whistler)
  750.                 p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 4);
  751.                 if( !p ) break;
  752.                 p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
  753.                 if( !p ) break;
  754.                 dwReadingStrLen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * 2);
  755.                 dwErr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * 2 + 1*4);
  756.                 wstr  = (WCHAR *) (p + 1*4 + (16*2+2*4) + 5*4);
  757.                 bUnicodeIme = true;
  758.                 break;
  759.             // the code tested only with Win 98 SE (MSPY 1.5/ ver 4.1.0.21)
  760.             case IMEID_CHS_VER41:
  761.             {
  762.                 int nOffset;
  763.                 nOffset = ( GetImeId( 1 ) >= 0x00000002 ) ? 8 : 7;
  764.                 p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + nOffset * 4);
  765.                 if( !p ) break;
  766.                 dwReadingStrLen = *(DWORD *)(p + 7*4 + 16*2*4);
  767.                 dwErr = *(DWORD *)(p + 8*4 + 16*2*4);
  768.                 dwErr = __min( dwErr, dwReadingStrLen );
  769.                 wstr = (WCHAR *)(p + 6*4 + 16*2*1);
  770.                 bUnicodeIme = true;
  771.                 break;
  772.             }
  773.             case IMEID_CHS_VER42: // 4.2.x.x // SCIME98 or MSPY2 (w/Office2k, Win2k, WinME, etc)
  774.             {
  775.                 OSVERSIONINFOW osi;
  776.                 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
  777.                 GetVersionExW( &osi );
  778.                 int nTcharSize = ( osi.dwPlatformId == VER_PLATFORM_WIN32_NT ) ? sizeof(WCHAR) : sizeof(char);
  779.                 p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 1*4 + 1*4 + 6*4);
  780.                 if( !p ) break;
  781.                 dwReadingStrLen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * nTcharSize);
  782.                 dwErr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * nTcharSize + 1*4);
  783.                 wstr  = (WCHAR *) (p + 1*4 + (16*2+2*4) + 5*4);
  784.                 bUnicodeIme = ( osi.dwPlatformId == VER_PLATFORM_WIN32_NT ) ? true : false;
  785.             }
  786.         }   // switch
  787.     }
  788.     // Copy the reading string to the candidate list first
  789.     s_CandList.awszCandidate[0][0] = 0;
  790.     s_CandList.awszCandidate[1][0] = 0;
  791.     s_CandList.awszCandidate[2][0] = 0;
  792.     s_CandList.awszCandidate[3][0] = 0;
  793.     s_CandList.dwCount = dwReadingStrLen;
  794.     s_CandList.dwSelection = (DWORD)-1; // do not select any char
  795.     if( bUnicodeIme )
  796.     {
  797.         UINT i;
  798.         for( i = 0; i < dwReadingStrLen; ++i ) // dwlen > 0, if known IME
  799.         {
  800.             if( dwErr <= i && s_CandList.dwSelection == (DWORD)-1 )
  801.             {
  802.                 // select error char
  803.                 s_CandList.dwSelection = i;
  804.             }
  805.             s_CandList.awszCandidate[i][0] = wstr[i];
  806.             s_CandList.awszCandidate[i][1] = 0;
  807.         }
  808.         s_CandList.awszCandidate[i][0] = 0;
  809.     }
  810.     else
  811.     {
  812.         char *p = (char *)wstr;
  813.         DWORD i, j;
  814.         for( i = 0, j = 0; i < dwReadingStrLen; ++i, ++j ) // dwlen > 0, if known IME
  815.         {
  816.             if( dwErr <= i && s_CandList.dwSelection == (DWORD)-1 )
  817.             {
  818.                 s_CandList.dwSelection = j;
  819.             }
  820.             // Obtain the current code page
  821.             WCHAR wszCodePage[8];
  822.             UINT uCodePage = CP_ACP;  // Default code page
  823.             if( GetLocaleInfoW( MAKELCID( GetLanguage(), SORT_DEFAULT ),
  824.                                 LOCALE_IDEFAULTANSICODEPAGE,
  825.                                 wszCodePage,
  826.                                 sizeof(wszCodePage)/sizeof(wszCodePage[0]) ) )
  827.             {
  828.                 uCodePage = wcstoul( wszCodePage, NULL, 0 );
  829.             }
  830.             MultiByteToWideChar( uCodePage, 0, p + i, IsDBCSLeadByteEx( uCodePage, p[i] ) ? 2 : 1,
  831.                                  s_CandList.awszCandidate[j], 1 );
  832.             if( IsDBCSLeadByteEx( uCodePage, p[i] ) )
  833.                 ++i;
  834.         }
  835.         s_CandList.awszCandidate[j][0] = 0;
  836.         s_CandList.dwCount = j;
  837.     }
  838.     if( !_GetReadingString )
  839.     {
  840.         _ImmUnlockIMCC( lpIC->hPrivate );
  841.         _ImmUnlockIMC( hImc );
  842.         GetReadingWindowOrientation( dwId );
  843.     }
  844.     _ImmReleaseContext( DXUTGetHWND(), hImc );
  845.     if( pwszReadingStringBuffer )
  846.         HeapFree( GetProcessHeap(), 0, pwszReadingStringBuffer );
  847.     // Copy the string to the reading string buffer
  848.     if( s_CandList.dwCount > 0 )
  849.         s_bShowReadingWindow = true;
  850.     else
  851.         s_bShowReadingWindow = false;
  852.     if( s_bHorizontalReading )
  853.     {
  854.         s_CandList.nReadingError = -1;
  855.         s_wszReadingString[0] = 0;
  856.         for( UINT i = 0; i < s_CandList.dwCount; ++i )
  857.         {
  858.             if( s_CandList.dwSelection == i )
  859.                 s_CandList.nReadingError = lstrlen( s_wszReadingString );
  860.             StringCchCat( s_wszReadingString, 32, s_CandList.awszCandidate[i] );
  861.         }
  862.     }
  863.     s_CandList.dwPageSize = MAX_CANDLIST;
  864. }
  865. //--------------------------------------------------------------------------------------
  866. // This function is used only briefly in CHT IME handling,
  867. // so accelerator isn't processed.
  868. void CDXUTIMEEditBox::PumpMessage()
  869. {
  870.     MSG msg;
  871.     while( PeekMessageW( &msg, NULL, 0, 0, PM_NOREMOVE ) )
  872.     {
  873.         if( !GetMessageW( &msg, NULL, 0, 0 ) )
  874.         {
  875.             PostQuitMessage( (int)msg.wParam );
  876.             return;
  877.         }
  878.         TranslateMessage( &msg );
  879.         DispatchMessageA( &msg );
  880.     }
  881. }
  882. //--------------------------------------------------------------------------------------
  883. void CDXUTIMEEditBox::OnFocusIn()
  884. {
  885.     CDXUTEditBox::OnFocusIn();
  886.     if( s_bEnableImeSystem )
  887.     {
  888.         _ImmAssociateContext( DXUTGetHWND(), s_hImcDef );
  889.         CheckToggleState();
  890.     } else
  891.         _ImmAssociateContext( DXUTGetHWND(), NULL );
  892.     //
  893.     // Set up the IME global state according to the current instance state
  894.     //
  895.     HIMC hImc;
  896.     if( NULL != ( hImc = _ImmGetContext( DXUTGetHWND() ) ) ) 
  897.     {
  898.         if( !s_bEnableImeSystem )
  899.             s_ImeState = IMEUI_STATE_OFF;
  900.         _ImmReleaseContext( DXUTGetHWND(), hImc );
  901.         CheckToggleState();
  902.     }
  903. }
  904. //--------------------------------------------------------------------------------------
  905. void CDXUTIMEEditBox::OnFocusOut()
  906. {
  907.     CDXUTEditBox::OnFocusOut();
  908.     FinalizeString( false );  // Don't send the comp string as to match RichEdit behavior
  909.     _ImmAssociateContext( DXUTGetHWND(), NULL );
  910. }
  911. //--------------------------------------------------------------------------------------
  912. bool CDXUTIMEEditBox::StaticMsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
  913. {
  914.     HIMC hImc;
  915.     if( !s_bEnableImeSystem )
  916.         return false;
  917. #if defined(DEBUG) || defined(_DEBUG)
  918.     m_bIMEStaticMsgProcCalled = true;
  919. #endif
  920.     switch( uMsg )
  921.     {
  922.         case WM_ACTIVATEAPP:
  923.             if( wParam )
  924.             {
  925.                 // Populate s_Locale with the list of keyboard layouts.
  926.                 UINT cKL = GetKeyboardLayoutList( 0, NULL );
  927.                 s_Locale.RemoveAll();
  928.                 HKL *phKL = new HKL[cKL];
  929.                 if( phKL )
  930.                 {
  931.                     GetKeyboardLayoutList( cKL, phKL );
  932.                     for( UINT i = 0; i < cKL; ++i )
  933.                     {
  934.                         CInputLocale Locale;
  935.                         // Filter out East Asian languages that are not IME.
  936.                         if( ( PRIMARYLANGID( LOWORD( phKL[i] ) ) == LANG_CHINESE ||
  937.                               PRIMARYLANGID( LOWORD( phKL[i] ) ) == LANG_JAPANESE ||
  938.                               PRIMARYLANGID( LOWORD( phKL[i] ) ) == LANG_KOREAN ) &&
  939.                               !_ImmIsIME( phKL[i] ) )
  940.                               continue;
  941.                         // If this language is already in the list, don't add it again.
  942.                         bool bBreak = false;
  943.                         for( int e = 0; e < s_Locale.GetSize(); ++e )
  944.                             if( LOWORD( s_Locale.GetAt( e ).m_hKL ) ==
  945.                                 LOWORD( phKL[i] ) )
  946.                             {
  947.                                 bBreak = true;
  948.                                 break;
  949.                             }
  950.                         if( bBreak )
  951.                             break;
  952.                         Locale.m_hKL = phKL[i];
  953.                         WCHAR wszDesc[128] = L"";
  954.                         switch( PRIMARYLANGID( LOWORD( phKL[i] ) ) )
  955.                         {
  956.                             // Simplified Chinese
  957.                             case LANG_CHINESE:
  958.                                 switch( SUBLANGID( LOWORD( phKL[i] ) ) )
  959.                                 {
  960.                                     case SUBLANG_CHINESE_SIMPLIFIED:
  961.                                         StringCchCopy( Locale.m_wszLangAbb, 3, s_aszIndicator[INDICATOR_CHS] );
  962.                                         break;
  963.                                     case SUBLANG_CHINESE_TRADITIONAL:
  964.                                         StringCchCopy( Locale.m_wszLangAbb, 3, s_aszIndicator[INDICATOR_CHT] );
  965.                                         break;
  966.                                     default:    // unsupported sub-language
  967.                                         GetLocaleInfoW( MAKELCID( LOWORD( phKL[i] ), SORT_DEFAULT ), LOCALE_SABBREVLANGNAME, wszDesc, 128 );
  968.                                         Locale.m_wszLangAbb[0] = wszDesc[0];
  969.                                         Locale.m_wszLangAbb[1] = towlower( wszDesc[1] );
  970.                                         Locale.m_wszLangAbb[2] = L'';
  971.                                         break;
  972.                                 }
  973.                                 break;
  974.                             // Korean
  975.                             case LANG_KOREAN:
  976.                                 StringCchCopy( Locale.m_wszLangAbb, 3, s_aszIndicator[INDICATOR_KOREAN] );
  977.                                 break;
  978.                             // Japanese
  979.                             case LANG_JAPANESE:
  980.                                 StringCchCopy( Locale.m_wszLangAbb, 3, s_aszIndicator[INDICATOR_JAPANESE] );
  981.                                 break;         
  982.                             default:
  983.                                 // A non-IME language.  Obtain the language abbreviation
  984.                                 // and store it for rendering the indicator later.
  985.                                 GetLocaleInfoW( MAKELCID( LOWORD( phKL[i] ), SORT_DEFAULT ), LOCALE_SABBREVLANGNAME, wszDesc, 128 );
  986.                                 Locale.m_wszLangAbb[0] = wszDesc[0];
  987.                                 Locale.m_wszLangAbb[1] = towlower( wszDesc[1] );
  988.                                 Locale.m_wszLangAbb[2] = L'';
  989.                                 break;
  990.                         }
  991.                         GetLocaleInfoW( MAKELCID( LOWORD( phKL[i] ), SORT_DEFAULT ), LOCALE_SLANGUAGE, wszDesc, 128 );
  992.                         StringCchCopy( Locale.m_wszLang, 64, wszDesc );
  993.                         s_Locale.Add( Locale );
  994.                     }
  995.                     delete[] phKL;
  996.                 }
  997.             }
  998.             break;
  999.         case WM_INPUTLANGCHANGE:
  1000.             DXUTTRACE( L"WM_INPUTLANGCHANGEn" );
  1001.             {
  1002.                 UINT uLang = GetPrimaryLanguage();
  1003.                 CheckToggleState();
  1004.                 if ( uLang != GetPrimaryLanguage() )
  1005.                 {
  1006.                     // Korean IME always inserts on keystroke.  Other IMEs do not.
  1007.                     s_bInsertOnType = ( GetPrimaryLanguage() == LANG_KOREAN );
  1008.                 }
  1009.                 // IME changed.  Setup the new IME.
  1010.                 SetupImeApi();
  1011.                 if( _ShowReadingWindow )
  1012.                 {
  1013.                     if ( NULL != ( hImc = _ImmGetContext( DXUTGetHWND() ) ) )
  1014.                     {
  1015.                         _ShowReadingWindow( hImc, false );
  1016.                         _ImmReleaseContext( DXUTGetHWND(), hImc );
  1017.                     }
  1018.                 }
  1019.             }
  1020.             return true;
  1021.         case WM_IME_SETCONTEXT:
  1022.             DXUTTRACE( L"WM_IME_SETCONTEXTn" );
  1023.             //
  1024.             // We don't want anything to display, so we have to clear this
  1025.             //
  1026.             lParam = 0;
  1027.             return false;
  1028.         // Handle WM_IME_STARTCOMPOSITION here since
  1029.         // we do not want the default IME handler to see
  1030.         // this when our fullscreen app is running.
  1031.         case WM_IME_STARTCOMPOSITION:
  1032.             DXUTTRACE( L"WM_IME_STARTCOMPOSITIONn" );
  1033.             ResetCompositionString();
  1034.             // Since the composition string has its own caret, we don't render
  1035.             // the edit control's own caret to avoid double carets on screen.
  1036.             s_bHideCaret = true;
  1037.             return true;
  1038.         case WM_IME_COMPOSITION:
  1039.             DXUTTRACE( L"WM_IME_COMPOSITIONn" );
  1040.             return false;
  1041.     }
  1042.     return false;
  1043. }
  1044. //--------------------------------------------------------------------------------------
  1045. bool CDXUTIMEEditBox::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lParam )
  1046. {
  1047.     if( !m_bEnabled || !m_bVisible )
  1048.         return false;
  1049.     switch( uMsg )
  1050.     {
  1051.         case WM_LBUTTONDOWN:
  1052.         case WM_LBUTTONDBLCLK:
  1053.         {
  1054.             DXUTFontNode* pFont = m_pDialog->GetFont( m_Elements.GetAt( 9 )->iFont );
  1055.             // Check if this click is on top of the composition string
  1056.             int nCompStrWidth;
  1057.             s_CompString.CPtoX( s_CompString.GetTextSize(), FALSE, &nCompStrWidth );
  1058.             if( s_ptCompString.x <= pt.x &&
  1059.                 s_ptCompString.y <= pt.y &&
  1060.                 s_ptCompString.x + nCompStrWidth > pt.x &&
  1061.                 s_ptCompString.y + pFont->nHeight > pt.y )
  1062.             {
  1063.                 int nCharBodyHit, nCharHit;
  1064.                 int nTrail;
  1065.                 // Determine the character clicked on.
  1066.                 s_CompString.XtoCP( pt.x - s_ptCompString.x, &nCharBodyHit, &nTrail );
  1067.                 if( nTrail && nCharBodyHit < s_CompString.GetTextSize() )
  1068.                     nCharHit = nCharBodyHit + 1;
  1069.                 else
  1070.                     nCharHit = nCharBodyHit;
  1071.                 // Now generate keypress events to move the comp string cursor
  1072.                 // to the click point.  First, if the candidate window is displayed,
  1073.                 // send Esc to close it.
  1074.                 HIMC hImc = _ImmGetContext( DXUTGetHWND() );
  1075.                 if( !hImc )
  1076.                     return true;
  1077.                 _ImmNotifyIME( hImc, NI_CLOSECANDIDATE, 0, 0 );
  1078.                 _ImmReleaseContext( DXUTGetHWND(), hImc );
  1079.                 switch( GetPrimaryLanguage() )
  1080.                 {
  1081.                     case LANG_JAPANESE:
  1082.                         // For Japanese, there are two cases.  If s_nFirstTargetConv is
  1083.                         // -1, the comp string hasn't been converted yet, and we use
  1084.                         // s_nCompCaret.  For any other value of s_nFirstTargetConv,
  1085.                         // the string has been converted, so we use clause information.
  1086.                         if( s_nFirstTargetConv != -1 )
  1087.                         {
  1088.                             int nClauseClicked = 0;
  1089.                             while( (int)s_adwCompStringClause[nClauseClicked + 1] <= nCharBodyHit )
  1090.                                 ++nClauseClicked;
  1091.                             int nClauseSelected = 0;
  1092.                             while( (int)s_adwCompStringClause[nClauseSelected + 1] <= s_nFirstTargetConv )
  1093.                                 ++nClauseSelected;
  1094.                             BYTE nVirtKey = nClauseClicked > nClauseSelected ? VK_RIGHT : VK_LEFT;
  1095.                             int nSendCount = abs( nClauseClicked - nClauseSelected );
  1096.                             while( nSendCount-- > 0 )
  1097.                                 SendKey( nVirtKey );
  1098.                             return true;
  1099.                         }
  1100.                         // Not converted case. Fall thru to Chinese case.
  1101.                     case LANG_CHINESE:
  1102.                     {
  1103.                         // For Chinese, use s_nCompCaret.
  1104.                         BYTE nVirtKey = nCharHit > s_nCompCaret ? VK_RIGHT : VK_LEFT;
  1105.                         int nSendCount = abs( nCharHit - s_nCompCaret );
  1106.                         while( nSendCount-- > 0 )
  1107.                             SendKey( nVirtKey );
  1108.                         break;
  1109.                     }
  1110.                 }
  1111.                 return true;
  1112.             }
  1113.             // Check if the click is on top of the candidate window
  1114.             if( s_CandList.bShowWindow && PtInRect( &s_CandList.rcCandidate, pt ) )
  1115.             {
  1116.                 if( s_bVerticalCand )
  1117.                 {
  1118.                     // Vertical candidate window
  1119.                     // Compute the row the click is on
  1120.                     int nRow = ( pt.y - s_CandList.rcCandidate.top ) / pFont->nHeight;
  1121.                     if( nRow < (int)s_CandList.dwCount )
  1122.                     {
  1123.                         // nRow is a valid entry.
  1124.                         // Now emulate keystrokes to select the candidate at this row.
  1125.                         switch( GetPrimaryLanguage() )
  1126.                         {
  1127.                             case LANG_CHINESE:
  1128.                             case LANG_KOREAN:
  1129.                                 // For Chinese and Korean, simply send the number keystroke.
  1130.                                 SendKey( (BYTE) ('0' + nRow + 1) );
  1131.                                 break;
  1132.                             case LANG_JAPANESE:
  1133.                                 // For Japanese, move the selection to the target row,
  1134.                                 // then send Right, then send Left.
  1135.                                 BYTE nVirtKey;
  1136.                                 if( nRow > (int)s_CandList.dwSelection )
  1137.                                     nVirtKey = VK_DOWN;
  1138.                                 else
  1139.                                     nVirtKey = VK_UP;
  1140.                                 int nNumToHit = abs( int( nRow - s_CandList.dwSelection ) );
  1141.                                 for( int nStrike = 0; nStrike < nNumToHit; ++nStrike )
  1142.                                     SendKey( nVirtKey );
  1143.                                 // Do this to close the candidate window without ending composition.
  1144.                                 SendKey( VK_RIGHT );
  1145.                                 SendKey( VK_LEFT );
  1146.                                 break;
  1147.                         }
  1148.                     }
  1149.                 } else
  1150.                 {
  1151.                     // Horizontal candidate window
  1152.                     // Determine which the character the click has hit.
  1153.                     int nCharHit;
  1154.                     int nTrail;
  1155.                     s_CandList.HoriCand.XtoCP( pt.x - s_CandList.rcCandidate.left, &nCharHit, &nTrail );
  1156.                     // Determine which candidate string the character belongs to.
  1157.                     int nCandidate = s_CandList.dwCount - 1;
  1158.                     int nEntryStart = 0;
  1159.                     for( UINT i = 0; i < s_CandList.dwCount; ++i )
  1160.                     {
  1161.                         if( nCharHit >= nEntryStart )
  1162.                         {
  1163.                             // Haven't found it.
  1164.                             nEntryStart += lstrlenW( s_CandList.awszCandidate[i] ) + 1;  // plus space separator
  1165.                         } else
  1166.                         {
  1167.                             // Found it.  This entry starts at the right side of the click point,
  1168.                             // so the char belongs to the previous entry.
  1169.                             nCandidate = i - 1;
  1170.                             break;
  1171.                         }
  1172.                     }
  1173.                     // Now emulate keystrokes to select the candidate entry.
  1174.                     switch( GetPrimaryLanguage() )
  1175.                     {
  1176.                         case LANG_CHINESE:
  1177.                         case LANG_KOREAN:
  1178.                             // For Chinese and Korean, simply send the number keystroke.
  1179.                             SendKey( (BYTE) ('0' + nCandidate + 1) );
  1180.                             break;
  1181.                     }
  1182.                 }
  1183.                 return true;
  1184.             }
  1185.         }
  1186.     }
  1187.     // If we didn't care for the msg, let the parent process it.
  1188.     return CDXUTEditBox::HandleMouse( uMsg, pt, wParam, lParam );
  1189. }
  1190. //--------------------------------------------------------------------------------------
  1191. bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
  1192. {
  1193.     if( !m_bEnabled || !m_bVisible )
  1194.         return false;
  1195. #if defined(DEBUG) || defined(_DEBUG)
  1196.     // DXUT.cpp used to call CDXUTIMEEditBox::StaticMsgProc() so that, but now
  1197.     // this is the application's responsiblity.  To do this, call 
  1198.     // CDXUTDialogResourceManager::MsgProc() before calling this function.
  1199.     assert( m_bIMEStaticMsgProcCalled && L"To fix, call CDXUTDialogResourceManager::MsgProc() first" );
  1200. #endif
  1201.     bool trappedData;
  1202.     bool *trapped = &trappedData;
  1203.     HIMC hImc;
  1204.     static LPARAM lAlt = 0x80000000, lCtrl = 0x80000000, lShift = 0x80000000;
  1205.     *trapped = false;
  1206.     if( !s_bEnableImeSystem )
  1207.         return CDXUTEditBox::MsgProc( uMsg, wParam, lParam );
  1208.     switch( uMsg )
  1209.     {
  1210.         //
  1211.         //  IME Handling
  1212.         //
  1213.         case WM_IME_COMPOSITION:
  1214.             DXUTTRACE( L"WM_IME_COMPOSITIONn" );
  1215.             {
  1216.                 LONG lRet;  // Returned count in CHARACTERS
  1217.                 WCHAR wszCompStr[MAX_COMPSTRING_SIZE];
  1218.                 *trapped = true;
  1219.                 if( NULL == ( hImc = _ImmGetContext( DXUTGetHWND() ) ) )
  1220.                 {
  1221.                     break;
  1222.                 }
  1223.                 // Get the caret position in composition string
  1224.                 if ( lParam & GCS_CURSORPOS )
  1225.                 {
  1226.                     s_nCompCaret = _ImmGetCompositionStringW( hImc, GCS_CURSORPOS, NULL, 0 );
  1227.                     if( s_nCompCaret < 0 )
  1228.                         s_nCompCaret = 0; // On error, set caret to pos 0.
  1229.                 }
  1230.                 // ResultStr must be processed before composition string.
  1231.                 //
  1232.                 // This is because for some IMEs, such as CHT, pressing Enter
  1233.                 // to complete the composition sends WM_IME_COMPOSITION with both
  1234.                 // GCS_RESULTSTR and GCS_COMPSTR.  Retrieving the result string
  1235.                 // gives the correct string, while retrieving the comp string
  1236.                 // (GCS_COMPSTR) gives empty string.  GCS_RESULTSTR should be
  1237.                 // handled first so that the application receives the string.  Then
  1238.                 // GCS_COMPSTR can be handled to clear the comp string buffer.
  1239.                 if ( lParam & GCS_RESULTSTR )
  1240.                 {
  1241.                     DXUTTRACE( L"  GCS_RESULTSTRn" );
  1242.                     lRet = _ImmGetCompositionStringW( hImc, GCS_RESULTSTR, wszCompStr, sizeof( wszCompStr ) );
  1243.                     if( lRet > 0 )
  1244.                     {
  1245.                         lRet /= sizeof(WCHAR);
  1246.                         wszCompStr[lRet] = 0;  // Force terminate
  1247.                         TruncateCompString( false, (int)wcslen( wszCompStr ) );
  1248.                         s_CompString.SetText( wszCompStr );
  1249.                         SendCompString();
  1250.                         ResetCompositionString();
  1251.                     }
  1252.                 }
  1253.                 //
  1254.                 // Reads in the composition string.
  1255.                 //
  1256.                 if ( lParam & GCS_COMPSTR )
  1257.                 {
  1258.                     DXUTTRACE( L"  GCS_COMPSTRn" );
  1259.                     //////////////////////////////////////////////////////
  1260.                     // Retrieve the latest user-selected IME candidates
  1261.                     lRet = _ImmGetCompositionStringW( hImc, GCS_COMPSTR, wszCompStr, sizeof( wszCompStr ) );
  1262.                     if( lRet > 0 )
  1263.                     {
  1264.                         lRet /= sizeof(WCHAR);  // Convert size in byte to size in char
  1265.                         wszCompStr[lRet] = 0;  // Force terminate
  1266.                         //
  1267.                         // Remove the whole of the string
  1268.                         //
  1269.                         TruncateCompString( false, (int)wcslen( wszCompStr ) );
  1270.                         s_CompString.SetText( wszCompStr );
  1271.                         // Older CHT IME uses composition string for reading string
  1272.                         if ( GetLanguage() == LANG_CHT && !GetImeId() )
  1273.                         {
  1274.                             if( lstrlen( s_CompString.GetBuffer() ) )
  1275.                             {
  1276.                                 s_CandList.dwCount = 4;             // Maximum possible length for reading string is 4
  1277.                                 s_CandList.dwSelection = (DWORD)-1; // don't select any candidate
  1278.                                 // Copy the reading string to the candidate list
  1279.                                 for( int i = 3; i >= 0; --i )
  1280.                                 {
  1281.                                     if( i > lstrlen( s_CompString.GetBuffer() ) - 1 )
  1282.                                         s_CandList.awszCandidate[i][0] = 0;  // Doesn't exist
  1283.                                     else
  1284.                                     {
  1285.                                         s_CandList.awszCandidate[i][0] = s_CompString[i];
  1286.                                         s_CandList.awszCandidate[i][1] = 0;
  1287.                                     }
  1288.                                 }
  1289.                                 s_CandList.dwPageSize = MAX_CANDLIST;
  1290.                                 // Clear comp string after we are done copying
  1291.                                 ZeroMemory( (LPVOID)s_CompString.GetBuffer(), 4 * sizeof(WCHAR) );
  1292.                                 s_bShowReadingWindow = true;
  1293.                                 GetReadingWindowOrientation( 0 );
  1294.                                 if( s_bHorizontalReading )
  1295.                                 {
  1296.                                     s_CandList.nReadingError = -1;  // Clear error
  1297.                                     // Create a string that consists of the current
  1298.                                     // reading string.  Since horizontal reading window
  1299.                                     // is used, we take advantage of this by rendering
  1300.                                     // one string instead of several.
  1301.                                     //
  1302.                                     // Copy the reading string from the candidate list
  1303.                                     // to the reading string buffer.
  1304.                                     s_wszReadingString[0] = 0;
  1305.                                     for( UINT i = 0; i < s_CandList.dwCount; ++i )
  1306.                                     {
  1307.                                         if( s_CandList.dwSelection == i )
  1308.                                             s_CandList.nReadingError = lstrlen( s_wszReadingString );
  1309.                                         StringCchCat( s_wszReadingString, 32, s_CandList.awszCandidate[i] );
  1310.                                     }
  1311.                                 }
  1312.                             }
  1313.                             else
  1314.                             {
  1315.                                 s_CandList.dwCount = 0;
  1316.                                 s_bShowReadingWindow = false;
  1317.                             }
  1318.                         }
  1319.                         if( s_bInsertOnType )
  1320.                         {
  1321.                             // Send composition string to the edit control
  1322.                             SendCompString();
  1323.                             // Restore the caret to the correct location.
  1324.                             // It's at the end right now, so compute the number
  1325.                             // of times left arrow should be pressed to
  1326.                             // send it to the original position.
  1327.                             int nCount = lstrlen( s_CompString.GetBuffer() + s_nCompCaret );
  1328.                             // Send left keystrokes
  1329.                             for( int i = 0; i < nCount; ++i )
  1330.                                 SendMessage( DXUTGetHWND(), WM_KEYDOWN, VK_LEFT, 0 );
  1331.                             SendMessage( DXUTGetHWND(), WM_KEYUP, VK_LEFT, 0 );
  1332.                         }
  1333.                     }
  1334.                     ResetCaretBlink();
  1335.                 }
  1336.                 // Retrieve comp string attributes
  1337.                 if( lParam & GCS_COMPATTR )
  1338.                 {
  1339.                     lRet = _ImmGetCompositionStringW( hImc, GCS_COMPATTR, s_abCompStringAttr, sizeof( s_abCompStringAttr ) );
  1340.                     if( lRet > 0 )
  1341.                         s_abCompStringAttr[lRet] = 0;  // ??? Is this needed for attributes?
  1342.                 }
  1343.                 // Retrieve clause information
  1344.                 if( lParam & GCS_COMPCLAUSE )
  1345.                 {
  1346.                     lRet = _ImmGetCompositionStringW(hImc, GCS_COMPCLAUSE, s_adwCompStringClause, sizeof( s_adwCompStringClause ) );
  1347.                     s_adwCompStringClause[lRet / sizeof(DWORD)] = 0;  // Terminate
  1348.                 }
  1349.                 _ImmReleaseContext( DXUTGetHWND(), hImc );
  1350.             }
  1351.             break;
  1352.         case WM_IME_ENDCOMPOSITION:
  1353.             DXUTTRACE( L"WM_IME_ENDCOMPOSITIONn" );
  1354.             TruncateCompString();
  1355.             ResetCompositionString();
  1356.             // We can show the edit control's caret again.
  1357.             s_bHideCaret = false;
  1358.             // Hide reading window
  1359.             s_bShowReadingWindow = false;
  1360.             break;
  1361.         case WM_IME_NOTIFY:
  1362.             DXUTTRACE( L"WM_IME_NOTIFY %un", wParam );
  1363.             switch( wParam )
  1364.             {
  1365.                 case IMN_SETCONVERSIONMODE:
  1366.                     DXUTTRACE( L"  IMN_SETCONVERSIONMODEn" );
  1367.                 case IMN_SETOPENSTATUS:
  1368.                     DXUTTRACE( L"  IMN_SETOPENSTATUSn" );
  1369.                     CheckToggleState();
  1370.                     break;
  1371.                 case IMN_OPENCANDIDATE:
  1372.                 case IMN_CHANGECANDIDATE:
  1373.                 {
  1374.                     DXUTTRACE( wParam == IMN_CHANGECANDIDATE ? L"  IMN_CHANGECANDIDATEn" : L"  IMN_OPENCANDIDATEn" );
  1375.                     s_CandList.bShowWindow = true;
  1376.                     *trapped = true;
  1377.                     if( NULL == ( hImc = _ImmGetContext( DXUTGetHWND() ) ) )
  1378.                         break;
  1379.                     LPCANDIDATELIST lpCandList = NULL;
  1380.                     DWORD dwLenRequired;
  1381.                     s_bShowReadingWindow = false;
  1382.                     // Retrieve the candidate list
  1383.                     dwLenRequired = _ImmGetCandidateListW( hImc, 0, NULL, 0 );
  1384.                     if( dwLenRequired )
  1385.                     {
  1386.                         lpCandList = (LPCANDIDATELIST)HeapAlloc( GetProcessHeap(), 0, dwLenRequired );
  1387.                         dwLenRequired = _ImmGetCandidateListW( hImc, 0, lpCandList, dwLenRequired );
  1388.                     }
  1389.                     if( lpCandList )
  1390.                     {
  1391.                         // Update candidate list data
  1392.                         s_CandList.dwSelection = lpCandList->dwSelection;
  1393.                         s_CandList.dwCount = lpCandList->dwCount;
  1394.                         int nPageTopIndex = 0;
  1395.                         s_CandList.dwPageSize = __min( lpCandList->dwPageSize, MAX_CANDLIST );
  1396.                         if( GetPrimaryLanguage() == LANG_JAPANESE )
  1397.                         {
  1398.                             // Japanese IME organizes its candidate list a little
  1399.                             // differently from the other IMEs.
  1400.                             nPageTopIndex = ( s_CandList.dwSelection / s_CandList.dwPageSize ) * s_CandList.dwPageSize;
  1401.                         }
  1402.                         else
  1403.                             nPageTopIndex = lpCandList->dwPageStart;
  1404.                         // Make selection index relative to first entry of page
  1405.                         s_CandList.dwSelection = ( GetLanguage() == LANG_CHS && !GetImeId() ) ? (DWORD)-1
  1406.                                                  : s_CandList.dwSelection - nPageTopIndex;
  1407.                         ZeroMemory( s_CandList.awszCandidate, sizeof(s_CandList.awszCandidate) );
  1408.                         for( UINT i = nPageTopIndex, j = 0;
  1409.                             (DWORD)i < lpCandList->dwCount && j < s_CandList.dwPageSize;
  1410.                             i++, j++ )
  1411.                         {
  1412.                             // Initialize the candidate list strings
  1413.                             LPWSTR pwsz = s_CandList.awszCandidate[j];
  1414.                             // For every candidate string entry,
  1415.                             // write [index] + Space + [String] if vertical,
  1416.                             // write [index] + [String] + Space if horizontal.
  1417.                             *pwsz++ = (WCHAR)( L'0' + ( (j + 1) % 10 ) );  // Index displayed is 1 based
  1418.                             if( s_bVerticalCand )
  1419.                                 *pwsz++ = L' ';
  1420.                             WCHAR *pwszNewCand = (LPWSTR)( (LPBYTE)lpCandList + lpCandList->dwOffset[i] );
  1421.                             while ( *pwszNewCand )
  1422.                                 *pwsz++ = *pwszNewCand++;
  1423.                             if( !s_bVerticalCand )
  1424.                                 *pwsz++ = L' ';
  1425.                             *pwsz = 0;  // Terminate
  1426.                         }
  1427.                         // Make dwCount in s_CandList be number of valid entries in the page.
  1428.                         s_CandList.dwCount = lpCandList->dwCount - lpCandList->dwPageStart;
  1429.                         if( s_CandList.dwCount > lpCandList->dwPageSize )
  1430.                             s_CandList.dwCount = lpCandList->dwPageSize;
  1431.                         HeapFree( GetProcessHeap(), 0, lpCandList );
  1432.                         _ImmReleaseContext( DXUTGetHWND(), hImc );
  1433.                         // Korean and old Chinese IME can't have selection.
  1434.                         // User must use the number hotkey or Enter to select
  1435.                         // a candidate.
  1436.                         if( GetPrimaryLanguage() == LANG_KOREAN ||
  1437.                             GetLanguage() == LANG_CHT && !GetImeId() )
  1438.                         {
  1439.                             s_CandList.dwSelection = (DWORD)-1;
  1440.                         }
  1441.                         // Initialize s_CandList.HoriCand if we have a
  1442.                         // horizontal candidate window.
  1443.                         if( !s_bVerticalCand )
  1444.                         {
  1445.                             WCHAR wszCand[256] = L"";
  1446.                             s_CandList.nFirstSelected = 0;
  1447.                             s_CandList.nHoriSelectedLen = 0;
  1448.                             for( UINT i = 0; i < MAX_CANDLIST; ++i )
  1449.                             {
  1450.                                 if( s_CandList.awszCandidate[i][0] == L'' )
  1451.                                     break;
  1452.                                 WCHAR wszEntry[32];
  1453.                                 StringCchPrintf( wszEntry, 32, L"%s ", s_CandList.awszCandidate[i] );
  1454.                                 // If this is the selected entry, mark its char position.
  1455.                                 if( s_CandList.dwSelection == i )
  1456.                                 {
  1457.                                     s_CandList.nFirstSelected = lstrlen( wszCand );
  1458.                                     s_CandList.nHoriSelectedLen = lstrlen( wszEntry ) - 1;  // Minus space
  1459.                                 }
  1460.                                 StringCchCat( wszCand, 256, wszEntry );
  1461.                             }
  1462.                             wszCand[lstrlen(wszCand) - 1] = L'';  // Remove the last space
  1463.                             s_CandList.HoriCand.SetText( wszCand );
  1464.                         }
  1465.                     }
  1466.                     break;
  1467.                 }
  1468.                 case IMN_CLOSECANDIDATE:
  1469.                 {
  1470.                     DXUTTRACE( L"  IMN_CLOSECANDIDATEn" );
  1471.                     s_CandList.bShowWindow = false;
  1472.                     if( !s_bShowReadingWindow )
  1473.                     {
  1474.                         s_CandList.dwCount = 0;
  1475.                         ZeroMemory( s_CandList.awszCandidate, sizeof(s_CandList.awszCandidate) );
  1476.                     }
  1477.                     *trapped = true;
  1478.                     break;
  1479.                 }
  1480.                 case IMN_PRIVATE:
  1481.                     DXUTTRACE( L"  IMN_PRIVATEn" );
  1482.                     {
  1483.                         if( !s_CandList.bShowWindow )
  1484.                             GetPrivateReadingString();
  1485.                         // Trap some messages to hide reading window
  1486.                         DWORD dwId = GetImeId();
  1487.                         switch( dwId )
  1488.                         {
  1489.                             case IMEID_CHT_VER42:
  1490.                             case IMEID_CHT_VER43:
  1491.                             case IMEID_CHT_VER44:
  1492.                             case IMEID_CHS_VER41:
  1493.                             case IMEID_CHS_VER42:
  1494.                                 if( ( lParam == 1 ) || ( lParam == 2 ) )
  1495.                                 {
  1496.                                     *trapped = true;
  1497.                                 }
  1498.                                 break;
  1499.                             case IMEID_CHT_VER50:
  1500.                             case IMEID_CHT_VER51:
  1501.                             case IMEID_CHT_VER52:
  1502.                             case IMEID_CHT_VER60:
  1503.                             case IMEID_CHS_VER53:
  1504.                                 if( (lParam == 16) || (lParam == 17) || (lParam == 26) || (lParam == 27) || (lParam == 28) )
  1505.                                 {
  1506.                                     *trapped = true;
  1507.                                 }
  1508.                                 break;
  1509.                         }
  1510.                     }
  1511.                     break;
  1512.                 default:
  1513.                     *trapped = true;
  1514.                     break;
  1515.             }
  1516.             break;
  1517.         // When Text Service Framework is installed in Win2K, Alt+Shift and Ctrl+Shift combination (to switch input
  1518.         // locale / keyboard layout) doesn't send WM_KEYUP message for the key that is released first. We need to check
  1519.         // if these keys are actually up whenever we receive key up message for other keys.
  1520.         case WM_KEYUP:
  1521.         case WM_SYSKEYUP:
  1522.             if ( !( lAlt & 0x80000000 ) && wParam != VK_MENU && ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) == 0 )
  1523.             {
  1524.                 PostMessageW( GetFocus(), WM_KEYUP, (WPARAM)VK_MENU, ( lAlt & 0x01ff0000 ) | 0xC0000001 );
  1525.             }   
  1526.             else if ( !( lCtrl & 0x80000000 ) && wParam != VK_CONTROL && ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) == 0 )
  1527.             {
  1528.                 PostMessageW( GetFocus(), WM_KEYUP, (WPARAM)VK_CONTROL, ( lCtrl & 0x01ff0000 ) | 0xC0000001 );
  1529.             }
  1530.             else if ( !( lShift & 0x80000000 ) && wParam != VK_SHIFT && ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) == 0 )
  1531.             {
  1532.                 PostMessageW( GetFocus(), WM_KEYUP, (WPARAM)VK_SHIFT, ( lShift & 0x01ff0000 ) | 0xC0000001 );
  1533.             }
  1534.             // fall through WM_KEYDOWN / WM_SYSKEYDOWN
  1535.         case WM_KEYDOWN:
  1536.         case WM_SYSKEYDOWN:
  1537.             switch ( wParam )
  1538.             {
  1539.                 case VK_MENU:
  1540.                     lAlt = lParam;
  1541.                     break;
  1542.                 case VK_SHIFT:
  1543.                     lShift = lParam;
  1544.                     break;
  1545.                 case VK_CONTROL:
  1546.                     lCtrl = lParam;
  1547.                     break;
  1548.             }
  1549.             //break;
  1550.             // Fall through to default case
  1551.             // so we invoke the parent.
  1552.         default:
  1553.             // Let the parent handle the message that we
  1554.             // don't handle.
  1555.             return CDXUTEditBox::MsgProc( uMsg, wParam, lParam );
  1556.     }  // switch
  1557.     return *trapped;
  1558. }
  1559. //--------------------------------------------------------------------------------------
  1560. void CDXUTIMEEditBox::RenderCandidateReadingWindow( IDirect3DDevice9* pd3dDevice, float fElapsedTime, bool bReading )
  1561. {
  1562.     RECT rc;
  1563.     UINT nNumEntries = bReading ? 4 : MAX_CANDLIST;
  1564.     D3DCOLOR TextColor, TextBkColor, SelTextColor, SelBkColor;
  1565.     int nX, nXFirst, nXComp;
  1566.     m_Buffer.CPtoX( m_nCaret, FALSE, &nX );
  1567.     m_Buffer.CPtoX( m_nFirstVisible, FALSE, &nXFirst );
  1568.     if( bReading )
  1569.     {
  1570.         TextColor = m_ReadingColor;
  1571.         TextBkColor = m_ReadingWinColor;
  1572.         SelTextColor = m_ReadingSelColor;
  1573.         SelBkColor = m_ReadingSelBkColor;
  1574.     } else
  1575.     {
  1576.         TextColor = m_CandidateColor;
  1577.         TextBkColor = m_CandidateWinColor;
  1578.         SelTextColor = m_CandidateSelColor;
  1579.         SelBkColor = m_CandidateSelBkColor;
  1580.     }
  1581.     // For Japanese IME, align the window with the first target converted character.
  1582.     // For all other IMEs, align with the caret.  This is because the caret
  1583.     // does not move for Japanese IME.
  1584.     if ( GetLanguage() == LANG_CHT && !GetImeId() )
  1585.         nXComp = 0;
  1586.     else
  1587.     if( GetPrimaryLanguage() == LANG_JAPANESE )
  1588.         s_CompString.CPtoX( s_nFirstTargetConv, FALSE, &nXComp );
  1589.     else
  1590.         s_CompString.CPtoX( s_nCompCaret, FALSE, &nXComp );
  1591.     // Compute the size of the candidate window
  1592.     int nWidthRequired = 0;
  1593.     int nHeightRequired = 0;
  1594.     int nSingleLineHeight = 0;
  1595.     if( ( s_bVerticalCand && !bReading ) ||
  1596.         ( !s_bHorizontalReading && bReading ) )
  1597.     {
  1598.         // Vertical window
  1599.         for( UINT i = 0; i < nNumEntries; ++i )
  1600.         {
  1601.             if( s_CandList.awszCandidate[i][0] == L'' )
  1602.                 break;
  1603.             SetRect( &rc, 0, 0, 0, 0 );
  1604.             m_pDialog->CalcTextRect( s_CandList.awszCandidate[i], m_Elements.GetAt( 1 ), &rc );
  1605.             nWidthRequired = __max( nWidthRequired, rc.right - rc.left );
  1606.             nSingleLineHeight = __max( nSingleLineHeight, rc.bottom - rc.top );
  1607.         }
  1608.         nHeightRequired = nSingleLineHeight * nNumEntries;
  1609.     } else
  1610.     {
  1611.         // Horizontal window
  1612.         SetRect( &rc, 0, 0, 0, 0 );
  1613.         if( bReading )
  1614.             m_pDialog->CalcTextRect( s_wszReadingString, m_Elements.GetAt( 1 ), &rc );
  1615.         else
  1616.             m_pDialog->CalcTextRect( s_CandList.HoriCand.GetBuffer(), m_Elements.GetAt( 1 ), &rc );
  1617.         nWidthRequired = rc.right - rc.left;
  1618.         nSingleLineHeight = nHeightRequired = rc.bottom - rc.top;
  1619.     }
  1620.     // Now that we have the dimension, calculate the location for the candidate window.
  1621.     // We attempt to fit the window in this order:
  1622.     // bottom, top, right, left.
  1623.     bool bHasPosition = false;
  1624.     // Bottom
  1625.     SetRect( &rc, s_ptCompString.x + nXComp, s_ptCompString.y + m_rcText.bottom - m_rcText.top,
  1626.                   s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y + m_rcText.bottom - m_rcText.top + nHeightRequired );
  1627.     // if the right edge is cut off, move it left.
  1628.     if( rc.right > m_pDialog->GetWidth() )
  1629.     {
  1630.         rc.left -= rc.right - m_pDialog->GetWidth();
  1631.         rc.right = m_pDialog->GetWidth();
  1632.     }
  1633.     if( rc.bottom <= m_pDialog->GetHeight() )
  1634.         bHasPosition = true;
  1635.     // Top
  1636.     if( !bHasPosition )
  1637.     {
  1638.         SetRect( &rc, s_ptCompString.x + nXComp, s_ptCompString.y - nHeightRequired,
  1639.                       s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y );
  1640.         // if the right edge is cut off, move it left.
  1641.         if( rc.right > m_pDialog->GetWidth() )
  1642.         {
  1643.             rc.left -= rc.right - m_pDialog->GetWidth();
  1644.             rc.right = m_pDialog->GetWidth();
  1645.         }
  1646.         if( rc.top >= 0 )
  1647.             bHasPosition = true;
  1648.     }
  1649.     // Right
  1650.     if( !bHasPosition )
  1651.     {
  1652.         int nXCompTrail;
  1653.         s_CompString.CPtoX( s_nCompCaret, TRUE, &nXCompTrail );
  1654.         SetRect( &rc, s_ptCompString.x + nXCompTrail, 0,
  1655.                       s_ptCompString.x + nXCompTrail + nWidthRequired, nHeightRequired );
  1656.         if( rc.right <= m_pDialog->GetWidth() )
  1657.             bHasPosition = true;
  1658.     }
  1659.     // Left
  1660.     if( !bHasPosition )
  1661.     {
  1662.         SetRect( &rc, s_ptCompString.x + nXComp - nWidthRequired, 0,
  1663.                       s_ptCompString.x + nXComp, nHeightRequired );
  1664.         if( rc.right >= 0 )
  1665.             bHasPosition = true;
  1666.     }
  1667.     if( !bHasPosition )
  1668.     {
  1669.         // The dialog is too small for the candidate window.
  1670.         // Fall back to render at 0, 0.  Some part of the window
  1671.         // will be cut off.
  1672.         rc.left = 0;
  1673.         rc.right = nWidthRequired;
  1674.     }
  1675.     // If we are rendering the candidate window, save the position
  1676.     // so that mouse clicks are checked properly.
  1677.     if( !bReading )
  1678.         s_CandList.rcCandidate = rc;
  1679.     // Render the elements
  1680.     m_pDialog->DrawRect( &rc, TextBkColor );
  1681.     if( ( s_bVerticalCand && !bReading ) ||
  1682.         ( !s_bHorizontalReading && bReading ) )
  1683.     {
  1684.         // Vertical candidate window
  1685.         for( UINT i = 0; i < nNumEntries; ++i )
  1686.         {
  1687.             // Here we are rendering one line at a time
  1688.             rc.bottom = rc.top + nSingleLineHeight;
  1689.             // Use a different color for the selected string
  1690.             if( s_CandList.dwSelection == i )
  1691.             {
  1692.                 m_pDialog->DrawRect( &rc, SelBkColor );
  1693.                 m_Elements.GetAt( 1 )->FontColor.Current = SelTextColor;
  1694.             } else
  1695.                 m_Elements.GetAt( 1 )->FontColor.Current = TextColor;
  1696.             m_pDialog->DrawText( s_CandList.awszCandidate[i], m_Elements.GetAt( 1 ), &rc );
  1697.             rc.top += nSingleLineHeight;
  1698.         }
  1699.     } else
  1700.     {
  1701.         // Horizontal candidate window
  1702.         m_Elements.GetAt( 1 )->FontColor.Current = TextColor;
  1703.         if( bReading )
  1704.             m_pDialog->DrawText( s_wszReadingString, m_Elements.GetAt( 1 ), &rc );
  1705.         else
  1706.             m_pDialog->DrawText( s_CandList.HoriCand.GetBuffer(), m_Elements.GetAt( 1 ), &rc );
  1707.         // Render the selected entry differently
  1708.         if( !bReading )
  1709.         {
  1710.             int nXLeft, nXRight;
  1711.             s_CandList.HoriCand.CPtoX( s_CandList.nFirstSelected, FALSE, &nXLeft );
  1712.             s_CandList.HoriCand.CPtoX( s_CandList.nFirstSelected + s_CandList.nHoriSelectedLen, FALSE, &nXRight );
  1713.             rc.right = rc.left + nXRight;
  1714.             rc.left += nXLeft;
  1715.             m_pDialog->DrawRect( &rc, SelBkColor );
  1716.             m_Elements.GetAt( 1 )->FontColor.Current = SelTextColor;
  1717.             m_pDialog->DrawText( s_CandList.HoriCand.GetBuffer() + s_CandList.nFirstSelected,
  1718.                                 m_Elements.GetAt( 1 ), &rc, false, s_CandList.nHoriSelectedLen );
  1719.         }
  1720.     }
  1721. }
  1722. //--------------------------------------------------------------------------------------
  1723. void CDXUTIMEEditBox::RenderComposition( IDirect3DDevice9* pd3dDevice, float fElapsedTime )
  1724. {
  1725.     RECT rcCaret = { 0, 0, 0, 0 };
  1726.     int nX, nXFirst;
  1727.     m_Buffer.CPtoX( m_nCaret, FALSE, &nX );
  1728.     m_Buffer.CPtoX( m_nFirstVisible, FALSE, &nXFirst );
  1729.     CDXUTElement *pElement = m_Elements.GetAt( 1 );
  1730.     // Get the required width
  1731.     RECT rc = { m_rcText.left + nX - nXFirst, m_rcText.top,
  1732.                 m_rcText.left + nX - nXFirst, m_rcText.bottom };
  1733.     m_pDialog->CalcTextRect( s_CompString.GetBuffer(), pElement, &rc );
  1734.     // If the composition string is too long to fit within
  1735.     // the text area, move it to below the current line.
  1736.     // This matches the behavior of the default IME.
  1737.     if( rc.right > m_rcText.right )
  1738.         OffsetRect( &rc, m_rcText.left - rc.left, rc.bottom - rc.top );
  1739.     // Save the rectangle position for processing highlighted text.
  1740.     RECT rcFirst = rc;
  1741.     // Update s_ptCompString for RenderCandidateReadingWindow().
  1742.     s_ptCompString.x = rc.left; s_ptCompString.y = rc.top;
  1743.     D3DCOLOR TextColor = m_CompColor;
  1744.     // Render the window and string.
  1745.     // If the string is too long, we must wrap the line.
  1746.     pElement->FontColor.Current = TextColor;
  1747.     const WCHAR *pwszComp = s_CompString.GetBuffer();
  1748.     int nCharLeft = s_CompString.GetTextSize();
  1749.     for( ; ; )
  1750.     {
  1751.         // Find the last character that can be drawn on the same line.
  1752.         int nLastInLine;
  1753.         int bTrail;
  1754.         s_CompString.XtoCP( m_rcText.right - rc.left, &nLastInLine, &bTrail );
  1755.         int nNumCharToDraw = __min( nCharLeft, nLastInLine );
  1756.         m_pDialog->CalcTextRect( pwszComp, pElement, &rc, nNumCharToDraw );
  1757.         // Draw the background
  1758.         // For Korean IME, blink the composition window background as if it
  1759.         // is a cursor.
  1760.         if( GetPrimaryLanguage() == LANG_KOREAN )
  1761.         {
  1762.             if( m_bCaretOn )
  1763.             {
  1764.                 m_pDialog->DrawRect( &rc, m_CompWinColor );
  1765.             }
  1766.             else
  1767.             {
  1768.                 // Not drawing composition string background. We
  1769.                 // use the editbox's text color for composition
  1770.                 // string text.
  1771.                 TextColor = m_Elements.GetAt(0)->FontColor.States[DXUT_STATE_NORMAL];
  1772.             }
  1773.         } else
  1774.         {
  1775.             // Non-Korean IME. Always draw composition background.
  1776.             m_pDialog->DrawRect( &rc, m_CompWinColor );
  1777.         }
  1778.         // Draw the text
  1779.         pElement->FontColor.Current = TextColor;
  1780.         m_pDialog->DrawText( pwszComp, pElement, &rc, false, nNumCharToDraw );
  1781.         // Advance pointer and counter
  1782.         nCharLeft -= nNumCharToDraw;
  1783.         pwszComp += nNumCharToDraw;
  1784.         if( nCharLeft <= 0 )
  1785.             break;
  1786.         // Advance rectangle coordinates to beginning of next line
  1787.         OffsetRect( &rc, m_rcText.left - rc.left, rc.bottom - rc.top );
  1788.     }
  1789.     // Load the rect for the first line again.
  1790.     rc = rcFirst;
  1791.     // Inspect each character in the comp string.
  1792.     // For target-converted and target-non-converted characters,
  1793.     // we display a different background color so they appear highlighted.
  1794.     int nCharFirst = 0;
  1795.     nXFirst = 0;
  1796.     s_nFirstTargetConv = -1;
  1797.     BYTE *pAttr;
  1798.     const WCHAR *pcComp;
  1799.     for( pcComp = s_CompString.GetBuffer(), pAttr = s_abCompStringAttr;
  1800.           *pcComp != L''; ++pcComp, ++pAttr )
  1801.     {
  1802.         D3DCOLOR bkColor;
  1803.         // Render a different background for this character
  1804.         int nXLeft, nXRight;
  1805.         s_CompString.CPtoX( int(pcComp - s_CompString.GetBuffer()), FALSE, &nXLeft );
  1806.         s_CompString.CPtoX( int(pcComp - s_CompString.GetBuffer()), TRUE, &nXRight );
  1807.         // Check if this character is off the right edge and should
  1808.         // be wrapped to the next line.
  1809.         if( nXRight - nXFirst > m_rcText.right - rc.left )
  1810.         {
  1811.             // Advance rectangle coordinates to beginning of next line
  1812.             OffsetRect( &rc, m_rcText.left - rc.left, rc.bottom - rc.top );
  1813.             // Update the line's first character information
  1814.             nCharFirst = int(pcComp - s_CompString.GetBuffer());
  1815.             s_CompString.CPtoX( nCharFirst, FALSE, &nXFirst );
  1816.         }
  1817.         // If the caret is on this character, save the coordinates
  1818.         // for drawing the caret later.
  1819.         if( s_nCompCaret == int(pcComp - s_CompString.GetBuffer()) )
  1820.         {
  1821.             rcCaret = rc;
  1822.             rcCaret.left += nXLeft - nXFirst - 1;
  1823.             rcCaret.right = rcCaret.left + 2;
  1824.         }
  1825.         // Set up color based on the character attribute
  1826.         if( *pAttr == ATTR_TARGET_CONVERTED )
  1827.         {
  1828.             pElement->FontColor.Current = m_CompTargetColor;
  1829.             bkColor = m_CompTargetBkColor;
  1830.         }
  1831.         else
  1832.         if( *pAttr == ATTR_TARGET_NOTCONVERTED )
  1833.         {
  1834.             pElement->FontColor.Current = m_CompTargetNonColor;
  1835.             bkColor = m_CompTargetNonBkColor;
  1836.         }
  1837.         else
  1838.         {
  1839.             continue;
  1840.         }
  1841.         RECT rcTarget = { rc.left + nXLeft - nXFirst, rc.top, rc.left + nXRight - nXFirst, rc.bottom };
  1842.         m_pDialog->DrawRect( &rcTarget, bkColor );
  1843.         m_pDialog->DrawText( pcComp, pElement, &rcTarget, false, 1 );
  1844.         // Record the first target converted character's index
  1845.         if( -1 == s_nFirstTargetConv )
  1846.             s_nFirstTargetConv = int(pAttr - s_abCompStringAttr);
  1847.     }
  1848.     // Render the composition caret
  1849.     if( m_bCaretOn )
  1850.     {
  1851.         // If the caret is at the very end, its position would not have
  1852.         // been computed in the above loop.  We compute it here.
  1853.         if( s_nCompCaret == s_CompString.GetTextSize() )
  1854.         {
  1855.             s_CompString.CPtoX( s_nCompCaret, FALSE, &nX );
  1856.             rcCaret = rc;
  1857.             rcCaret.left += nX - nXFirst - 1;
  1858.             rcCaret.right = rcCaret.left + 2;
  1859.         }
  1860.         m_pDialog->DrawRect( &rcCaret, m_CompCaretColor );
  1861.     }
  1862. }
  1863. //--------------------------------------------------------------------------------------
  1864. void CDXUTIMEEditBox::RenderIndicator( IDirect3DDevice9* pd3dDevice, float fElapsedTime )
  1865. {
  1866.     CDXUTElement *pElement = m_Elements.GetAt( 9 );
  1867.     pElement->TextureColor.Blend( DXUT_STATE_NORMAL, fElapsedTime );
  1868.     m_pDialog->DrawSprite( pElement, &m_rcIndicator );
  1869.     RECT rc = m_rcIndicator;
  1870.     InflateRect( &rc, -m_nSpacing, -m_nSpacing );
  1871.     pElement->FontColor.Current = s_ImeState == IMEUI_STATE_ON && s_bEnableImeSystem ? m_IndicatorImeColor : m_IndicatorEngColor;
  1872.     RECT rcCalc = { 0, 0, 0, 0 };
  1873.     // If IME system is off, draw English indicator.
  1874.     WCHAR *pwszIndicator = s_bEnableImeSystem ? s_wszCurrIndicator : s_aszIndicator[0];
  1875.     m_pDialog->CalcTextRect( pwszIndicator, pElement, &rcCalc );
  1876.     m_pDialog->DrawText( pwszIndicator, pElement, &rc );
  1877. }
  1878. //--------------------------------------------------------------------------------------
  1879. void CDXUTIMEEditBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime )
  1880. {
  1881.     if( m_bVisible == false )
  1882.         return;
  1883.     // If we have not computed the indicator symbol width,
  1884.     // do it.
  1885.     if( !m_nIndicatorWidth )
  1886.     {
  1887.         for( int i = 0; i < 5; ++i )
  1888.         {
  1889.             RECT rc = { 0, 0, 0, 0 };
  1890.             m_pDialog->CalcTextRect( s_aszIndicator[i], m_Elements.GetAt( 9 ), &rc );
  1891.             m_nIndicatorWidth = __max( m_nIndicatorWidth, rc.right - rc.left );
  1892.         }
  1893.         // Update the rectangles now that we have the indicator's width
  1894.         UpdateRects();
  1895.     }
  1896.     // Let the parent render first (edit control)
  1897.     CDXUTEditBox::Render( pd3dDevice, fElapsedTime );
  1898.     CDXUTElement* pElement = GetElement( 1 );
  1899.     if( pElement )
  1900.     {
  1901.         s_CompString.SetFontNode( m_pDialog->GetFont( pElement->iFont ) );
  1902.         s_CandList.HoriCand.SetFontNode( m_pDialog->GetFont( pElement->iFont ) );
  1903.     }
  1904.     //
  1905.     // Now render the IME elements
  1906.     //
  1907.     if( m_bHasFocus )
  1908.     {
  1909.         // Render the input locale indicator
  1910.         RenderIndicator( pd3dDevice, fElapsedTime );
  1911.         // Display the composition string.
  1912.         // This method should also update s_ptCompString
  1913.         // for RenderCandidateReadingWindow.
  1914.         RenderComposition( pd3dDevice, fElapsedTime );
  1915.         // Display the reading/candidate window. RenderCandidateReadingWindow()
  1916.         // uses s_ptCompString to position itself.  s_ptCompString must have
  1917.         // been filled in by RenderComposition().
  1918.         if( s_bShowReadingWindow )
  1919.             // Reading window
  1920.             RenderCandidateReadingWindow( pd3dDevice, fElapsedTime, true );
  1921.         else
  1922.         if( s_CandList.bShowWindow )
  1923.             // Candidate list window
  1924.             RenderCandidateReadingWindow( pd3dDevice, fElapsedTime, false );
  1925.     }
  1926. }
  1927. //--------------------------------------------------------------------------------------
  1928. void CUniBuffer::Initialize()
  1929. {
  1930.     if( s_hDll ) // Only need to do once
  1931.         return;
  1932.     WCHAR wszPath[MAX_PATH+1];
  1933.     if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) )
  1934.         return;
  1935.     // Verify whether it is safe to concatenate these strings
  1936.     int len1 = lstrlen(wszPath);
  1937.     int len2 = lstrlen(UNISCRIBE_DLLNAME);
  1938.     if (len1 + len2 > MAX_PATH)
  1939.     {
  1940.         return;
  1941.     }
  1942.     // We have verified that the concatenated string will fit into wszPath,
  1943.     // so it is safe to concatenate them.
  1944.     StringCchCat( wszPath, MAX_PATH, UNISCRIBE_DLLNAME );
  1945.     s_hDll = LoadLibrary( wszPath );
  1946.     if( s_hDll )
  1947.     {
  1948.         FARPROC Temp;
  1949.         GETPROCADDRESS( s_hDll, ScriptApplyDigitSubstitution, Temp );
  1950.         GETPROCADDRESS( s_hDll, ScriptStringAnalyse, Temp );
  1951.         GETPROCADDRESS( s_hDll, ScriptStringCPtoX, Temp );
  1952.         GETPROCADDRESS( s_hDll, ScriptStringXtoCP, Temp );
  1953.         GETPROCADDRESS( s_hDll, ScriptStringFree, Temp );
  1954.         GETPROCADDRESS( s_hDll, ScriptString_pLogAttr, Temp );
  1955.         GETPROCADDRESS( s_hDll, ScriptString_pcOutChars, Temp );
  1956.     }
  1957. }
  1958. //--------------------------------------------------------------------------------------
  1959. void CUniBuffer::Uninitialize()
  1960. {
  1961.     if( s_hDll )
  1962.     {
  1963.         PLACEHOLDERPROC( ScriptApplyDigitSubstitution );
  1964.         PLACEHOLDERPROC( ScriptStringAnalyse );
  1965.         PLACEHOLDERPROC( ScriptStringCPtoX );
  1966.         PLACEHOLDERPROC( ScriptStringXtoCP );
  1967.         PLACEHOLDERPROC( ScriptStringFree );
  1968.         PLACEHOLDERPROC( ScriptString_pLogAttr );
  1969.         PLACEHOLDERPROC( ScriptString_pcOutChars );
  1970.         FreeLibrary( s_hDll );
  1971.         s_hDll = NULL;
  1972.     }
  1973. }
  1974. //--------------------------------------------------------------------------------------
  1975. bool CUniBuffer::SetBufferSize( int nNewSize )
  1976. {
  1977.     // If the current size is already the maximum allowed,
  1978.     // we can't possibly allocate more.
  1979.     if( m_nBufferSize == DXUT_MAX_EDITBOXLENGTH )
  1980.         return false;
  1981.     int nAllocateSize = ( nNewSize == -1 || nNewSize < m_nBufferSize * 2 ) ? ( m_nBufferSize ? m_nBufferSize * 2 : 256 ) : nNewSize * 2;
  1982.     // Cap the buffer size at the maximum allowed.
  1983.     if( nAllocateSize > DXUT_MAX_EDITBOXLENGTH )
  1984.         nAllocateSize = DXUT_MAX_EDITBOXLENGTH;
  1985.     WCHAR *pTempBuffer = new WCHAR[nAllocateSize];
  1986.     if( !pTempBuffer )
  1987.         return false;
  1988.     if( m_pwszBuffer )
  1989.     {
  1990.         CopyMemory( pTempBuffer, m_pwszBuffer, m_nBufferSize * sizeof(WCHAR) );
  1991.         delete[] m_pwszBuffer;
  1992.     }
  1993.     else
  1994.     {
  1995.         ZeroMemory( pTempBuffer, sizeof(WCHAR) * nAllocateSize );
  1996.     }
  1997.     m_pwszBuffer = pTempBuffer;
  1998.     m_nBufferSize = nAllocateSize;
  1999.     return true;
  2000. }
  2001. //--------------------------------------------------------------------------------------
  2002. // Uniscribe -- Analyse() analyses the string in the buffer
  2003. //--------------------------------------------------------------------------------------
  2004. HRESULT CUniBuffer::Analyse()
  2005. {
  2006.     if( m_Analysis )
  2007.         _ScriptStringFree( &m_Analysis );
  2008.     SCRIPT_CONTROL ScriptControl; // For uniscribe
  2009.     SCRIPT_STATE   ScriptState;   // For uniscribe
  2010.     ZeroMemory( &ScriptControl, sizeof(ScriptControl) );
  2011.     ZeroMemory( &ScriptState, sizeof(ScriptState) );
  2012.     _ScriptApplyDigitSubstitution ( NULL, &ScriptControl, &ScriptState );
  2013.     if( !m_pFontNode )
  2014.         return E_FAIL;
  2015.     HRESULT hr = _ScriptStringAnalyse( m_pFontNode->pFont ? m_pFontNode->pFont->GetDC() : NULL,
  2016.                                        m_pwszBuffer,
  2017.                                        lstrlenW( m_pwszBuffer ) + 1,  // NULL is also analyzed.
  2018.                                        lstrlenW( m_pwszBuffer ) * 3 / 2 + 16,
  2019.                                        -1,
  2020.                                        SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK,
  2021.                                        0,
  2022.                                        &ScriptControl,
  2023.                                        &ScriptState,
  2024.                                        NULL,
  2025.                                        NULL,
  2026.                                        NULL,
  2027.                                        &m_Analysis );
  2028.     if( SUCCEEDED( hr ) )
  2029.         m_bAnalyseRequired = false;  // Analysis is up-to-date
  2030.     return hr;
  2031. }
  2032. //--------------------------------------------------------------------------------------
  2033. CUniBuffer::CUniBuffer( int nInitialSize )
  2034. {
  2035.     CUniBuffer::Initialize();  // ensure static vars are properly init'ed first
  2036.     m_nBufferSize = 0;
  2037.     m_pwszBuffer = NULL;
  2038.     m_bAnalyseRequired = true;
  2039.     m_Analysis = NULL;
  2040.     m_pFontNode = NULL;
  2041.     
  2042.     if( nInitialSize > 0 )
  2043.         SetBufferSize( nInitialSize );
  2044. }
  2045. //--------------------------------------------------------------------------------------
  2046. CUniBuffer::~CUniBuffer()
  2047. {
  2048.     delete[] m_pwszBuffer;
  2049.     if( m_Analysis )
  2050.         _ScriptStringFree( &m_Analysis );
  2051. }
  2052. //--------------------------------------------------------------------------------------
  2053. WCHAR& CUniBuffer::operator[]( int n )  // No param checking
  2054. {
  2055.     // This version of operator[] is called only
  2056.     // if we are asking for write access, so
  2057.     // re-analysis is required.
  2058.     m_bAnalyseRequired = true;
  2059.     return m_pwszBuffer[n];
  2060. }
  2061. //--------------------------------------------------------------------------------------
  2062. void CUniBuffer::Clear()
  2063. {
  2064.     *m_pwszBuffer = L'';
  2065.     m_bAnalyseRequired = true;
  2066. }
  2067. //--------------------------------------------------------------------------------------
  2068. // Inserts the char at specified index.
  2069. // If nIndex == -1, insert to the end.
  2070. //--------------------------------------------------------------------------------------
  2071. bool CUniBuffer::InsertChar( int nIndex, WCHAR wChar )
  2072. {
  2073.     assert( nIndex >= 0 );
  2074.     if( nIndex < 0 || nIndex > lstrlenW( m_pwszBuffer ) )
  2075.         return false;  // invalid index
  2076.     // Check for maximum length allowed
  2077.     if( GetTextSize() + 1 >= DXUT_MAX_EDITBOXLENGTH )
  2078.         return false;
  2079.     if( lstrlenW( m_pwszBuffer ) + 1 >= m_nBufferSize )
  2080.     {
  2081.         if( !SetBufferSize( -1 ) )
  2082.             return false;  // out of memory
  2083.     }
  2084.     assert( m_nBufferSize >= 2 );
  2085.     // Shift the characters after the index, start by copying the null terminator
  2086.     WCHAR* dest = m_pwszBuffer + lstrlenW(m_pwszBuffer)+1;
  2087.     WCHAR* stop = m_pwszBuffer + nIndex;
  2088.     WCHAR* src = dest - 1;
  2089.     while( dest > stop )
  2090.     {
  2091.         *dest-- = *src--;
  2092.     }
  2093.     // Set new character
  2094.     m_pwszBuffer[ nIndex ] = wChar;
  2095.     m_bAnalyseRequired = true;
  2096.     return true;
  2097. }
  2098. //--------------------------------------------------------------------------------------
  2099. // Removes the char at specified index.
  2100. // If nIndex == -1, remove the last char.
  2101. //--------------------------------------------------------------------------------------
  2102. bool CUniBuffer::RemoveChar( int nIndex )
  2103. {
  2104.     if( !lstrlenW( m_pwszBuffer ) || nIndex < 0 || nIndex >= lstrlenW( m_pwszBuffer ) )
  2105.         return false;  // Invalid index
  2106.     MoveMemory( m_pwszBuffer + nIndex, m_pwszBuffer + nIndex + 1, sizeof(WCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex ) );
  2107.     m_bAnalyseRequired = true;
  2108.     return true;
  2109. }
  2110. //--------------------------------------------------------------------------------------
  2111. // Inserts the first nCount characters of the string pStr at specified index.
  2112. // If nCount == -1, the entire string is inserted.
  2113. // If nIndex == -1, insert to the end.
  2114. //--------------------------------------------------------------------------------------
  2115. bool CUniBuffer::InsertString( int nIndex, const WCHAR *pStr, int nCount )
  2116. {
  2117.     assert( nIndex >= 0 );
  2118.     if( nIndex > lstrlenW( m_pwszBuffer ) )
  2119.         return false;  // invalid index
  2120.     if( -1 == nCount )
  2121.         nCount = lstrlenW( pStr );
  2122.     // Check for maximum length allowed
  2123.     if( GetTextSize() + nCount >= DXUT_MAX_EDITBOXLENGTH )
  2124.         return false;
  2125.     if( lstrlenW( m_pwszBuffer ) + nCount >= m_nBufferSize )
  2126.     {
  2127.         if( !SetBufferSize( lstrlenW( m_pwszBuffer ) + nCount + 1 ) )
  2128.             return false;  // out of memory
  2129.     }
  2130.     MoveMemory( m_pwszBuffer + nIndex + nCount, m_pwszBuffer + nIndex, sizeof(WCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex + 1 ) );
  2131.     CopyMemory( m_pwszBuffer + nIndex, pStr, nCount * sizeof(WCHAR) );
  2132.     m_bAnalyseRequired = true;
  2133.     return true;
  2134. }
  2135. //--------------------------------------------------------------------------------------
  2136. bool CUniBuffer::SetText( LPCWSTR wszText )
  2137. {
  2138.     assert( wszText != NULL );
  2139.     int nRequired = int(wcslen( wszText ) + 1);
  2140.     // Check for maximum length allowed
  2141.     if( nRequired >= DXUT_MAX_EDITBOXLENGTH )
  2142.         return false;
  2143.     while( GetBufferSize() < nRequired )
  2144.         if( !SetBufferSize( -1 ) )
  2145.             break;
  2146.     // Check again in case out of memory occurred inside while loop.
  2147.     if( GetBufferSize() >= nRequired )
  2148.     {
  2149.         StringCchCopy( m_pwszBuffer, GetBufferSize(), wszText );
  2150.         m_bAnalyseRequired = true;
  2151.         return true;
  2152.     }
  2153.     else
  2154.         return false;
  2155. }
  2156. //--------------------------------------------------------------------------------------
  2157. HRESULT CUniBuffer::CPtoX( int nCP, BOOL bTrail, int *pX )
  2158. {
  2159.     assert( pX );
  2160.     *pX = 0;  // Default
  2161.     HRESULT hr = S_OK;
  2162.     if( m_bAnalyseRequired )
  2163.         hr = Analyse();
  2164.     if( SUCCEEDED( hr ) )
  2165.         hr = _ScriptStringCPtoX( m_Analysis, nCP, bTrail, pX );
  2166.     return hr;
  2167. }
  2168. //--------------------------------------------------------------------------------------
  2169. HRESULT CUniBuffer::XtoCP( int nX, int *pCP, int *pnTrail )
  2170. {
  2171.     assert( pCP && pnTrail );
  2172.     *pCP = 0; *pnTrail = FALSE;  // Default
  2173.     HRESULT hr = S_OK;
  2174.     if( m_bAnalyseRequired )
  2175.         hr = Analyse();
  2176.     if( SUCCEEDED( hr ) )
  2177.         hr = _ScriptStringXtoCP( m_Analysis, nX, pCP, pnTrail );
  2178.     // If the coordinate falls outside the text region, we
  2179.     // can get character positions that don't exist.  We must
  2180.     // filter them here and convert them to those that do exist.
  2181.     if( *pCP == -1 && *pnTrail == TRUE )
  2182.     {
  2183.         *pCP = 0; *pnTrail = FALSE;
  2184.     } else
  2185.     if( *pCP > lstrlenW( m_pwszBuffer ) && *pnTrail == FALSE )
  2186.     {
  2187.         *pCP = lstrlenW( m_pwszBuffer ); *pnTrail = TRUE;
  2188.     }
  2189.     return hr;
  2190. }
  2191. //--------------------------------------------------------------------------------------
  2192. void CUniBuffer::GetPriorItemPos( int nCP, int *pPrior )
  2193. {
  2194.     *pPrior = nCP;  // Default is the char itself
  2195.     if( m_bAnalyseRequired )
  2196.         if( FAILED( Analyse() ) )
  2197.             return;
  2198.     const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis );
  2199.     if( !pLogAttr )
  2200.         return;
  2201.     if( !_ScriptString_pcOutChars( m_Analysis ) )
  2202.         return;
  2203.     int nInitial = *_ScriptString_pcOutChars( m_Analysis );
  2204.     if( nCP - 1 < nInitial )
  2205.         nInitial = nCP - 1;
  2206.     for( int i = nInitial; i > 0; --i )
  2207.         if( pLogAttr[i].fWordStop ||       // Either the fWordStop flag is set
  2208.             ( !pLogAttr[i].fWhiteSpace &&  // Or the previous char is whitespace but this isn't.
  2209.                 pLogAttr[i-1].fWhiteSpace ) )
  2210.         {
  2211.             *pPrior = i;
  2212.             return;
  2213.         }
  2214.     // We have reached index 0.  0 is always a break point, so simply return it.
  2215.     *pPrior = 0;
  2216. }
  2217.     
  2218. //--------------------------------------------------------------------------------------
  2219. void CUniBuffer::GetNextItemPos( int nCP, int *pPrior )
  2220. {
  2221.     *pPrior = nCP;  // Default is the char itself
  2222.     HRESULT hr = S_OK;
  2223.     if( m_bAnalyseRequired )
  2224.         hr = Analyse();
  2225.     if( FAILED( hr ) )
  2226.         return;
  2227.     const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis );
  2228.     if( !pLogAttr )
  2229.         return;
  2230.     if( !_ScriptString_pcOutChars( m_Analysis ) )
  2231.         return;
  2232.     int nInitial = *_ScriptString_pcOutChars( m_Analysis );
  2233.     if( nCP + 1 < nInitial )
  2234.         nInitial = nCP + 1;
  2235.     for( int i = nInitial; i < *_ScriptString_pcOutChars( m_Analysis ) - 1; ++i )
  2236.     {
  2237.         if( pLogAttr[i].fWordStop )      // Either the fWordStop flag is set
  2238.         {
  2239.             *pPrior = i;
  2240.             return;
  2241.         }
  2242.         else
  2243.         if( pLogAttr[i].fWhiteSpace &&  // Or this whitespace but the next char isn't.
  2244.             !pLogAttr[i+1].fWhiteSpace )
  2245.         {
  2246.             *pPrior = i+1;  // The next char is a word stop
  2247.             return;
  2248.         }
  2249.     }
  2250.     // We have reached the end. It's always a word stop, so simply return it.
  2251.     *pPrior = *_ScriptString_pcOutChars( m_Analysis ) - 1;
  2252. }
  2253. //--------------------------------------------------------------------------------------
  2254. void CDXUTEditBox::ResetCaretBlink()
  2255. {
  2256.     m_bCaretOn = true;
  2257.     m_dfLastBlink = DXUTGetGlobalTimer()->GetAbsoluteTime();
  2258. }
  2259. //--------------------------------------------------------------------------------------
  2260. void CDXUTIMEEditBox::Initialize()
  2261. {
  2262.     if( s_hDllImm32 ) // Only need to do once
  2263.         return;
  2264.     FARPROC Temp;
  2265.     s_CompString.SetBufferSize( MAX_COMPSTRING_SIZE );
  2266.     WCHAR wszPath[MAX_PATH+1];
  2267.     if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) )
  2268.         return;
  2269.     StringCchCat( wszPath, MAX_PATH, IMM32_DLLNAME );
  2270.     s_hDllImm32 = LoadLibrary( wszPath );
  2271.     if( s_hDllImm32 )
  2272.     {
  2273.         GETPROCADDRESS( s_hDllImm32, ImmLockIMC, Temp );
  2274.         GETPROCADDRESS( s_hDllImm32, ImmUnlockIMC, Temp );
  2275.         GETPROCADDRESS( s_hDllImm32, ImmLockIMCC, Temp );
  2276.         GETPROCADDRESS( s_hDllImm32, ImmUnlockIMCC, Temp );
  2277.         GETPROCADDRESS( s_hDllImm32, ImmDisableTextFrameService, Temp );
  2278.         GETPROCADDRESS( s_hDllImm32, ImmGetCompositionStringW, Temp );
  2279.         GETPROCADDRESS( s_hDllImm32, ImmGetCandidateListW, Temp );
  2280.         GETPROCADDRESS( s_hDllImm32, ImmGetContext, Temp );
  2281.         GETPROCADDRESS( s_hDllImm32, ImmReleaseContext, Temp );
  2282.         GETPROCADDRESS( s_hDllImm32, ImmAssociateContext, Temp );
  2283.         GETPROCADDRESS( s_hDllImm32, ImmGetOpenStatus, Temp );
  2284.         GETPROCADDRESS( s_hDllImm32, ImmSetOpenStatus, Temp );
  2285.         GETPROCADDRESS( s_hDllImm32, ImmGetConversionStatus, Temp );
  2286.         GETPROCADDRESS( s_hDllImm32, ImmGetDefaultIMEWnd, Temp );
  2287.         GETPROCADDRESS( s_hDllImm32, ImmGetIMEFileNameA, Temp );
  2288.         GETPROCADDRESS( s_hDllImm32, ImmGetVirtualKey, Temp );
  2289.         GETPROCADDRESS( s_hDllImm32, ImmNotifyIME, Temp );
  2290.         GETPROCADDRESS( s_hDllImm32, ImmSetConversionStatus, Temp );
  2291.         GETPROCADDRESS( s_hDllImm32, ImmSimulateHotKey, Temp );
  2292.         GETPROCADDRESS( s_hDllImm32, ImmIsIME, Temp );
  2293.     }
  2294.     if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) )
  2295.         return;
  2296.     StringCchCat( wszPath, MAX_PATH, VER_DLLNAME );
  2297.     s_hDllVer = LoadLibrary( wszPath );
  2298.     if( s_hDllVer )
  2299.     {
  2300.         GETPROCADDRESS( s_hDllVer, VerQueryValueA, Temp );
  2301.         GETPROCADDRESS( s_hDllVer, GetFileVersionInfoA, Temp );
  2302.         GETPROCADDRESS( s_hDllVer, GetFileVersionInfoSizeA, Temp );
  2303.     }
  2304. }
  2305. //--------------------------------------------------------------------------------------
  2306. void CDXUTIMEEditBox::Uninitialize()
  2307. {
  2308.     if( s_hDllImm32 )
  2309.     {
  2310.         PLACEHOLDERPROC( ImmLockIMC );
  2311.         PLACEHOLDERPROC( ImmUnlockIMC );
  2312.         PLACEHOLDERPROC( ImmLockIMCC );
  2313.         PLACEHOLDERPROC( ImmUnlockIMCC );
  2314.         PLACEHOLDERPROC( ImmDisableTextFrameService );
  2315.         PLACEHOLDERPROC( ImmGetCompositionStringW );
  2316.         PLACEHOLDERPROC( ImmGetCandidateListW );
  2317.         PLACEHOLDERPROC( ImmGetContext );
  2318.         PLACEHOLDERPROC( ImmReleaseContext );
  2319.         PLACEHOLDERPROC( ImmAssociateContext );
  2320.         PLACEHOLDERPROC( ImmGetOpenStatus );
  2321.         PLACEHOLDERPROC( ImmSetOpenStatus );
  2322.         PLACEHOLDERPROC( ImmGetConversionStatus );
  2323.         PLACEHOLDERPROC( ImmGetDefaultIMEWnd );
  2324.         PLACEHOLDERPROC( ImmGetIMEFileNameA );
  2325.         PLACEHOLDERPROC( ImmGetVirtualKey );
  2326.         PLACEHOLDERPROC( ImmNotifyIME );
  2327.         PLACEHOLDERPROC( ImmSetConversionStatus );
  2328.         PLACEHOLDERPROC( ImmSimulateHotKey );
  2329.         PLACEHOLDERPROC( ImmIsIME );
  2330.         FreeLibrary( s_hDllImm32 );
  2331.         s_hDllImm32 = NULL;
  2332.     }
  2333.     if( s_hDllIme )
  2334.     {
  2335.         PLACEHOLDERPROC( GetReadingString );
  2336.         PLACEHOLDERPROC( ShowReadingWindow );
  2337.         FreeLibrary( s_hDllIme );
  2338.         s_hDllIme = NULL;
  2339.     }
  2340.     if( s_hDllVer )
  2341.     {
  2342.         PLACEHOLDERPROC( VerQueryValueA );
  2343.         PLACEHOLDERPROC( GetFileVersionInfoA );
  2344.         PLACEHOLDERPROC( GetFileVersionInfoSizeA );
  2345.         FreeLibrary( s_hDllVer );
  2346.         s_hDllVer = NULL;
  2347.     }
  2348. }
  2349. //--------------------------------------------------------------------------------------
  2350. void DXUTBlendColor::Init( D3DCOLOR defaultColor, D3DCOLOR disabledColor, D3DCOLOR hiddenColor )
  2351. {
  2352.     for( int i=0; i < MAX_CONTROL_STATES; i++ )
  2353.     {
  2354.         States[ i ] = defaultColor;
  2355.     }
  2356.     States[ DXUT_STATE_DISABLED ] = disabledColor;
  2357.     States[ DXUT_STATE_HIDDEN ] = hiddenColor;
  2358.     Current = hiddenColor;
  2359. }
  2360. //--------------------------------------------------------------------------------------
  2361. void DXUTBlendColor::Blend( UINT iState, float fElapsedTime, float fRate )
  2362. {
  2363.     D3DXCOLOR destColor = States[ iState ];
  2364.     D3DXColorLerp( &Current, &Current, &destColor, 1.0f - powf( fRate, 30 * fElapsedTime ) );
  2365. }
  2366. //--------------------------------------------------------------------------------------
  2367. void CDXUTElement::SetTexture( UINT iTexture, RECT* prcTexture, D3DCOLOR defaultTextureColor )
  2368. {
  2369.     this->iTexture = iTexture;
  2370.     
  2371.     if( prcTexture )
  2372.         rcTexture = *prcTexture;
  2373.     else
  2374.         SetRectEmpty( &rcTexture );
  2375.     
  2376.     TextureColor.Init( defaultTextureColor );
  2377. }
  2378.     
  2379. //--------------------------------------------------------------------------------------
  2380. void CDXUTElement::SetFont( UINT iFont, D3DCOLOR defaultFontColor, DWORD dwTextFormat )
  2381. {
  2382.     this->iFont = iFont;
  2383.     this->dwTextFormat = dwTextFormat;
  2384.     FontColor.Init( defaultFontColor );
  2385. }
  2386. //--------------------------------------------------------------------------------------
  2387. void CDXUTElement::Refresh()
  2388. {
  2389.     TextureColor.Current = TextureColor.States[ DXUT_STATE_HIDDEN ];
  2390.     FontColor.Current = FontColor.States[ DXUT_STATE_HIDDEN ];
  2391. }